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.