Skip to content

rail5/xgetopt

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

XGetOpt

XGetOpt is a simple, header-only, constexpr-first C++20 library for parsing command-line options.

See the wiki for complete documentation and examples.

Features

  • Fully constexpr, compile-time help-string generation
  • First-class support for non-option arguments
  • Support for early-stop parsing via parse_until<StopCondition>()
    • Stop before/after first non-option argument, or before first error
    • Remainder of unparsed arguments provided for further processing
  • Preservation of getopt semantics
    • Short options, long options, optional arguments, option clustering, etc
    • Matches user expectations
    • XGetOpt is in fact a wrapper around getopt_long

The basic design goals are:

  • Simple to use
  • No added cost compared to using getopt_long directly
  • Preservation of familiar semantics

Example Usage

#include "xgetopt.h"
#include <iostream>

int main(int argc, char* argv[]) {
	constexpr XGetOpt::OptionParser<
		XGetOpt::Option<'h', "help", "Display this help message", XGetOpt::NoArgument>,
		XGetOpt::Option<'o', "output", "Specify output file", XGetOpt::RequiredArgument, "file">,
		XGetOpt::Option<'p', "parameter", "Specify optional parameter", XGetOpt::OptionalArgument>,
		XGetOpt::Option<1001, "long-option-only", "This has no shortopt", XGetOpt::NoArgument>,
		XGetOpt::Option<1002, "long-option-with-arg", "This has no shortopt and requires an argument", XGetOpt::RequiredArgument>,
		XGetOpt::Option<'s', "", "This has no longopt", XGetOpt::NoArgument>
	> parser;
	
	XGetOpt::OptionSequence options;

	try { // .parse() will throw an exception if invalid options are provided
		options = parser.parse(argc, argv);
	} catch (const std::exception& e) {
		std::cerr << e.what() << std::endl;
		return 1;
	}

	for (const auto& opt : options) {
		switch (opt.getShortOpt()) {
			case 'h':
				std::cout << parser.getHelpString();
				return 0;
			case 'p': // Optional argument
				if (opt.hasArgument()) {
					std::cout << "Parameter option with argument: " << opt.getArgument() << std::endl;
				} else {
					std::cout << "Parameter option with no argument" << std::endl;
				}
				break;
			case 1001: // --long-option-only
				std::cout << "--long-option-only given" << std::endl;
				break;
			// Etc
		}
	}

	for (const auto& arg : options.getNonOptionArguments()) {
		std::cout << "Non-option argument: " << arg << std::endl;
	}
}

The generated help string looks like this:

  -h, --help                       Display this help message
  -o, --output <file>              Specify output file
  -p, --parameter[=arg]            Specify optional parameter
      --long-option-only           This has no shortopt
      --long-option-with-arg <arg> This has no shortopt and requires an argument
  -s                               This has no longopt

And is fully generated at compile-time.

Subcommand Pattern Example

#include "xgetopt.h"
#include <iostream>

int main(int argc, char* argv[]) {
	constexpr XGetOpt::OptionParser<
		XGetOpt::Option<'h', "help", "Display this help message", XGetOpt::NoArgument>,
		XGetOpt::Option<'o', "output", "Specify output file", XGetOpt::RequiredArgument, "file">,
		XGetOpt::Option<'p', "parameter", "Specify optional parameter", XGetOpt::OptionalArgument>
	> parser;

	constexpr XGetOpt::OptionParser<
		XGetOpt::Option<'a', "alpha", "Alpha option for subcommand", XGetOpt::NoArgument>,
		XGetOpt::Option<'b', "beta", "Beta option for subcommand", XGetOpt::RequiredArgument, "value">,
		XGetOpt::Option<'h', "help", "Display this help message for subcommand", XGetOpt::NoArgument>
	> subCommandParser;

	try {
		// The first non-option argument is treated as the subcommand name
		auto [options, remainder] = parser.parse_until<XGetOpt::BeforeFirstNonOptionArgument>(argc, argv);

		for (const auto& opt : options) {
			// Base program options processing...
		}

		if (remainder.argc == 0) {
			return 0; // No subcommand, done
		}

		std::string_view subcommandName = remainder.argv[0];

		if (subcommandName == "subcmd") {
			auto subOptions = subCommandParser.parse(remainder.argc, remainder.argv);
			for (const auto& opt : subOptions) {
				// Subcommand options processing...
			}
		}
	} catch (const std::exception& e) {
		std::cerr << e.what() << std::endl;
		return 1;
	}
}

In the above example, the first non-option argument is treated as the subcommand name, and the remaining arguments are parsed using a separate parser for that subcommand.

API Reference

See the wiki for complete documentation and examples.

Debian Package

Users of Debian-based systems can install XGetOpt via the deb.rail5.org repository:

sudo curl -s -o /etc/apt/trusted.gpg.d/rail5-signing-key.gpg "https://deb.rail5.org/rail5-signing-key.gpg"
sudo curl -s -o /etc/apt/sources.list.d/rail5.list "https://deb.rail5.org/rail5.list"
sudo apt update
sudo apt install xgetopt-dev

Compatibility

XGetOpt does not guarantee compatibility outside of the GNU ecosystem, but it should work with most getopt_long implementations. See the tests/README.md for a table of tested platforms.

License

XGetOpt is released under the GNU General Public License v2 or later. See the LICENSE file for details.

About

Header-only, constexpr-first C++20 command-line option parsing library

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors