Programming in C++

Commandline Arguments

Gerald Senarclens de Grancy

Purpose of Commandline Arguments

  • Allow the user to pass information to a program upon startup
    • Which input files should be processed
      clang++ infile.cpp
    • Options can be defined
      ls -l -a -h
    • Possible subcommands
      apt install programname

Issues

  • Not all operating systems pass arguments in the same way
  • String parsing is tedious
  • POSIX getopt ( unistd.h) has limited features (eg. no long options)
  • 3rd party libraries offer differing feature sets
  • Argument parsing libraries are usually not trivial to use

Foundation of Argument Passing

In C++, arguments are passed to main(.)

argc
argument count
argv
argument vector
int main(int argc, char** argv) {
  /* work with arguments */
  return 0;
}

Example: Echo Arguments Without Using a Library

#include <iostream>

int main(int argc, char **argv) {
  for (int i{0}; i < argc; ++i) {
    std::cout << argv[i] << std::endl;
  }
  return 0;
}
Download echo.cpp

Example: Show a Simple Help Message

#include <iostream>
#include <cstdlib>
#include <string>
using std::cout, std::endl, std::string;
int main(int argc, char **argv) {
  for (int i{1}; i < argc; ++i) {
    string arg{argv[i]};
    if (arg == "-h" or arg == "--help") {
      cout << "Usage: a.out [OPTION]... " << endl;
      cout << "-h, --help display this help and exit" << endl;
      std::exit(0);
    }
  }
  cout << "To solve the world's problems, use the correct arguments" << endl;
  return 0;
}
Download help.cpp

POSIX Standard getopt

+
standardized
available (almost) everywhere
-
no support for long options
lack of support for subcommands etc.
#include <iostream>
#include <string>
#include <unistd.h>  /* for getopt */
using std::cout, std::endl, std::oct, std::string;
int main (int argc, char **argv) {
  int c{0};
  bool aopt = false, bopt = false;  // flags
  string copt, dopt;  // option arguments
  while ((c = getopt(argc, argv, "abc:d:")) != -1) {
    switch (c) {
    case 'a':
      aopt = true;
      break;
    case 'b':
      bopt = true;
      break;
    case 'c':
      copt = optarg;
      break;
    case 'd':
      dopt = optarg;
      break;
    default:
      cout << "?? getopt returned character code 0" << oct << c << " ??\n";
    }
  }
  if (optind < argc) {
    cout << "non-option ARGV-elements: ";
    while (optind < argc) {
      cout << argv[optind++] << " ";
    }
    cout << endl;
  }
  cout << "aopt: " << aopt << ", bopt: " << bopt << ", copt: " << copt
       << ", dopt: " << dopt << endl;
  return 0;
}
Download posix_getopt.cpp

GNU getopt_long

+
widely available GNU extenstion
supports long options
-
not available everywhere
lack of support for subcommands etc.
name
a long option's name
has_arg
no_argument | required_argument | optional_argument
arguments, if provided, are stored in optarg; otherwise optarg is NULL
flag
if flag is NULL, getopt_long returns val
if flag is a pointer to an int variable, val is assigned to the variable
and getopt_long returns 0
val
specifies the value returned by getopt_long or stored in the flag variable depending on the scenario
#include <getopt.h>  // getopt_long
#include <iostream>
#include <string>
using std::cout, std::endl;
int main(int argc, char** argv) {
  int c;
  bool has_a = false, has_b = false;  // flags
  int is_superuser = 0;
  int verbosity_level = 0;
  std::string c_arg, d_arg;  // option_arguments
  static struct option long_options[] = {
  /* NAME       HAS_ARG            FLAG  VAL (SHORTNAME) */
    {"add",     required_argument, NULL, 0},
    {"append",  no_argument,       NULL, 1000},
    {"delete",  no_argument,       NULL, 0},
    {"verbose", no_argument,       NULL, 'v'},
    {"create",  required_argument, NULL, 'c'},
    {"file",    required_argument, NULL, 0},
    {"config",  optional_argument, NULL, 1001},
    {"su",      optional_argument, &is_superuser,  1},
    {NULL,      0,                 NULL, 0}
  };
  int option_index = 0;
  while ((c = getopt_long(argc, argv, "abc:d:vz",
          long_options, &option_index)) != -1) {
    switch (c) {
    case 0:
      cout << "option " << long_options[option_index].name;
      if (optarg) {
        cout << " with arg " << optarg;
      }
      cout << endl;
      break;
    case 1000:  // append
      cout << "`append` option was given" << endl;
      break;
    case 1001:  // config
      cout << "`config` option was given";
      if (optarg) {  // --config=configfilename
        cout << " with arg " << optarg;
      }
      cout << endl;
      break;
    case 'a':
      has_a = true;
      break;
    case 'b':
      has_b = true;
      break;
    case 'c':
      c_arg = optarg;
      break;
    case 'd':
      d_arg = optarg;
      break;
    case 'v':
      ++verbosity_level;
      break;
    case '?':  // getopt_long returns '?' on error (unknown option)
      cout << "dealing with invalid option (see getopt_long output)" << endl;
      break;
    default:  // "catches" option 'z' which isn't dealt with above
      cout << "getopt returned character code 0" << std::oct << c << " ??\n";
    }
  }
  if (optind < argc) {
    cout << "non-option ARGV-elements: ";
    while (optind < argc) {
      cout << argv[optind++] << " ";
    }
    cout << endl;
  }
  if (is_superuser) {
    cout << "Superuser mode is on" << endl;
  }
  cout << "a: " << has_a << ", b: " << has_b << ", c_arg: " << c_arg
       << ", d_arg: " << d_arg << ", verbosity-level: " << verbosity_level
       << endl;

  return 0;
}
Download gnu_getopt.cpp

Other Libraries

There are many libraries for argument parsing...

... select the one that suits your needs, eg.

argparse - Argument Parser for Modern C++
https://github.com/p-ranav/argparse
Requires C++17 or newer
Single header library
MIT license
sudo apt install libargparse-dev
Boost Program Options
https://github.com/boostorg/program_options
Chapter 29. Boost.Program_options
BSL-1.0 license
Requires linking (some) Boost C++ Libraries
sudo apt install libboost-program-options1.83-dev
cxxopts
https://github.com/jarro2783/cxxopts
MIT license
sudo apt install libcxxopts-dev

Comprehensive Example(s)

Look at the source code of your favorite commanline programs...

This can be done via apt source $PACKAGE

eg. apt source htop

Or checkout a repository with an example
GNU getopt
eg. cvrptwms cli.c
Boost Program Options
eg. cvrptwms main_cli.cpp

Exercise

Write a C++ program that takes the following arguments

-h, --help
-a, --author
display your name and email address
--color {red,green,blue}
display a message in the given color

Questions
and feedback...