Introduction

This library attempts to implement the POSIX.1-2017 standard for parsing arguments passed to an application. These arguments are delivered to the main function as the well known argc and argv parameters. This library allows you to parse the contents of these variables and then provides easy access to the information.

The library also contains code to parse configuration files marked up in the well known .ini file format. Support for sections in these files is supported as of version 2.0.

Synopsis

// Example of using libmcfp

#include <iomanip>
#include <iostream>
#include <vector>

#include "mcfp/mcfp.hpp"

int main(int argc, char *const argv[])
{
    // config is a singleton
    auto &config = mcfp::config::instance();

    // Initialise the config object. This can be done more than once,
    // e.g. when you have different sets of options depending on the
    // first operand.

    config.init(
              // The first parameter is the 'usage' line, used when printing out the options
              "usage: example [options] file",

              // Flag options (not taking a parameter)
              mcfp::make_option("help,h", "Print this help text"),
              mcfp::make_option("verbose,v", "Verbose level, can be specified more than once to increase level"),

              // A couple of options with parameter
              mcfp::make_option<std::string>("config", "Config file to use"),
              mcfp::make_option<std::string>("text", "The text string to echo"),

              // And options with a default parameter
              mcfp::make_option<int>("a", 1, "first parameter for multiplication"),
              mcfp::make_option<float>("b", 2.0f, "second parameter for multiplication"),

              // You can also allow multiple values
              mcfp::make_option<std::vector<std::string>>("c", "Option c, can be specified more than once"),

              // This option is not shown when printing out the options
              mcfp::make_hidden_option("d", "Debug mode"))
        .add_section("section-1",
            mcfp::make_option<std::string>("text", "Another text option, now part of section-1"),

            mcfp::make_option<std::string>("an-option-with-a-long-name",
                "Shows that the output of help ends up correctly and wrapped as well if you have a small terminal"));

    // There are two flavors of calls, ones that take an error_code
    // and return the error in that code in case something is wrong.
    // The alternative is calling without an error_code, in which
    // case an exception is thrown when appropriate

    // Parse the command line arguments here

    std::error_code ec;
    config.parse(argc, argv, ec);
    if (ec)
    {
        std::cerr << "Error parsing argument " << std::quoted(config.get_last_option()) << ": " << ec.message() << '\n';
        exit(1);
    }

    // First check, to see if we need to stop early on

    if (config.has("help") or config.operands().size() != 1)
    {
        // Tell user what was wrong
        // This will print out the 'usage' message with all the visible options
        std::cerr << config << '\n';

        if (config.operands().size() != 1)
            std::cerr << "Invalid number of operands, should be exactly one\n\n";

        exit(config.has("help") ? 0 : 1);
    }

    // Configuration files, read it if it exists. If the users
    // specifies an alternative config file, it is an error if that
    // file cannot be found.

    config.parse_config_file("config", "example.conf", { "." }, ec);
    if (ec)
    {
        std::cerr << "Error parsing config file, option " << std::quoted(config.get_last_option()) << ": " << ec.message() << '\n';
        exit(1);
    }

    // If options are specified more than once, you can get the count

    int VERBOSE = config.count("verbose");

    // Operands are arguments that are not options, e.g. files to act upon

    std::cout << "The first operand is " << config.operands().front() << '\n';

    // Getting the value of a string option

    auto text = config.get<std::string>("text", ec);
    if (ec)
    {
        std::cerr << "Error getting option text: " << ec.message() << '\n';
        exit(1);
    }

    std::cout << "Text option is " << std::quoted(text) << '\n';

    // Alternative, using get_optional

    if (auto t1 = config.get_optional("text"))
        std::cout << "Text option still is " << std::quoted(*t1) << '\n';

    // getting values for numeric options

    if (config.has("a") and config.has("b"))
    {
        int a = config.get<int>("a");
        float b = config.get<float>("b");

        std::cout << "a (" << a << ") * b (" << b << ") = " << a * b << '\n';
    }

    // And multiple strings

    for (const std::string& s : config.get<std::vector<std::string>>("c"))
        std::cout << "c: " << s << '\n';

    // Section support

    if (auto t = config.get_optional("section-1.text"); t.has_value())
        std::cout << "Text option for 'section-1' is " << std::quoted(*t) << '\n';

    return 0;
}

Running the program without any options, or --help results in:

usage: example [options] file

  -h [ --help ]           Print this help text
  -v [ --verbose ]        Verbose level, can be specified more than once to
                          increase level.
  --config arg            Config file to use
  --text arg              The text string to echo
  -a arg (=1)             first parameter for multiplication
  -b arg (=2)             second parameter for multiplication
  -c arg                  Option c, can be specified more than once

section "section-1"

  --section-1.text arg    Another text option, now part of section-1
  --section-1.an-option-with-a-long-name arg
                          Shows that the output of help ends up correctly and
                          wrapped as well if you have a small terminal.

Invalid number of operands, should be exactly one

Options and Operands

The POSIX standard defines two kinds of arguments, the ones whose name start with a hyphen are called options whereas the rest is called operands. An example is:

my-tool [-v] [-o option_arg] [operand...]

The option -o in the example above has an option-argument, the -v does not. Operands usually follow the options, but in the case of libmcfp options and operands can be mixed.

configuration files

The syntax for configuration files is the usual format of name followed by an equals character and then a value terminated by an end of line. E.g.:

name = value

The name here should be the long name of the option, not the short name.

As of version 2.0, mcfp supports more .ini file syntax rules. Like comments that can start with either a hash (#) or a semicolon (;) character. And there is support for sections. If your config file contains:

# A simple ini file with two sections, this is the first
[one]
name = value1

; And this is the second
[second]
name = value2

Then you can access these values like this:

auto &config = mcfp::config::instance();

...

config.get("one.name"); // returns "value1"

The function parse_config_file() can be used to parse these files. The first variant of this function is noteworthy, it takes an option name and uses its option-argument if specified as replacement for the second parameter which holds the default configuration file name. This file is then searched in the list of directories in the third parameter and when found, the file is parsed and the options in the file are appended to the config instance. Options provided on the command line take precedence.

For flag options (ones that do not take an argument on the command line) an argument is required in the config file. This value can be either true, false or an integral numerical value. So, the equivalent of passing -vvv on the command line is verbose = 3 in a config file if the option was inited with mcfp::make_option(“verbose,v”).

Use from a library

Suppose you have a library that would like to have configurable options. This library can specify these options in a section by using the static call mcfp::config::init_lib. The options passed to this call will be appended as a section to the global set of options each time they are reset using mcfp::config::init.

Installation

To build mcfp you will need a very recent compiler. This code has been tested with GCC 14 and CLang 22 as well as the compiler that comes with Visual Studio version 2026.

Other requirements are CMake version 3.28 or higher.

git clone https://github.com/mhekkel/libmcfp.git
cd libmcfp
cmake -B build
cmake --build build
cmake --install build