argparse
argparse copied to clipboard
Alternative implementation for subparsers
Sorry this came late, but I recently (in Feb!) also made an implementation for subparsers, so I thought I'd share it still. It's at https://github.com/limwz01/argparse_mf/tree/with_subparsers with the commit https://github.com/limwz01/argparse_mf/commit/fccc2102e605d0c34f2d1ca2f8ed9c606c6694e4 . It takes a slightly different approach, but it is slightly more general, allowing specifying main options as well, something like prog --main_option subparser --sub_option.
Here's the subparsers' sample in my version:
#include <iostream>
#include "argparse/argparse.hpp"
using namespace std;
struct CommitArgs : public argparse::Args {
bool &all = flag("a,all", "Tell the command to automatically stage files that have been modified and deleted, but new files you have not told git about are not affected.");
std::string &message = kwarg("m,message", "Use the given <msg> as the commit message.");
};
struct PushArgs : public argparse::Args {
std::string &source = arg("Source repository").set_default("origin");
std::string &destination = arg("Destination repository").set_default("master");
void welcome() override {
std::cout << "Push code changes to remote" << std::endl;
}
};
struct MyArgs : public argparse::Args {
bool &version = flag("v,version", "Print version");
CommitArgs &commit = subparser("commit", "commit changes to repository");
PushArgs &push = subparser("push", "push changes to remote");
};
int main(int argc, char* argv[]) {
auto args = argparse::parse<MyArgs>(argc, argv);
if (args.version) {
std::cout << "argparse_subcomands version 1.0.0" << std::endl;
return 0;
}
args.print();
if (args.commit.get_is_parsed()) {
std::cout << "running commit with the with the following message: " << args.commit.message << std::endl;
} else if (args.push.get_is_parsed()) {
std::cout << "running push with the following parameters" << std::endl;
args.push.print();
}
return 0;
}
Hi @limwz01 thanks for sharing your alternative approach for subcommands! You are right, this approach has some benefits for being able to access the main and sub command arguments by iterating over them in the main function.
While it is not documented, it is also possible to do so using the current implementation. Similar to your implementation you can check for each subcommand if they re valid and proceed to execute the logic behind this instead of using the args.run_subcommands():
if (args.commit.is_valid) {
std::cout << "running commit with the with the following message: " << args.commit.message << std::endl;
} else if (args.push.is_valid) {
std::cout << "running push with the following parameters" << std::endl;
args.push.print();
} else {
std::cout << "No subcommand given" << std::endl;
}
The run_subcommands function is using this method under the hood to iterate and execute all subcommands.
If more people prefer this approach I will add this to the documentation. Thanks for bringing this up!
I will keep this open so others can see your implementation as well.
I now understand what you mean when you say your approach also allows main program arguments. I didn't notice that you still parse the main arguments even after detecting a subcommand. A possible issue with your current approach is that now arguments that just happen to have the subcommand name as value will not work, e.g. git --config push push where --config is an argument which takes one string value. But sure, this is probably very uncommon. Even having main program args is not very common either.