Last active
May 25, 2024 08:26
-
-
Save randomphrase/10801888 to your computer and use it in GitHub Desktop.
Demonstration of how to do subcommand option processing with boost program_options
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define BOOST_TEST_MODULE subcommand options | |
#include <boost/test/unit_test.hpp> | |
#include <boost/program_options.hpp> | |
#include <boost/variant/variant.hpp> | |
#include <boost/variant/get.hpp> | |
struct GenericOptions { | |
bool debug_; | |
}; | |
struct LsCommand : public GenericOptions { | |
bool hidden_; | |
std::string path_; | |
}; | |
struct ChmodCommand : public GenericOptions { | |
bool recurse_; | |
std::string perms_; | |
std::string path_; | |
}; | |
typedef boost::variant<LsCommand, ChmodCommand> Command; | |
Command ParseOptions(int argc, const char *argv[]) | |
{ | |
namespace po = boost::program_options; | |
po::options_description global("Global options"); | |
global.add_options() | |
("debug", "Turn on debug output") | |
("command", po::value<std::string>(), "command to execute") | |
("subargs", po::value<std::vector<std::string> >(), "Arguments for command"); | |
po::positional_options_description pos; | |
pos.add("command", 1). | |
add("subargs", -1); | |
po::variables_map vm; | |
po::parsed_options parsed = po::command_line_parser(argc, argv). | |
options(global). | |
positional(pos). | |
allow_unregistered(). | |
run(); | |
po::store(parsed, vm); | |
std::string cmd = vm["command"].as<std::string>(); | |
if (cmd == "ls") | |
{ | |
// ls command has the following options: | |
po::options_description ls_desc("ls options"); | |
ls_desc.add_options() | |
("hidden", "Show hidden files") | |
("path", po::value<std::string>(), "Path to list"); | |
// Collect all the unrecognized options from the first pass. This will include the | |
// (positional) command name, so we need to erase that. | |
std::vector<std::string> opts = po::collect_unrecognized(parsed.options, po::include_positional); | |
opts.erase(opts.begin()); | |
// Parse again... | |
po::store(po::command_line_parser(opts).options(ls_desc).run(), vm); | |
LsCommand ls; | |
ls.debug_ = vm.count("debug"); | |
ls.hidden_ = vm.count("hidden"); | |
ls.path_ = vm["path"].as<std::string>(); | |
return ls; | |
} | |
else if (cmd == "chmod") | |
{ | |
// Something similar | |
} | |
// unrecognised command | |
throw po::invalid_option_value(cmd); | |
} | |
BOOST_AUTO_TEST_CASE(NoCommand) | |
{ | |
const int argc = 2; | |
const char *argv[argc] = { "0", "nocommand" }; | |
BOOST_CHECK_THROW( | |
ParseOptions(argc, argv), | |
boost::program_options::invalid_option_value); | |
} | |
BOOST_AUTO_TEST_CASE(LsTest) | |
{ | |
const int argc = 5; | |
const char *argv[argc] = { "0", "--debug", "ls", "--hidden", "--path=./" }; | |
Command c = ParseOptions(argc, argv); | |
BOOST_REQUIRE(boost::get<LsCommand>(&c)); | |
const LsCommand& ls = boost::get<LsCommand>(c); | |
BOOST_CHECK(ls.debug_); | |
BOOST_CHECK(ls.hidden_); | |
BOOST_CHECK_EQUAL(ls.path_, "./"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@markusdr
I know this reply comes late but I just had the same issue...
You can fix this by checking if a command was provided.
If you just use
if (vm.count("help")) std::cout << global << std::endl;
, you get the same help output whenever a--help
flag is set anywhere in your options.You can use this instead and you will get your different outpus: