I think you want a combination of composing and multi-token:
Live On Coliru
#include <boost/program_options.hpp> #include <fmt/ranges.h> namespace po = boost::program_options; using strings = std::vector<std::string>; int main(int argc, char** argv) { po::options_description opts; opts.add_options()("param", po::value<strings>() ->multitoken() ->composing() ->default_value({}, "") ->implicit_value({}, ""), "you know the drill"); po::variables_map vm; store(parse_command_line(argc, argv, opts), vm); if (vm.contains("param")) { fmt::print("Params: {}\n", vm["param"].as<strings>()); } }
Which prints e.g.:
+ ./a.out Params: [] + ./a.out --param Params: [] + ./a.out --param a b Params: ["a", "b"] + ./a.out --param a b --param c d Params: ["a", "b", "c", "d"]
Note, if you don't want to allow --param without a value, remove the implicit_value: Live On Coliru
UPDATE
To the comment:
You can always use the parser section of the library, inspecting the parsed_options instance:
// using parsed-options to see "source" material: auto parsed = parse_command_line(argc, argv, opts); for (auto& po : parsed.options) { auto key = po.position_key == -1 // ? po.string_key : '#' + std::to_string(po.position_key); fmt::print("Option {}: {}\n", key, po.value); } // original logic: store(parsed, vm); if (vm.contains("param")) { fmt::print("Effective combined params: {}\n", vm["param"].as<strings>()); }
See it Live On Coliru, printing:
+ ./a.out Effective combined params: [] + ./a.out --param Option param: [] Effective combined params: [] + ./a.out --param a b Option param: ["a", "b"] Effective combined params: ["a", "b"] + ./a.out --param a b --param c d Option param: ["a", "b"] Option param: ["c", "d"] Effective combined params: ["a", "b", "c", "d"]