An open API service indexing awesome lists of open source software.

https://github.com/pboettch/cxx_argp

Modern C++, header-only command line argument parser interface based on argp
https://github.com/pboettch/cxx_argp

argument-parser cpp-library cpp11

Last synced: 9 months ago
JSON representation

Modern C++, header-only command line argument parser interface based on argp

Awesome Lists containing this project

README

          

# Modern C++ argument parser based on ARGP

## Description

This project provides a header-only, C++ (C++11 and later)
argument parser library manufactured around GLIBC's ARGP-library.

Its goal is to stick to a maximum to the original concepts and features of ARGP
in regards to argument-parsing. However, it adds some more advanced features,
especially the **association of variables, check-functions and their
option-arguments** - represented by the argp-struct.

It's licensed under the term of the LGPL3 (as is argp inside the GLIBC).
As a header-only library is has the same license-requirements as stated by
[the Eigen Project](http://eigen.tuxfamily.org/index.php?title=Licensing_FAQ&oldid=1117#So_what_does_the_LGPL_require_me_to_do.3F).

## How to use it

Here's how this library can be used:

```C++
#include
#include

int main(int argc, char *argv[])
{
// initializers are default values for omitted options
std::string host = "127.0.0.1";

// create parser object,
cxx_argp::parser parser;

// add option and associate to simple variables, the class creates a
// conversion function (if the type is supported) which does a basic check
// and fills the variable
// Note: the first argument is the real 'struct argp_option'
parser.add_option({nullptr, 'h', "host-address", 0, "IP address of host"},
host);

if (parser.parse(argc, argv)) {
std::cerr << "parsing OK\n";
} else {
std::cerr << "there was an error - exiting\n";
return 1;
}

std::cerr << "hostname " << host << "\n";

// here, do more checks on the values passed to the program

return 0;
}
```

Running this program (called readme, see the test-folder) without arguments,
`hostname` keeps it default value

```bash
$ ./readme
parsing OK
hostname 127.0.0.1
```

Running it with `-h google.com`, `hostname` changes:

```bash
$ ./readme -h google.com
parsing OK
hostname google.com
```

Omitting the argument to the '-h' option, errors out:

```bash
$ ./readme -h
./readme: option requires an argument -- 'h'
Try `readme --help' or `readme --usage' for more information.
```

Printing the help, generated by argp: (note that the program exists
before returning to main())

```bash
Usage: readme [OPTION...]

-h host-address IP address of host
-?, --help Give this help list
--usage Give a short usage message
```

## Adding options

The method `add_option()` adds an option to the parser-object. The first argment is
a value of `struct argp_option`, the second argument is either a variable,
which is used by reference, or a lamdba-function, `std::function` or `std::bind`
(of prototype `bool(const char *)`.

This second argument is used during argument parsing, if it was variable the option's
argument is tried to be converted to the variable (based on its type), if this is not
possible, an error is produced and argp will bail out.

If the second argument is a function, this function is called with the argument value
(a `const char *`) for further processing. This function returns `true` if the
argument is accepted, otherwise `false`.

## Argument conversion

### Basic types

The parser-library internally provides some conversion-functions for some variable-type,
which will make parsing fail if conversion has not worked, e.g. using a string
on a float-type variable.

Floating point, std::strings and integer argument conversion functions are built in,

```C++
float single = 1.1f;
double doublef = 2.2;
uint16_t port = 502;
int16_t sint = 0;
std::string host = "127.0.0.1";

cxx_argp::parser parser;

parser.add_option({"float", 's', "single", 0, "floating-point test"}, single);
parser.add_option({"double", 'd', "double", 0, "floating-point test"}, doublef);
parser.add_option({"port", 'p', "port", 0, "TCP port the server will listen on"}, port);
parser.add_option({"host", 'h', "host-address", 0, "IP address of host"}, host);
```

### Boolean and switches

`bool`-variable-based options are consider as 'switches', i.e. the option is expected
to not have an argument and if the option is present, the associated bool-variable is
set to true.

```C++
bool enable = false;

parser.add_option({"enable", 'e', nullptr, 0, "enable"}, enable);
```

### File streams and file names

The built-in conversion functions for file-streams is taking the argument as
a filename and tries to open the file. If this does not work, the argument is considered
as an error.

```C++
std::ifstream file;

parser.add_option({"file", 'f', "filename", 0, "a file"}, file);
```

Sometimes the filename and a stream is required. A paired type can then be used:

```C++
std::pair file_and_name;

parser.add_option({"file", 'f', "filename", 0, "a file"}, file_and_name);
```

This works the same way as for simple streams, except that the open file is the `first`-part of the pair
and the `second`-part contains the filename as a `std::string`.

To just get a filename without any file-opening, `std::string` can be used.

### Comma-separated list of integer

A more complex conversion function is built in, this converts are comma-separated list of integers
into a `std::vector<>`:

```C++
std::vector vec;

parser_.add_option({"vector", 'V', "list", 0, "list of ints"}, vec);
```

An argument for such a type can be given as `1,12,3`, resulting the version containing 1, 12 and 3.

### Custom argument converter

Custom argument converters can be implemented by passing a function as second argument to
`add_option()`. The user has thus complete control of what to do with the raw argument `const char *`.

The prototype of the function to be given is `bool(const char *)`.

The user has to return `true` if the argument is acceptable for this option, or `false` if not.

There are no limit in regards what can be done using this feature:

#### Lambdas

Toggle a boolean:

```C++
bool enable = false;

parser.add_option({"enable", 'e', nullptr, 0, "enable"},
[&enable] (const char *) { enable = !enable; return true; } );
```

A verbosity-level indicator:

```C++
int verbose = 0;

parser.add_option({nullptr, 'v', nullptr, 0, "verbosity level increase"},
[&verbose] (const char *) { verbose++; return true; } );
```

#### `std::bind`

A useful feature for when using `std::bind` could be to fill in a map-like
object (think of JSON as a more complex example than a `std::map`).

```C++
std::map cfg;

auto to_map = [&cfg] (const char *arg, const std::string &key) {
map[key] = arg;
return true;
};

parser.add_option({nullptr, 'a', nullptr, 0, "value A"},
std::bind(to_map, std::placeholders::_1, "value1"));
parser.add_option({nullptr, 'b', nullptr, 0, "value B"},
std::bind(to_map, std::placeholders::_1, "value2"));
parser.add_option({nullptr, 'c', nullptr, 0, "value C"},
std::bind(to_map, std::placeholders::_1, "value3"));
```

## Contributing

Do not hesite to ask questions and issue pull-requests here on GitHub.
Every help is more than welcome.