Command Line Subcommands in C++

Here’s the translation of the Go code to C++, along with explanations in Markdown format suitable for Hugo:

Our program demonstrates the use of subcommands in command-line applications. Some command-line tools have many subcommands, each with its own set of flags. For example, git commit and git push are two different subcommands of the git tool. We’ll use the <cxxopts> library to easily define simple subcommands that have their own flags.

#include <iostream>
#include <cxxopts.hpp>
#include <string>
#include <vector>

int main(int argc, char* argv[]) {
    // We declare subcommands using cxxopts::Options objects,
    // and proceed to define new options specific for each subcommand.
    cxxopts::Options fooCmd("foo", "Foo subcommand");
    fooCmd.add_options()
        ("e,enable", "Enable", cxxopts::value<bool>()->default_value("false"))
        ("n,name", "Name", cxxopts::value<std::string>()->default_value(""));

    // For a different subcommand we can define different
    // supported options.
    cxxopts::Options barCmd("bar", "Bar subcommand");
    barCmd.add_options()
        ("l,level", "Level", cxxopts::value<int>()->default_value("0"));

    // The subcommand is expected as the first argument
    // to the program.
    if (argc < 2) {
        std::cout << "expected 'foo' or 'bar' subcommands" << std::endl;
        return 1;
    }

    // Check which subcommand is invoked.
    std::string subcommand = argv[1];

    // For every subcommand, we parse its own options and
    // have access to trailing positional arguments.
    if (subcommand == "foo") {
        auto result = fooCmd.parse(argc - 1, argv + 1);
        std::cout << "subcommand 'foo'" << std::endl;
        std::cout << "  enable: " << (result["enable"].as<bool>() ? "true" : "false") << std::endl;
        std::cout << "  name: " << result["name"].as<std::string>() << std::endl;
        std::cout << "  tail: ";
        for (const auto& arg : result.unmatched()) {
            std::cout << arg << " ";
        }
        std::cout << std::endl;
    } else if (subcommand == "bar") {
        auto result = barCmd.parse(argc - 1, argv + 1);
        std::cout << "subcommand 'bar'" << std::endl;
        std::cout << "  level: " << result["level"].as<int>() << std::endl;
        std::cout << "  tail: ";
        for (const auto& arg : result.unmatched()) {
            std::cout << arg << " ";
        }
        std::cout << std::endl;
    } else {
        std::cout << "expected 'foo' or 'bar' subcommands" << std::endl;
        return 1;
    }

    return 0;
}

To compile the program:

$ g++ -std=c++11 command_line_subcommands.cpp -o command_line_subcommands -lcxxopts

First invoke the foo subcommand:

$ ./command_line_subcommands foo --enable --name=joe a1 a2
subcommand 'foo'
  enable: true
  name: joe
  tail: a1 a2 

Now try bar:

$ ./command_line_subcommands bar --level 8 a1
subcommand 'bar'
  level: 8
  tail: a1 

But bar won’t accept foo’s flags:

$ ./command_line_subcommands bar --enable a1
error: unrecognised option '--enable'

Next we’ll look at environment variables, another common way to parameterize programs.