CLI Parsing in C++ with cxxopts: A Simple Guide

cxxopts

Streamlining Command-Line Arguments with cxxopts


In the realm of C++, numerous libraries handle command-line arguments effectively. Among them, Jarro2783’s cxxopts stands out, particularly for simpler programs, I’m currently using it on a project related to my Portable Executable Format blog post. For larger, complex applications, CLI11 often takes precedence. However, this post aims to provide a straightforward guide to leveraging cxxopts for your CLI program.

Employing a library like cxxopts eliminates the masculine urge to write a whole bunch of spaghetti-code of if-statements/switch statements to achieve desired functionalities in your CLI program.


Getting Started

To begin, include the cxxopts.h header file and configure your project settings.

#include <cxxopts.h>

int main(int argc, char* argv[])
{
    cxxopts::Options options("ProjectName", "Description of your program.");
}

This simple setup provides cxxopts with basic program information and a brief description, visible when utilizing your CLI program.

Next, define the actual flags for your CLI program, starting with what every CLI program should have; -h/–help flags, to indicate all available arguments.

#include <cxxopts.h>

int main(int argc, char* argv[])
{
    cxxopts::Options options("ProjectName", "Description of your program.");
    
    options.add_options()
		    ("h,help", "Help page.");
}

Here, both short and long options are provided. For instance, the user can use either -h or –help to access the help page, with the second argument serving as the flag description in the help page. However, many flags in CLI applications also require passing a value, depicted below:

options.add_options()
    ("h,help", "Program help page")
    ("j,json", "Json output file", cxxopts::value<std::string>());
    // -j/--json flag expects a string value from the user.
FlagsDescriptionType
h,helpProgram help pageBoolean
j,jsonJson output filestd::string
If you compare the table to the above code-snippet you will see that if you don’t include a third argument for the flag it will auto-set the third argument to a boolean.

Now, proceed to check the flags to determine user-provided inputs:

#include <cxxopts.h>

int main(int argc, char* argv[])
{
    cxxopts::Options options("ProjectName", "Description of your program.");
    
    options.add_options()
		    ("h,help", "Help page.");
		    
		try 
		{
		    auto result{ options.parse(argc, argv) };
		    
		    if (result.count("help"))
		    {
			      std::cout << options.help() << std::endl;
			      return 0;
		    }
		}
		catch (const std::exception& e)
	  {
		    std::cerr << "Error parsing options: " << e.what() << std::endl;
		    return 1;
	  }
}

The check auto result{ options.parse(argc, argv) }; is contained within a try-block to manage potential issues arising from a user input being feral and not reading documentation or the help page.

Upon determining the user’s provided inputs, the program can evaluate if specific arguments were used, as shown in result.count("help"). Additionally, cxxopts shines here by generating its own help page, accessible via options.help().


Handling Positional Arguments with cxxopts

Positional arguments require a specific order for processing. For example, consider a CLI program that transforms one file into another. It’s essential to input the file to be processed before specifying the output file. Positional arguments ensure this sequential processing.

To manage positional arguments, cxxopts provides parse_positional(), which enforces the required order:

#include <cxxopts.h>
#include <iostream>

int main(int argc, char* argv[])
{
    cxxopts::Options options("ProjectName", "Description of your program.");
    
    options.add_options()
		    ("h,help", "Help page")
		    ("input", "Input file", cxxopts::value<std::string>())
		    ("output", "Output file", cxxopts::value<std::string>());
		    
		options.parse_positional({ "input", "output" }); // <--
		    
		try 
		{
		    auto result{ options.parse(argc, argv) };
		    
		    if (result.count("help"))
		    {
			      std::cout << options.help() << std::endl;
			      return 0;
		    }
		    
		    if (result.count("input"))
		    {
		        std::cout << "Input file provided\n";
		    }
		    
		    if (result.count("output"))
		    {
		        std::cout << "Output file provided\n";
		    }
		    
		}
		catch (const std::exception& e)
	  {
		    std::cerr << "Error parsing options: " << e.what() << std::endl;
		    return 1;
	  }
	  
	  return 0;
}

This setup ensures the correct order of input and output files for processing. To access the user-provided positional arguments, utilize the option name as an index in the option parser:

//CLI INPUT: ProjectName input.png output.png

std::string inputFile { result["input"].as<std::string>() }; // input.png
std::string outputFile { result["output"].as<std::string>() }; // output.png

Conclusion of cxxopts

As you can see the cxxopts library is very simple to use and saves us an incredible amount of time by doing all the heavy-lifting dealing with CLI inputs. I hope this serves as a helpful guide to kickstart your journey with this remarkable library.

Leave a Reply

Your email address will not be published. Required fields are marked *