#include "argparse.hpp" #include "argparse_util.hpp" using argparse::ArgValue; using argparse::ConvertedValue; #define TEST struct Args { ArgValue architecture_file; ArgValue circuit; ArgValue disp; ArgValue auto_value; ArgValue pack; ArgValue place; ArgValue route; ArgValue show_help; ArgValue timing_analysis; ArgValue slack_definition; ArgValue echo_files; ArgValue verify_file_digests; ArgValue num_workers; ArgValue blif_file; ArgValue net_file; ArgValue place_file; ArgValue route_file; ArgValue sdc_file; ArgValue outfile_prefix; ArgValue absorb_buffer_luts; ArgValue sweep_dangling_primary_ios; ArgValue sweep_dangling_nets; ArgValue sweep_dangling_blocks; ArgValue sweep_constant_primary_outputs; ArgValue connection_driven_clustering; ArgValue allow_unrelated_clustering; ArgValue alpha_clustering; ArgValue beta_clustering; ArgValue timing_driven_clustering; ArgValue cluster_seed_type; ArgValue seed; ArgValue enable_timing_computations; ArgValue inner_num; ArgValue init_t; ArgValue exit_t; ArgValue alpha_t; ArgValue fix_pins; ArgValue place_algorithm; ArgValue place_chan_width; ArgValue timing_tradeoff; ArgValue recompute_crit_iter; ArgValue inner_loop_recompute_divider; ArgValue td_place_exp_first; ArgValue td_place_exp_last; ArgValue max_router_iterations; ArgValue first_iter_pres_fac; ArgValue initial_pres_fac; ArgValue pres_fac_mult; ArgValue acc_fac; ArgValue bb_factor; ArgValue base_cost_type; ArgValue bend_cost; ArgValue route_type; ArgValue route_chan_width; ArgValue min_route_chan_width_hint; ArgValue verify_binary_search; ArgValue router_algorithm; ArgValue min_incremental_reroute_fanout; ArgValue astar_fac; ArgValue max_criticality; ArgValue criticality_exp; ArgValue routing_failure_predictor; ArgValue power; ArgValue tech_properties_file; ArgValue activity_file; ArgValue full_stats; ArgValue gen_post_synthesis_netlist; ArgValue> one_or_more; ArgValue> zero_or_more; }; bool expect_pass(argparse::ArgumentParser& parser, std::vector cmd_line); bool expect_fail(argparse::ArgumentParser& parser, std::vector cmd_line); struct OnOff { ConvertedValue from_str(std::string str) { ConvertedValue converted_value; if (str == "on") converted_value.set_value(true); else if (str == "off") converted_value.set_value(false); else converted_value.set_error("Invalid argument value"); return converted_value; } ConvertedValue to_str(bool val) { ConvertedValue converted_value; if (val) converted_value.set_value("on"); else converted_value.set_value("off"); return converted_value; } std::vector default_choices() { return {"on", "off"}; } }; int main( int #ifndef TEST argc #endif , const char** argv) { Args args; auto parser = argparse::ArgumentParser(argv[0], "Test parser for libargparse"); parser.epilog("This is the epilog"); auto& pos_grp = parser.add_argument_group("positional arguments"); pos_grp.add_argument(args.architecture_file, "architecture") .help("FPGA Architecture description file (XML)"); pos_grp.add_argument(args.circuit, "circuit") .help("Circuit file (or circuit name if --blif_file specified)"); auto& stage_grp = parser.add_argument_group("stage options"); stage_grp.add_argument(args.pack, "--pack") .help("Run packing") .action(argparse::Action::STORE_TRUE) .default_value("off"); stage_grp.add_argument(args.place, "--place") .help("Run placement") .action(argparse::Action::STORE_TRUE) .default_value("off"); stage_grp.add_argument(args.route, "--route") .help("Run routing") .action(argparse::Action::STORE_TRUE) .default_value("off"); stage_grp.add_argument(args.route, "--analysis") .help("Run analysis") .action(argparse::Action::STORE_TRUE) .required(true) .default_value("off"); stage_grp.epilog("If none of the stage options are specified, all stages are run.\n" "Analysis is always run after routing."); auto& gfx_grp = parser.add_argument_group("graphics options"); gfx_grp.add_argument(args.disp, "--disp") .help("Enable or disable interactive graphics") .default_value("off"); gfx_grp.add_argument(args.auto_value, "--auto") .help("Controls how often VPR pauses for interactive" " graphics (requiring Proceed to be clicked)." " Higher values pause less frequently") .default_value("1") .choices({"0", "1", "2"}) .show_in(argparse::ShowIn::HELP_ONLY); auto& gen_grp = parser.add_argument_group("general options"); gen_grp.add_argument(args.show_help, "--help", "-h") .help("Show this help message then exit") .action(argparse::Action::HELP); gen_grp.add_argument(args.timing_analysis, "--timing_analysis") .help("Controls whether timing analysis (and timing driven optimizations) are enabled.") .default_value("on") ; gen_grp.add_argument(args.slack_definition, "--slack_definition") .help("Sets the slack definition used by the classic timing analyyzer") .default_value("R") .choices({"R", "I", "S", "G", "C", "N"}) .show_in(argparse::ShowIn::HELP_ONLY); gen_grp.add_argument(args.echo_files, "--echo_file") .help("Generate echo files of key internal data structures." " Useful for debugging VPR, and typically end in .echo") .default_value("off") .show_in(argparse::ShowIn::HELP_ONLY); gen_grp.add_argument(args.verify_file_digests, "--verify_file_digests") .help("Verify that files loaded by VPR (e.g. architecture, netlist," " previous packing/placement/routing) are consistent") .default_value("on") .show_in(argparse::ShowIn::HELP_ONLY); gen_grp.add_argument(args.num_workers, "--num_workers", "-j") .help("Number of parallel workers") .default_value("1") .show_in(argparse::ShowIn::HELP_ONLY); auto& file_grp = parser.add_argument_group("filename options"); file_grp.add_argument(args.blif_file, "--blif_file") .help("Path to technology mapped circuit in BLIF format") .show_in(argparse::ShowIn::HELP_ONLY); file_grp.add_argument(args.net_file, "--net_file") .help("Path to packed netlist file") .show_in(argparse::ShowIn::HELP_ONLY); file_grp.add_argument(args.place_file, "--place_file") .help("Path to placement file") .show_in(argparse::ShowIn::HELP_ONLY); file_grp.add_argument(args.route_file, "--route_file") .help("Path to routing file") .show_in(argparse::ShowIn::HELP_ONLY); file_grp.add_argument(args.sdc_file, "--sdc_file") .help("Path to timing constraints file in SDC format") .show_in(argparse::ShowIn::HELP_ONLY); file_grp.add_argument(args.outfile_prefix, "--outfile_prefix") .help("Prefix for output files") .show_in(argparse::ShowIn::HELP_ONLY); auto& netlist_grp = parser.add_argument_group("netlist options"); netlist_grp.add_argument(args.absorb_buffer_luts, "--absorb_buffer_luts") .help("Controls whether LUTS programmed as buffers are absorbed by downstream logic") .default_value("on") .show_in(argparse::ShowIn::HELP_ONLY); netlist_grp.add_argument(args.sweep_dangling_primary_ios, "--sweep_dangling_primary_ios") .help("Controls whether dangling primary inputs and outputs are removed from the netlist") .default_value("on") .show_in(argparse::ShowIn::HELP_ONLY); netlist_grp.add_argument(args.sweep_dangling_nets, "--sweep_dangling_nets") .help("Controls whether dangling nets are removed from the netlist") .default_value("on") .show_in(argparse::ShowIn::HELP_ONLY); netlist_grp.add_argument(args.sweep_dangling_blocks, "--sweep_dangling_blocks") .help("Controls whether dangling blocks are removed from the netlist") .default_value("on") .show_in(argparse::ShowIn::HELP_ONLY); netlist_grp.add_argument(args.sweep_constant_primary_outputs, "--sweep_constant_primary_outputs") .help("Controls whether primary outputs driven by constant values are removed from the netlist") .default_value("off") .show_in(argparse::ShowIn::HELP_ONLY); auto& pack_grp = parser.add_argument_group("packing options"); pack_grp.add_argument(args.connection_driven_clustering, "--connection_driven_clustering") .help("Controls whether or not packing prioritizes the absorption of nets with fewer" " connections into a complex logic block over nets with more connections") .default_value("on") .show_in(argparse::ShowIn::HELP_ONLY); pack_grp.add_argument(args.allow_unrelated_clustering, "--allow_unrelated_clustering") .help("Controls whether or not primitives with no attraction to the current cluster" " can be packed into it") .default_value("on") .show_in(argparse::ShowIn::HELP_ONLY); pack_grp.add_argument(args.alpha_clustering, "--alpha_clustering") .help("Parameter that weights the optimization of timing vs area. 0.0 focuses solely on" " area, 1.0 solely on timing.") .default_value("0.75") .show_in(argparse::ShowIn::HELP_ONLY); pack_grp.add_argument(args.beta_clustering, "--beta_clustering") .help("Parameter that weights the absorption of small nets vs signal sharing." " 0.0 focuses solely on sharing, 1.0 solely on small net absoprtion." " Only meaningful if --connection_driven_clustering=on") .default_value("0.9") .show_in(argparse::ShowIn::HELP_ONLY); pack_grp.add_argument(args.timing_driven_clustering, "--timing_driven_clustering") .help("Controls whether custering optimizes for timing") .default_value("on") .show_in(argparse::ShowIn::HELP_ONLY); pack_grp.add_argument(args.cluster_seed_type, "--cluster_seed_type") .help("Controls how primitives are chosen as seeds." " (Default: blend if timing driven, max_inputs otherwise)") .choices({"blend", "timing", "max_inputs"}) .show_in(argparse::ShowIn::HELP_ONLY); auto& place_grp = parser.add_argument_group("placement options"); place_grp.add_argument(args.seed, "--seed") .help("Placement random number generator seed") .default_value("1") .show_in(argparse::ShowIn::HELP_ONLY); place_grp.add_argument(args.enable_timing_computations, "--enable_timing_computations") .help("Displays delay statistics even if placement is not timing driven") .default_value("on") .show_in(argparse::ShowIn::HELP_ONLY); place_grp.add_argument(args.inner_num, "--inner_num") .help("Controls number of moves per temperature: inner_num * num_blocks ^ (4/3)") .default_value("10.0") .show_in(argparse::ShowIn::HELP_ONLY); place_grp.add_argument(args.init_t, "--init_t") .help("Initial temperature for manual annealing schedule") .default_value("100.0") .show_in(argparse::ShowIn::HELP_ONLY); place_grp.add_argument(args.exit_t, "--exit_t") .help("Temperature at which annealing which terminate for manual annealing schedule") .default_value("0.01") .show_in(argparse::ShowIn::HELP_ONLY); place_grp.add_argument(args.alpha_t, "--alpha_t") .help("Temperature scaling factor for manual annealing schedule." " Old temperature is multiplied by alpha_t") .default_value("0.01") .show_in(argparse::ShowIn::HELP_ONLY); place_grp.add_argument(args.fix_pins, "--fix_pins") .help("Fixes I/O pad locations during placement." " Can be 'random' for a random initial assignment," " 'off' to allow the place to optimize pad locations," " or a file specifying the pad locations.") .default_value("off") .show_in(argparse::ShowIn::HELP_ONLY); place_grp.add_argument(args.place_algorithm, "--place_algorithm") .help("Controls which placement algorithm is used") .default_value("path_timing_driven") .choices({"bounding_box", "path_timing_driven"}) .show_in(argparse::ShowIn::HELP_ONLY); place_grp.add_argument(args.place_chan_width, "--place_chan_width") .help("Sets the assumed channel width during placement") .default_value("100") .show_in(argparse::ShowIn::HELP_ONLY); auto& place_timing_grp = parser.add_argument_group("timing-driven placement options"); place_timing_grp.add_argument(args.timing_tradeoff, "--timing_tradeoff") .help("Trade-off control between delay and wirelength during placement." " 0.0 focuses completely on wirelength, 1.0 completely on timing") .default_value("0.5") .show_in(argparse::ShowIn::HELP_ONLY); place_timing_grp.add_argument(args.recompute_crit_iter, "--recompute_crit_iter") .help("Controls how many temperature updates occur between timing analysis during placement") .default_value("1") .show_in(argparse::ShowIn::HELP_ONLY); place_timing_grp.add_argument(args.inner_loop_recompute_divider, "--inner_loop_recompute_divider") .help("Controls how many timing analysies are perform per temperature during placement") .default_value("0") .show_in(argparse::ShowIn::HELP_ONLY); place_timing_grp.add_argument(args.td_place_exp_first, "--td_place_exp_first") .help("Controls how critical a connection is as a function of slack at the start of placement." " A value of zero treats all connections as equally critical (regardless of slack)." " Values larger than 1.0 cause low slack connections to be treated more critically." " The value increases to --td_place_exp_last during placement.") .default_value("1.0") .show_in(argparse::ShowIn::HELP_ONLY); place_timing_grp.add_argument(args.td_place_exp_last, "--td_place_exp_last") .help("Controls how critical a connection is as a function of slack at the end of placement.") .default_value("8.0") .show_in(argparse::ShowIn::HELP_ONLY); auto& route_grp = parser.add_argument_group("routing options"); route_grp.add_argument(args.max_router_iterations, "--max_route_iterations") .help("Maximum number of Pathfinder-based routing iterations before the circuit is" " declared unroutable at a given channel width") .default_value("50") .show_in(argparse::ShowIn::HELP_ONLY); route_grp.add_argument(args.initial_pres_fac, "--first_iter_pres_fac") .help("Sets the present overuse factor for the first routing iteration") .default_value("0.0") .show_in(argparse::ShowIn::HELP_ONLY); route_grp.add_argument(args.initial_pres_fac, "--initial_pres_fac") .help("Sets the present overuse factor for the second routing iteration") .default_value("0.5") .show_in(argparse::ShowIn::HELP_ONLY); route_grp.add_argument(args.pres_fac_mult, "--pres_fac_mult") .help("Sets the growth factor by which the present overuse penalty factor is" " multiplied after each routing iteration") .default_value("1.3") .show_in(argparse::ShowIn::HELP_ONLY); route_grp.add_argument(args.acc_fac, "--acc_fac") .help("Specifies the accumulated overuse factor (historical congestion cost factor)") .default_value("1.0") .show_in(argparse::ShowIn::HELP_ONLY); route_grp.add_argument(args.bb_factor, "--bb_factor") .help("Sets the distance (in channels) outside a connection's bounding box which can be explored") .default_value("3") .show_in(argparse::ShowIn::HELP_ONLY); route_grp.add_argument(args.base_cost_type, "--base_cost_type") .help("Sets the basic cost of routing resource nodes:\n" " * demand_only: based on expected demand of node type\n" " * delay_normalized: like demand_only but normalized to magnitude of typical routing resource delay\n" "(Default: demand_only for bread-first router, delay_normalized for timing-driven router)") .choices({"demand_only", "delay_normalized"}) .show_in(argparse::ShowIn::HELP_ONLY); route_grp.add_argument(args.bend_cost, "--bend_cost") .help("The cost of a bend. (Default: 1.0 for global routing, 0.0 for detailed routing)") .show_in(argparse::ShowIn::HELP_ONLY); route_grp.add_argument(args.route_type, "--route_type") .help("Specifies whether global, or combined global and detailed routing is performed.") .choices({"global", "detailed"}) .show_in(argparse::ShowIn::HELP_ONLY); route_grp.add_argument(args.route_chan_width, "--route_chan_width") .help("Specifies a fixed channel width to route at.") .metavar("CHANNEL_WIDTH"); route_grp.add_argument(args.min_route_chan_width_hint, "--min_route_chan_width_hint") .help("Hint to the router what the minimum routable channel width is." " Good hints can speed-up determining the minimum channel width.") .show_in(argparse::ShowIn::HELP_ONLY); route_grp.add_argument(args.verify_binary_search, "--verify_binary_search") .help("Force the router to verify the minimum channel width by routing at" " consecutively lower channel widths until two consecutive failures are observed.") .default_value("off") .show_in(argparse::ShowIn::HELP_ONLY); route_grp.add_argument(args.router_algorithm, "--router_algorithm") .help("Specifies the router algorithm to use.\n" " * breadth_first: focuses solely on routability\n" " * timing driven: focuses on routability and circuit speed\n") .default_value("timing_driven") .choices({"breadth_first", "timing_driven"}) .show_in(argparse::ShowIn::HELP_ONLY); route_grp.add_argument(args.min_incremental_reroute_fanout, "--min_incremental_reroute_fanout") .help("The net fanout thershold above which nets will be re-routed incrementally.") .default_value("64") .show_in(argparse::ShowIn::HELP_ONLY); auto& route_timing_grp = parser.add_argument_group("timing-driven routing options"); route_timing_grp.add_argument(args.astar_fac, "--astar_fac") .help("How aggressive the directed search used by the timing-driven router is." " Values between 1 and 2 are resonable; higher values trade some quality for reduced run-time") .default_value("1.2") .show_in(argparse::ShowIn::HELP_ONLY); route_timing_grp.add_argument(args.max_criticality, "--max_criticality") .help("Sets the maximum fraction of routing cost derived from delay (vs routability) for any net." " 0.0 means no attention is paid to delay, 1.0 means nets on the critical path ignore congestion") .default_value("0.99") .show_in(argparse::ShowIn::HELP_ONLY); route_timing_grp.add_argument(args.criticality_exp, "--criticality_exp") .help("Controls the delay-routability trade-off for nets as a function of slack." " 0.0 implies all nets treated equally regardless of slack." " At large values (>> 1) only nets on the critical path will consider delay.") .default_value("1.0") .show_in(argparse::ShowIn::HELP_ONLY); route_timing_grp.add_argument(args.routing_failure_predictor, "--routing_failure_predictor") .help("Controls how aggressively the router will predict a routing as unsuccessful" " and give up early. This can significantly reducing the run-time required" " to find the minimum channel width).\n" " * safe: Only abort when it is extremely unlikely a routing will succeed\n" " * aggressive: Further reduce run-time by giving up earlier. This may increase the reported minimum channel width\n" " * off: Only abort when the maximum number of iterations is reached\n") .default_value("safe") .choices({"safe", "aggressive", "off"}) .show_in(argparse::ShowIn::HELP_ONLY); auto& analysis_grp = parser.add_argument_group("analysis options"); analysis_grp.add_argument(args.full_stats, "--full_stats") .help("Print extra statistics about the circuit and it's routing (useful for wireability analysis)") .default_value("off") .show_in(argparse::ShowIn::HELP_ONLY); analysis_grp.add_argument(args.gen_post_synthesis_netlist, "--gen_post_synthesis_netlist") .help("Generates the post-synthesis netlist (in BLIF and Verilog) along with delay information (in SDF)." " Used for post-implementation simulation and verification") .default_value("off") .show_in(argparse::ShowIn::HELP_ONLY); auto& power_grp = parser.add_argument_group("power analysis options"); power_grp.add_argument(args.power, "--power") .help("Enable power estimation") .action(argparse::Action::STORE_TRUE) .default_value("off") .show_in(argparse::ShowIn::HELP_ONLY); power_grp.add_argument(args.tech_properties_file, "--tech_properties_file") .help("XML file containing CMOS technology properties (see documentation).") .show_in(argparse::ShowIn::HELP_ONLY); power_grp.add_argument(args.activity_file, "--activity_file") .help("Signal activities file for all nets (see documentation).") .show_in(argparse::ShowIn::HELP_ONLY); auto& test_grp = parser.add_argument_group("test options"); test_grp.add_argument(args.one_or_more, "--one_or_more") .nargs('+'); test_grp.add_argument(args.zero_or_more, "--zero_or_more") .nargs('*'); #ifndef TEST auto specified_args = parser.parse_args(argc, argv); for(auto& arg : specified_args) { std::cout << "Group: " << arg->group_name() << " Specified argument: " << arg->long_option(); auto short_opt = arg->short_option(); if (!short_opt.empty()) { std::cout << "/" << short_opt; } std::cout << "\n"; } return 0; #else parser.print_help(); std::cout << "\n"; std::vector> pass_cases = { {"my_arch1.xml", "my_circuit1.blif", "--analysis"}, {"my_arch2.xml", "my_circuit2.blif", "--analysis", "--pack"}, {"my_arch3.xml", "my_circuit3.blif", "--analysis", "--timing_analysis", "on"}, {"my_arch4.xml", "my_circuit4.blif", "--analysis", "--route_chan_width", "300"}, {"my_arch5.xml", "my_circuit5.blif", "--analysis", "--criticality_exp", "2"}, //Float from integer {"my_arch6.xml", "my_circuit6.blif", "--analysis", "--criticality_exp", "2.0"}, //Float {"my_arch6.xml", "my_circuit6.blif", "--analysis", "-j", "3"}, {"my_arch6.xml", "my_circuit6.blif", "--analysis", "-j3"}, //No-space for single letter arg {"my_arch6.xml", "my_circuit6.blif", "--analysis", "-j 3"}, //Space in short arg (but one string) {"my_arch6.xml", "my_circuit6.blif", "--analysis", "-j3", "--analysis"}, //Space in short arg (but one string) {"my_arch6.xml", "my_circuit6.blif", "--analysis", "--one_or_more", "3.24"}, //Single value argument {"my_arch6.xml", "my_circuit6.blif", "--analysis", "--one_or_more", "3.24", "10", "29"}, //Multiple values {"my_arch6.xml", "my_circuit6.blif", "--analysis", "--zero_or_more"}, //No values {"my_arch6.xml", "my_circuit6.blif", "--analysis", "--zero_or_more", "234"}, //One values {"my_arch6.xml", "my_circuit6.blif", "--analysis", "--zero_or_more", "234", "254", "1.23"}, //Multiple values {"my_arch6.xml", "my_circuit6.blif", "--analysis", "--zero_or_more", "234", "254", "1.23", "--one_or_more", "284"}, //* followed by + {"my_arch6.xml", "my_circuit6.blif", "--analysis", "--zero_or_more", "--one_or_more", "284"}, //* followed by + {"my_arch6.xml", "my_circuit6.blif", "--analysis", "--one_or_more", "284", "--zero_or_more", }, //+ followed by * {"my_arch6.xml", "my_circuit6.blif", "--analysis", "--one_or_more", "284", "--zero_or_more", "798"}, //+ followed by * {"my_arch6.xml", "--analysis", "--one_or_more", "3.24", "10", "29", "my_circuit6.blif"}, //positional after nargs='+' {"my_arch6.xml", "--analysis", "--one_or_more", "3.24", "10", "29", "my_circuit6.blif"}, //positional after nargs='+' {"my_arch6.xml", "--analysis", "--zero_or_more", "3.24", "10", "29", "my_circuit6.blif"}, //positional after nargs='*' }; int num_failed = 0; for(const auto& cmd_line : pass_cases) { bool pass = expect_pass(parser, cmd_line); if(!pass) { std::cout << " Failed to parse: '" << argparse::join(cmd_line, " ") << "'" << std::endl; ++num_failed; } } std::vector> fail_cases = { {"--analysis"}, //Missing positional {"my_arch7.xml", "--analysis"}, //Missing positional {"my_arch8.xml", "my_circuit8.blif", "--analysis", "extra"}, //Extra positional {"my_arch9.xml", "my_circuit9.blif", "--analysis", "--route_chan_width"}, //Missing value to option {"my_arch10.xml", "my_circuit10.blif", "--analysis", "--route_chan_width", "off"}, //Wrong option value type {"my_arch11.xml", "my_circuit11.blif", "--analysis", "--disp", "132"}, //Wrong option value {"my_arch12.xml", "my_circuit12.blif", "--analysis", "--route_chan_width", "300", "5"}, //Extra option value {"my_arch13.xml", "my_circuit13.blif", "--analysis", "--pack", "on"}, //Extra option value to toggle option {"my_arch14.xml", "my_circuit14.blif", "--analysis", "--route_chan_width", "300.5"}, //Type mismatch: float->int {"my_arch15.xml", "my_circuit15.blif", "--analysis", "--criticality_exp", "on"}, //Wrong value type for float {"my_arch16.xml", "my_circuit16.blif", "--analysis", "--slack_definition", "Z"}, //Valid type, but wrong choice {"my_arch17.xml", "my_circuit17.blif"}, //Missing required {"my_arch6.xml", "my_circuit6.blif", "--analysis", "-j", "3.4"}, //Float when expected unsigned {"my_arch6.xml", "my_circuit6.blif", "--analysis", "--one_or_more"}, //Expected at least one argument }; for(const auto& cmd_line : fail_cases) { bool pass = expect_fail(parser, cmd_line); if(!pass) { std::cout << " Parsed successfully when expected failure: '" << argparse::join(cmd_line, " ") << "'" << std::endl; ++num_failed; } } if (num_failed != 0) { std::cout << "\n"; std::cout << "FAILED: " << num_failed << " test(s)!" << "\n"; } return num_failed; #endif } bool expect_pass(argparse::ArgumentParser& parser, std::vector cmd_line) { try { parser.parse_args_throw(cmd_line); } catch(const argparse::ArgParseHelp&) { parser.reset_destinations(); std::cout << "[PASS] Parsed help OK" << std::endl; return true; } catch(const argparse::ArgParseError& err) { std::cout << "[FAIL] " << err.what() << std::endl; parser.reset_destinations(); return false; } std::cout << "[PASS] Parsed OK" << std::endl; parser.reset_destinations(); return true; } bool expect_fail(argparse::ArgumentParser& parser, std::vector cmd_line) { try { parser.parse_args_throw(cmd_line); } catch(const argparse::ArgParseError& err) { std::cout << "[PASS] " << err.what() << std::endl; parser.reset_destinations(); return true; } std::cout << "[FAIL] Parsed OK when expected fail" << std::endl; parser.reset_destinations(); return false; }