C++ Boost Program Options – Fast and Dirty

A lot of tutorials and articles on this subject are wonderful in-depth studies of parsing command line arguments with Boost Program Options. This is not one of them. This article is aimed at getting us on the ground and running with a common scenario.

Let’s say we want to develop a command line utility to extract data from some data file written by something else in our project. We might start out with a hard coded path to an example data file. But at some point we are going to want to pass in arguments. The first argument to consider is a path to an arbitrary data file, so we can use our utility without changing code. Next we might want to add a flag to turn on or off behavior. Let’s say that during the course of development we added a few informational messages. Now we would like to suppress those so that our output can be piped into another command line utility or file. Finally, maybe we are not happy with the extracted data just going to standard out for piping. Then we need another optional argument that provides the path to an output file.

In the end we want something like this:


Usage:
./extract-data [options] <input-file>


Allowed options:
-h [ –help ] help message
-q [ –quiet ] suppress messages to stdout
-o [ –output-file ] arg output file path: /path/to/file
–input-file arg Path to data file: /path/to/data/file

Setting up these command line arguments in C++ with Boost Program Options is easy.  The example below is a good place to start.

#include <iostream>
#include <boost/program_options.hpp>


namespace po = boost::program_options;

int main(int argc, char** argv)
{
    try
    {

        po::options_description desc("Allowed options");
        desc.add_options()
                ("help,h", "help message")
                ("quiet,q", "suppress messages to stdout")
                ("output-file,o",  po::value< std::string >(), "output file path: /path/to/file")
                ("input-file", po::value< std::string >(), "Path to data file: /path/to/data/file")
                ;

        po::positional_options_description p;
        p.add("input-file", 1);

        po::variables_map vm;

        po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
        po::notify(vm);

        if (vm.count("help"))
        {
            std::cout << "Usage: \n  " << argv[0] << " [options] <" << p.name_for_position(0) << ">\n\n";
            std::cout << desc << std::endl;
            
            exit(0);
        }

        // Require the input file - exit with usage message and error if not present
        if (vm.count("input-file") == 0 )
        {
            std::cout << "Usage: \n  " << argv[0] << " [options] <" << p.name_for_position(0) << ">\n\n";
            std::cout << desc << std::endl;
            
            exit(1);
        }

        // program input parameters
        std::string inputFile;
        bool quietMode = false;
        std::string outputFile;
        
        // input file
        if (vm.count("input-file"))
        {
            inputFile = vm["input-file"].as< std::string >();
            std::cout << "Input File: " << inputFile << std::endl;
        }
        
        // quiet flag 
        if (vm.count("quiet"))
        {
            quietMode = true;
            std::cout << "Quiet Mode: " << std::boolalpha << quietMode << std::endl;
        }
        
        // output file
        if (vm.count("output-file"))
        {
            outputFile = vm["output-file"].as< std::string >();
            std::cout << "Output File: " << outputFile << std::endl;
        }
        
       

    }
    catch (std::exception& e)
    {
        std::cerr << "error: " << e.what() << "\n";
        exit(1);
    }
    catch (...)
    {
        std::cerr << "Exception of unknown type!\n";
        exit(1);
    }
    return 0;
}