Library for parsing command line arguments and config files.
  • C++ 62.5%
  • CMake 37.5%
Find a file
Maarten L. Hekkelman 3aa8f7fb36
All checks were successful
test on Debian / build (Release, g++, OFF, debian-latest) (push) Successful in 2m41s
test on Debian / build (Release, clang++-22, OFF, debian-latest) (push) Successful in 2m46s
test on FreeBSD / build (Release, c++, ON, freebsd-latest) (push) Successful in -16h39m19s
test on Debian / build (Release, clang++-22, ON, debian-latest) (push) Successful in 3m7s
Adding namespace
2026-08-10 14:21:33 +02:00
.forgejo/workflows Error message improved, 2026-06-05 15:11:34 +02:00
.github Fix links 2026-06-10 15:14:42 +02:00
cmake Fix cmake config file 2026-06-16 08:56:26 +02:00
docs Re-organise code, again 2026-06-15 10:40:26 +02:00
examples Renamed module macro 2026-07-19 10:04:03 +02:00
include/mcfp Adding namespace 2026-08-10 14:21:33 +02:00
LICENSES Update licenses 2026-06-03 14:13:16 +02:00
src Merge remote-tracking branch 'origin/fix/config-name-char' into merge-attempt 2026-08-10 09:32:06 +02:00
test Renamed module macro 2026-07-19 10:04:03 +02:00
.clang-format fix fastfloat name, version bump 2026-03-24 09:48:22 +01:00
.clang-tidy fix fastfloat name, version bump 2026-03-24 09:48:22 +01:00
.gitignore No <print> needed, just throw. 2026-08-08 21:15:24 +02:00
changelog Version bump, update changelog 2026-08-09 07:37:28 +02:00
changelog.license fix fastfloat name, version bump 2026-03-24 09:48:22 +01:00
CMakeLists.txt Version bump, update changelog 2026-08-09 07:37:28 +02:00
README.md Renamed module macro 2026-07-19 10:04:03 +02:00
vcpkg-configuration.json So sad... 2026-06-10 09:38:04 +02:00
vcpkg-configuration.json.license Add licenses to silence reuse 2026-06-10 10:35:42 +02:00
vcpkg.json So sad... 2026-06-10 09:38:04 +02:00
vcpkg.json.license Add licenses to silence reuse 2026-06-10 10:35:42 +02:00

forgejo CI forgejo CI

libmcfp

A library for parsing command line arguments and configuration files and making them available throughout a program.

There's a config file parser as well.

Before version 2 this library was header only, but that has changed, as of version 2 libmcfp can be built as either a C++ module library or a regular library. The default is to build and install both, but only when the compiler is new enough.

In your CMakeLists.txt file you can specify what version to use by either providing mcfp::mcfp or mcfp::mcfp-module as library to target_link_libraries.

Synopsis

// Example of using libmcfp

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

#if defined(MCFP_MODULE_MODE)
import mcfp;
#else
#include "mcfp/mcfp.hpp"
#endif

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

Installation

Use cmake to install libmcfp. You will also need a very recent compiler.

git clone https://forge.hekkelman.net/maarten/mcfp.git
cd mcfp
cmake -B build -G Ninja
cmake --build build
cmake --install build