cmdlime - is a C++17 header-only library for command line parsing with minimum of code and pain things to remember

Overview

cmdlime - is a C++17 header-only library for command line parsing with minimum of code and pain things to remember. See for yourself:

///examples/ex01.cpp
///
#include <cmdlime/config.h>
#include <iostream>

int main(int argc, char** argv)
{
    struct Cfg : public cmdlime::Config{
        ARG(zipCode, int);
        PARAM(name, std::string);
        FLAG(verbose);
    } cfg;

    auto reader = cmdlime::ConfigReader{cfg, "person-finder"};
    if (!reader.readCommandLine(argc, argv))
        return reader.exitCode();

    //At this point your config is ready to use
    std::cout << "Looking for person " << cfg.name << " in the region with zip code: " << cfg.zipCode;
    return 0;
}

The default configuration conforms to the GNU command line options convention, so this program can be launched like this:

[email protected]:~$ ./person-finder 684007 --name John --verbose
Looking for person John in the region with zip code: 684007

Please note, that in this example, --name is a parameter, --verbose is a flag, and 684007 is an argument, this naming is used in this document and within the cmdlime interface.

Table of Contents

Usage

Declaring the config structure

To use cmdlime you need to create a structure with fields corresponding to parameters, flags and arguments readed from the command line.
To do this subclass cmdlime::Config and declare fields with the following macros:

  • ARG(name, type) - creates type name; config field and registers it in the parser.
    Arguments are mapped to the config fields in the order of declaration. Arguments can't have default values and are always required to be specified in the command line.
  • ARGLIST(name, type) - creates std::vector name; config field and registers it in the parser.
    Config can have only one arguments list and elements are placed into it after all other config arguments are set, regardless of the order of declaration. The declaration form ARGLIST(name, type)(list-initialization) sets the default value of an argument list, which makes it optional, so it can be omitted from the command line without raising an error.
  • PARAM(name, type) - creates type name; config field and registers it in the parser.
    The declaration form PARAM(name, type)(default value) sets the default value of a parameter, which makes it optional, so it can be omitted from the command line without raising an error.
  • PARAMLIST(name, type) - creates std::vector name; config field and registers it in the parser.
    Parameter list can be filled by specifying it in the command line multiple times (--param-list val1 --param-list val2) or passing a comma separated value (--param-list val1,val2).
    The declaration form PARAMLIST(name, type)(list-initialization) sets the default value of a parameter list, which makes it optional, so it can be omitted from the command line without raising an error.
  • FLAG(name) - creates bool name; config field and registers it in the parser.
    Flags are always optional and have default value false
  • EXITFLAG(name) - creates bool name; config field and registers it in the parser. If at least one exit flag is set, no parsing errors are raised regardless of the command line's content and config fields other than exit flags are left in an unspecified state. It's usefull for flags like --help or --version when you're supposed to print some message and exit the program without checking the other fields.

Note: Types used for config fields must be default constructible and copyable.

Another note: You don't need to change your code style when declaring config fields - camelCase, snake_case and PascalCase names are supported and readed from the kebab-case named parameters in the command line.

Let's alter the config for the person-finder program by adding a required parameter surname and making the name parameter optional:

///examples/ex02.cpp
///
struct Cfg : public cmdlime::Config{
	ARG(zipCode, int);
    PARAM(surname, std::string);
    PARAM(name, std::string)();
    FLAG(verbose);
} cfg;

Now parameter --name can be skipped without raising an error:

[email protected]:~$ ./person-finder 684007 --surname Deer
Looking for person Deer in region with zip code: 684007

Using ConfigReader

ConfigReader - is a helper class hiding the error handling boilerplate and adding--help and --version flags processing to your config.
--help flag show detailed help message, that otherwise can be accessed through Config::usageInfoDetailed().
--version flag is enabled only if version info is set in the config with Config::setVersionInfo method.
Let's modify person-finder and see how it works.

///examples/ex03.cpp
///
...
    cfg.setVersionInfo("person-finder 1.0");
    auto reader = cmdlime::ConfigReader{cfg, "person-finder"};
    if (!reader.readCommandLine(argc, argv))
        return reader.exitCode();
...
[email protected]:~$ ./person-finder --version
person-finder 1.0
[email protected]:~$ ./person-finder --help
Usage: person-finder  --surname  [params] [flags] 
Arguments:
     (int)          
Parameters:
   -s, --surname      
   -n, --name         optional
Flags:
   -v, --verbose              
       --help                 show usage info and exit
       --version              show version info and exit

As mentioned before, ConfigReader is just a helper class, so if you like typing a lot, it's possible to implement the same program without using it:

///examples/ex04.cpp
///
#include <cmdlime/config.h>
#include <iostream>

int main(int argc, char** argv)
{
    struct Cfg : public cmdlime::Config{
        ARG(zipCode, int);
        PARAM(name, std::string);
        FLAG(verbose);
        EXITFLAG(help);
        EXITFLAG(version);        
    } cfg;
	
    try{
    	cfg.readCommandLine(argc, argv);
    }
    catch(const cmdlime::Error& e){
    	std::cerr << e.what();
        std::cout << cfg.usageInfo();
        return -1;
    }
    if (cfg.help){
    	std::cout << cfg.usageInfoDetailed();
        return 0;
    }
    if (cfg.version){
    	std::cout << "person-finder 1.0";
        return 0;
    }
    //At this point your config is ready to use
    std::cout << "Looking for person " << cfg.name << " in the region with zip code: " << cfg.zipCode;
    return 0;
}

Try to run it and...

Usage: person-finder  --name  [--verbose] [--help] [--version]
Flag's short name 'v' is already used.

you'll get this error. The thing is, the default command line format supports short names and our flags --verbose and --version ended up having the same short name -v. Read the next section to learn how to fix it.

Custom names

///examples/ex05.cpp
///
struct Cfg : public cmdlime::Config{
    ARG(zipCode, int);
    PARAM(name, std::string);
    FLAG(verbose);
    EXITFLAG(help)    << cmdlime::WithoutShortName{};
    EXITFLAG(version) << cmdlime::WithoutShortName{};
} cfg;

Here's the fixed config. Turning off the short name generation for flag --version resolves the name conflict. When you rely on ConfigReader for handling of --help and --version flags, it creates them without short names. At this point, we should do this as well, and all following examples will be based on our original version of person-finder program that uses ConfigReader.

You can use the following objects to customize names generation:
cmdlime::Name{"customName"} - overrides command line option's name.
cmdlime::ShortName{"customShortName"} - overrides command line option's short name.
cmdlime::WithoutShortName{} - removes command line option's short name.
cmdlime::ValueName{} - overrides parameter's value name in the usage info.

And it's time for another person-finder's rewrite:

///examples/ex06.cpp
///
struct Cfg : public cmdlime::Config{
    ARG(zipCode, int);
    PARAM(surname, std::string)  << cmdlime::ValueName{"A-Z..."};
    PARAM(name, std::string)()   << cmdlime::Name{"first-name"};
    FLAG(verbose);
} cfg;
[email protected]:~$ ./person-finder --help
Usage: person-finder  --surname  [params] [flags] 
Arguments:
     (int)             
Parameters:
   -s, --surname      
   -n, --first-name      optional
Flags:
   -v, --verbose                 
       --help                    show usage info and exit
       --version                 show version info and exit

Auto-generated usage info

cmdlime can generate help messages accessible with Config::usageInfo() and Config::usageInfoDetailed() methods. The former is the compact version that is supposed to be shown alongside error messages, the latter is the detailed version that is printed out when --help flag is set.

We can add more information to the detailed usage info by setting the parameters` descriptions:

///examples/ex07.cpp
///
struct Cfg : public cmdlime::Config{
    ARG(zipCode, int)              << "zip code of the searched region";
    PARAM(surname, std::string)    << "surname of the person to find"       << cmdlime::ValueName{"A-Z..."};
    PARAM(name, std::string)()     << "name of the person to find"          << cmdlime::Name{"first-name"};
    FLAG(verbose)                  << "adds more information to the output";
} cfg;
[email protected]:~$ ./person-finder --help
Usage: person-finder  --surname  [params] [flags] 
Arguments:
     (int)             zip code of the searched region
Parameters:
   -s, --surname      surname of the person to find
   -n, --first-name      name of the person to find
                                   (optional)
Flags:
   -v, --verbose                 adds more information to the output
       --help                    show usage info and exit
       --version                 show version info and exit

If you don't like auto-generated usage info message you can set your own with Config::setUsageInfo() and Config::setUsageInfoDetailed()

Supported formats

cmdlime supports several command line naming conventions and unlike many other parsers it strictly enforces them, so you can't mix usage of different formats together.

All formats support argument delimiter --, after encountering it, all command line options are treated as arguments, even if they start with hyphens.

GNU

All names are in kebab-case.
Parameters and flags prefix: --
Short names are supported. Short names prefix: -
Parameters usage: --parameter value, --parameter=value, -p value or -pvalue
Flags usage: --flag, -f
Flags in short form can be "glued" together: -abc or with one parameter: -fp value

This is the default command line format used by cmdlime. Subclass your config structure from cmdlime::Config or cmdlime::GNUConfig to use it.

///examples/ex08.cpp
///
#include <cmdlime/config.h>
struct Cfg : public cmdlime::Config{
    ARG(zipCode, int)              << "zip code of the searched region";
    PARAM(surname, std::string)    << "surname of the person to find";
    PARAM(name, std::string)()     << "name of the person to find";
    FLAG(verbose)                  << "adds more information to the output";
} cfg;
[email protected]:~$ ./person-finder --help
Usage: person-finder  --surname  [params] [flags] 
Arguments:
     (int)          zip code of the searched region
Parameters:
   -s, --surname      surname of the person to find
   -n, --name         name of the person to find
                                (optional)
Flags:
   -v, --verbose              adds more information to the output
       --help                 show usage info and exit
       --version              show version info and exit

POSIX

All names consist of single alphanumeric character.
Parameters and flags prefix: -
Short names aren't supported (the default names are already short enough).
Parameters usage: -p value or -pvalue
Flags usage: -f
Flags in short form can be "glued" together: -abc or with one parameter: -fp value

Parameters and flags must precede the arguments, besides that, this format is a subset of GNU format.

Subclass your config structure from cmdlime::POSIXConfig to use it.

///examples/ex09.cpp
///
#include <cmdlime/posixconfig.h>
struct Cfg : public cmdlime::POSIXConfig{
    ARG(zipCode, int)              << "zip code of the searched region";
    PARAM(surname, std::string)    << "surname of the person to find";
    PARAM(name, std::string)()     << "name of the person to find";
    FLAG(verbose)                  << "adds more information to the output" << cmdlime::Name{"V"};
} cfg;

Note: here's the clashing names error again, so we have to rename verbose config field flag from -v to -V

[email protected]:~$ ./person-finder -h
Usage: person-finder  -s  [params] [flags] 
Arguments:
     (int)     zip code of the searched region
Parameters:
   -s            surname of the person to find
   -n            name of the person to find
                           (optional)
Flags:
   -V                    adds more information to the output
   -h                    show usage info and exit
   -v                    show version info and exit

X11

All names are in lowercase.
Parameters and flags prefix: -
Short names aren't supported.
Parameters usage: -parameter value
Flags usage: -flag

Subclass your config structure from cmdlime::X11Config to use it.

///examples/ex10.cpp
///
#include <cmdlime/x11config.h>
struct Cfg : public cmdlime::X11Config{
    ARG(zipCode, int)              << "zip code of the searched region";
    PARAM(surname, std::string)    << "surname of the person to find";
    PARAM(name, std::string)()     << "name of the person to find";
    FLAG(verbose)                  << "adds more information to the output";
} cfg;
[email protected]:~$ ./person-finder -help
Usage: person-finder  -surname  [params] [flags] 
Arguments:
     (int)      zip code of the searched region
Parameters:
   -surname      surname of the person to find
   -name         name of the person to find
                           (optional)
Flags:
   -verbose              adds more information to the output
   -help                 show usage info and exit
   -version              show version info and exit

Simple format

This format is created for development purposes of cmdlime as it's the easiest one to parse, so cmdlime unit tests are probably the only software that use it.

All names are in camelCase.
Parameters prefix: -
Flags prefix: --
Short names aren't supported.
Parameters usage: -parameter=value
Flags usage: --flag

Subclass your config structure from cmdlime::SimpleConfig to use it.

///examples/ex11.cpp
///
#include <cmdlime/simpleconfig.h>
struct Cfg : public cmdlime::SimpleConfig{
    ARG(zipCode, int)              << "zip code of the searched region";
    PARAM(surname, std::string)    << "surname of the person to find";
    PARAM(name, std::string)()     << "name of the person to find";
    FLAG(verbose)                  << "adds more information to the output";
} cfg;
[email protected]:~$ ./person-finder --help
Usage: person-finder  -surname= [params] [flags] 
Arguments:
     (int)      zip code of the searched region
Parameters:
   -surname=     surname of the person to find
   -name=        name of the person to find
                           (optional)
Flags:
  --verbose              adds more information to the output
  --help                 show usage info and exit
  --version              show version info and exit

Using custom types

To use custom types in the config, it's necessary to overload std::stringstream operator<< and operator>>.
Let's add a coordinate parameter --coord to the person-finder program.

>(std::stringstream& stream, Coord& coord) { auto coordStr = std::string{}; stream >> coordStr; auto delimPos = coordStr.find('-'); if (delimPos == std::string::npos) throw cmdlime::Error{"Wrong coord format"}; coord.lat = std::stod(coordStr.substr(0, delimPos)); coord.lon = std::stod(coordStr.substr(delimPos + 1, coordStr.size() - delimPos - 1)); return stream; } int main(int argc, char** argv) { struct Cfg : public cmdlime::Config{ ARG(zipCode, int) << "zip code of the searched region"; PARAM(surname, std::string) << "surname of the person to find"; PARAM(name, std::string)() << "name of the person to find"; PARAM(coord, Coord) << "possible location"; FLAG(verbose) << "adds more information to the output"; } cfg; cfg.setVersionInfo("person-finder 1.0"); auto reader = cmdlime::ConfigReader{cfg, "person-finder"}; if (!reader.readCommandLine(argc, argv)) return reader.exitCode(); //At this point your config is ready to use std::cout << "Looking for person " << cfg.name << " " << cfg.surname << " in the region with zip code: " << cfg.zipCode << std::endl; std::cout << "Possible location:" << cfg.coord.lat << " " << cfg.coord.lon; return 0; } ">
///examples/ex12.cpp
///
#include <cmdlime/config.h>
#include <iostream>
#include <sstream>

struct Coord{
    double lat;
    double lon;
};
std::stringstream& operator<<(std::stringstream& stream, const Coord& coord)
{
    stream << coord.lat << "-" << coord.lon;
    return stream;
}
std::stringstream& operator>>(std::stringstream& stream, Coord& coord)
{
    auto coordStr = std::string{};
    stream >> coordStr;
    auto delimPos = coordStr.find('-');
    if (delimPos == std::string::npos)
        throw cmdlime::Error{"Wrong coord format"};
    coord.lat = std::stod(coordStr.substr(0, delimPos));
    coord.lon = std::stod(coordStr.substr(delimPos + 1, coordStr.size() - delimPos - 1));
    return stream;
}


int main(int argc, char** argv)
{
    struct Cfg : public cmdlime::Config{
        ARG(zipCode, int)              << "zip code of the searched region";
        PARAM(surname, std::string)    << "surname of the person to find";
        PARAM(name, std::string)()     << "name of the person to find";
        PARAM(coord, Coord)            << "possible location";
        FLAG(verbose)                  << "adds more information to the output";
    } cfg;


    cfg.setVersionInfo("person-finder 1.0");
    auto reader = cmdlime::ConfigReader{cfg, "person-finder"};
    if (!reader.readCommandLine(argc, argv))
        return reader.exitCode();

    //At this point your config is ready to use
    std::cout << "Looking for person " << cfg.name << " " << cfg.surname << " in the region with zip code: " << cfg.zipCode << std::endl;
    std::cout << "Possible location:" << cfg.coord.lat << " " << cfg.coord.lon;
    return 0;
}
[email protected]:~$ ./person-finder 684007 --surname Deer --coord 53.0-157.25
Looking for person  Deer in the region with zip code: 684007
Possible location:53 157.25

Installation

For the system-wide installation use these commands:

git clone https://github.com/kamchatka-volcano/cmdlime.git
cd cmdlime && mkdir build && cd build
cmake ..
make install

Or you can simply place cmdlime directory in your project and add cmdlime/include to the include paths.

Running tests

cd cmdlime/build
cmake .. -DENABLE_TESTS=ON
make
ctest

License

cmdlime is licensed under the MS-PL license
The bundled part of the GSL library is licensed under the MIT license

Comments
  • Building examples/ex01 fails

    Building examples/ex01 fails

    I tried to build the examples, but I got stuck at the first one.

    The current build script does not build them, but I added the following to CMakeLists.txt:

    add_executable(ex01 examples/ex01.cpp)
    target_link_libraries(ex01 PRIVATE cmdlime)
    

    Trying to build with g++ 9.3.0 on Ubuntu 20.04.2 LTS gives the following errors:

    In file included from /src/cmdlime/include/cmdlime/configreader.h:5,
                     from /src/cmdlime/include/cmdlime/gnuconfig.h:3,
                     from /src/cmdlime/include/cmdlime/config.h:2,
                     from /src/cmdlime/examples/ex01.cpp:1:
    /src/cmdlime/examples/ex01.cpp: In function ‘int main(int, char**)’:
    /src/cmdlime/include/cmdlime/detail/configmacro.h:12:105: error: invalid use of ‘this’ in non-member function
       12 | #define ARG(name, type) type name = cmdlime::detail::ArgCreator<type, std::remove_reference_t<decltype(*this)>>{*this, #name, #type, [this]()->type&{return name;}}
          |                                                                                                         ^~~~
    /src/cmdlime/examples/ex01.cpp:7:9: note: in expansion of macro ‘ARG’
        7 |         ARG(zipCode, int);
          |         ^~~
    /src/cmdlime/include/cmdlime/detail/configmacro.h:12:95: error: template argument 1 is invalid
       12 | #define ARG(name, type) type name = cmdlime::detail::ArgCreator<type, std::remove_reference_t<decltype(*this)>>{*this, #name, #type, [this]()->type&{return name;}}
          |                                                                                               ^~~~~~~~
    /src/cmdlime/examples/ex01.cpp:7:9: note: in expansion of macro ‘ARG’
        7 |         ARG(zipCode, int);
          |         ^~~
    In file included from /src/cmdlime/include/cmdlime/configreader.h:5,
                     from /src/cmdlime/include/cmdlime/gnuconfig.h:3,
                     from /src/cmdlime/include/cmdlime/config.h:2,
                     from /src/cmdlime/examples/ex01.cpp:1:
    /src/cmdlime/include/cmdlime/detail/configmacro.h:8:109: error: invalid use of ‘this’ in non-member function
        8 | #define PARAM(name, type) type name = cmdlime::detail::ParamCreator<type, std::remove_reference_t<decltype(*this)>>{*this, #name, #type, [this]()->type&{return name;}}
          |                                                                                                             ^~~~
    /src/cmdlime/examples/ex01.cpp:8:9: note: in expansion of macro ‘PARAM’
        8 |         PARAM(name, std::string);
          |         ^~~~~
    /src/cmdlime/include/cmdlime/detail/configmacro.h:8:99: error: template argument 1 is invalid
        8 | #define PARAM(name, type) type name = cmdlime::detail::ParamCreator<type, std::remove_reference_t<decltype(*this)>>{*this, #name, #type, [this]()->type&{return name;}}
          |                                                                                                   ^~~~~~~~
    /src/cmdlime/examples/ex01.cpp:8:9: note: in expansion of macro ‘PARAM’
        8 |         PARAM(name, std::string);
          |         ^~~~~
    In file included from /src/cmdlime/include/cmdlime/configreader.h:5,
                     from /src/cmdlime/include/cmdlime/gnuconfig.h:3,
                     from /src/cmdlime/include/cmdlime/config.h:2,
                     from /src/cmdlime/examples/ex01.cpp:1:
    /src/cmdlime/include/cmdlime/detail/configmacro.h:12:110: error: template argument 2 is invalid
       12 | #define ARG(name, type) type name = cmdlime::detail::ArgCreator<type, std::remove_reference_t<decltype(*this)>>{*this, #name, #type, [this]()->type&{return name;}}
          |                                                                                                              ^~
    /src/cmdlime/examples/ex01.cpp:7:9: note: in expansion of macro ‘ARG’
        7 |         ARG(zipCode, int);
          |         ^~~
    In file included from /src/cmdlime/include/cmdlime/configreader.h:5,
                     from /src/cmdlime/include/cmdlime/gnuconfig.h:3,
                     from /src/cmdlime/include/cmdlime/config.h:2,
                     from /src/cmdlime/examples/ex01.cpp:1:
    /src/cmdlime/include/cmdlime/detail/configmacro.h:8:114: error: template argument 2 is invalid
        8 | #define PARAM(name, type) type name = cmdlime::detail::ParamCreator<type, std::remove_reference_t<decltype(*this)>>{*this, #name, #type, [this]()->type&{return name;}}
          |                                                                                                                  ^~
    /src/cmdlime/examples/ex01.cpp:8:9: note: in expansion of macro ‘PARAM’
        8 |         PARAM(name, std::string);
          |         ^~~~~
    In file included from /src/cmdlime/include/cmdlime/detail/gnuformat.h:2,
                     from /src/cmdlime/include/cmdlime/gnuconfig.h:6,
                     from /src/cmdlime/include/cmdlime/config.h:2,
                     from /src/cmdlime/examples/ex01.cpp:1:
    /src/cmdlime/include/cmdlime/detail/parser.h: In lambda function:
    /src/cmdlime/include/cmdlime/detail/parser.h:68:13: warning: control reaches end of non-void function [-Wreturn-type]
       68 |             [&](auto param){
          |             ^~~~~~~~~~~~~~~~
       69 |                 switch(mode){
          |                 ~~~~~~~~~~~~~
       70 |                 case FindMode::Name:
          |                 ~~~~~~~~~~~~~~~~~~~~
       71 |                     return param->info().name() == name;
          |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       72 |                 case FindMode::ShortName:
          |                 ~~~~~~~~~~~~~~~~~~~~~~~~~
       73 |                     return param->info().shortName() == name;
          |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       74 |                 case FindMode::All:
          |                 ~~~~~~~~~~~~~~~~~~~
       75 |                     return param->info().name() == name || param->info().shortName() == name;
          |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       76 |                 }
          |                 ~
       77 |             });
          |             ~
    /src/cmdlime/include/cmdlime/detail/parser.h: In lambda function:
    /src/cmdlime/include/cmdlime/detail/parser.h:86:13: warning: control reaches end of non-void function [-Wreturn-type]
       86 |             [&](auto paramList){
          |             ^~~~~~~~~~~~~~~~~~~~
       87 |                 switch(mode){
          |                 ~~~~~~~~~~~~~
       88 |                 case FindMode::Name:
          |                 ~~~~~~~~~~~~~~~~~~~~
       89 |                     return paramList->info().name() == name;
          |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       90 |                 case FindMode::ShortName:
          |                 ~~~~~~~~~~~~~~~~~~~~~~~~~
       91 |                     return paramList->info().shortName() == name;
          |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       92 |                 case FindMode::All:
          |                 ~~~~~~~~~~~~~~~~~~~
       93 |                     return paramList->info().name() == name || paramList->info().shortName() == name;
          |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       94 |                 }
          |                 ~
       95 |             });
          |             ~
    In file included from /src/cmdlime/include/cmdlime/detail/gnuformat.h:2,
                     from /src/cmdlime/include/cmdlime/gnuconfig.h:6,
                     from /src/cmdlime/include/cmdlime/config.h:2,
                     from /src/cmdlime/examples/ex01.cpp:1:
    /src/cmdlime/include/cmdlime/detail/parser.h: In lambda function:
    /src/cmdlime/include/cmdlime/detail/parser.h:123:13: warning: control reaches end of non-void function [-Wreturn-type]
      123 |             [&](auto flag){
          |             ^~~~~~~~~~~~~~~
      124 |             switch(mode){
          |             ~~~~~~~~~~~~~
      125 |                 case FindMode::Name:
          |                 ~~~~~~~~~~~~~~~~~~~~
      126 |                     return flag->info().name() == name;
          |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      127 |                 case FindMode::ShortName:
          |                 ~~~~~~~~~~~~~~~~~~~~~~~~~
      128 |                     return flag->info().shortName() == name;
          |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      129 |                 case FindMode::All:
          |                 ~~~~~~~~~~~~~~~~~~~
      130 |                     return flag->info().name() == name || flag->info().shortName() == name;
          |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      131 |                 }
          |                 ~
      132 |             });
          |             ~
    

    Building with clang++ 10 on the same system seems to work. Building with MSVC (Visual Studio 2019) on Windows gives just a few warnings, so maybe I'm missing something in the g++ case:

    c:\src\cmdlime\include\cmdlime\detail\parser.h(77) : warning C4715: '<lambda_d6bf33724169dfe5569812608f3db3ac>::operator()<gsl::not_null<cmdlime::detail::IParam *> >': not all control paths return a value
    c:\src\cmdlime\include\cmdlime\detail\parser.h(132) : warning C4715: '<lambda_027da08a6184456737a8fd32a83f0ee3>::operator()<gsl::not_null<cmdlime::detail::IFlag *> >': not all control paths return a value
    c:\src\cmdlime\include\cmdlime\detail\parser.h(95) : warning C4715: '<lambda_1cce2af26feeeaaa5a7e6c0694b00dac>::operator()<gsl::not_null<cmdlime::detail::IParamList *> >': not all control paths return a value
    

    I haven't tried the other examples yet.

    opened by a-cristi 4
  • Error in string_utils.h

    Error in string_utils.h

    std::string_view has no constructor from iterator and size.

    Error in line 158 of string_utils.h

    Fix: return std::string_view{ str.data(), res };

    Error in line 166 of string_utils.h

    Fix return std::string_view{ str.data() + static_cast(res + val.size()), str.size() - (res + val.size()) };

    opened by tripleslash 2
  • Build fails with Clang flags (-Werror and -Wshorten-64-to-32)

    Build fails with Clang flags (-Werror and -Wshorten-64-to-32)

    Thank you for the great CLI parser! When I include cmdlime in my project, the build fails. The flags that I have added for my project are:

    • -Wall
    • -Wcast-align
    • -Wconversion
    • -Wpedantic
    • -Wextra
    • -Werror
    • -Wnon-virtual-dtor
    • -Woverloaded-virtual
    • -Wsign-conversion

    The error is:

    In file included from main.cpp:2:
    In file included from _deps/cmdlime-src/include/cmdlime/config.h:2:
    In file included from _deps/cmdlime-src/include/cmdlime/gnuconfig.h:3:
    In file included from _deps/cmdlime-src/include/cmdlime/configreader.h:4:
    In file included from _deps/cmdlime-src/include/cmdlime/detail/config.h:3:
    _deps/cmdlime-src/include/cmdlime/detail/usageinfocreator.h:321:16: error: implicit conversion loses integer precision: 'unsigned long' to 'int' [-Werror,-Wshorten-64-to-32]
            return size;
            ~~~~~~ ^~~~
    _deps/cmdlime-src/include/cmdlime/detail/usageinfocreator.h:101:26: note: in instantiation of member function 'cmdlime::detail::UsageInfoCreator<cmdlime::detail::FormatType::GNU>::maxOptionNameSize' requested here
        , maxOptionNameSize_(maxOptionNameSize() + outputSettings.columnsSpacing)
                             ^
    _deps/cmdlime-src/include/cmdlime/detail/config.h:69:16: note: in instantiation of member function 'cmdlime::detail::UsageInfoCreator<cmdlime::detail::FormatType::GNU>::UsageInfoCreator' requested here
            return UsageInfoCreator<formatType>{name, UsageInfoFormat{}, params, paramLists, flags, args, argList_.get()}.create();
                   ^
    main.cpp:18:22: note: in instantiation of member function 'cmdlime::detail::Config<cmdlime::detail::FormatType::GNU>::usageInfo' requested here
        std::cout << cfg.usageInfo("project");
    

    Here's my main.cpp for reference:

    #include <project/version.h>
    #include <cmdlime/config.h>
    
    #include <iostream>
    
    auto main(int argc, char** argv) -> int {
      struct Cfg : public cmdlime::Config {
        PARAM(t1, std::string);
        PARAM(t2, int);
        EXITFLAG(help);
        EXITFLAG(version);
      } cfg;
    
      try {
        cfg.readCommandLine(argc, argv);
      } catch (const cmdlime::Error& e) {
        std::cerr << e.what();
        std::cout << cfg.usageInfo("project");
        return -1;
      }
    
      if (cfg.help) {
        std::cout << cfg.usageInfoDetailed("project");
        return 0;
      }
    
      if (cfg.version) {
        std::cout << PROJECT_VERSION;
        return 0;
      }
    
      return 0;
    }
    
    

    This was tested on macOS 11.3 (Intel architecture) with AppleClang 12.0.5 and cmake 3.20.2.

    opened by btorres510 1
  • v2.0.0 update

    v2.0.0 update

    -now configs are created and read with CommandLineReader class, all config structure fields registration data is placed inside CommandLineReader object, config structures are now aggregates and only contain a single pointer to CommandLineReader instance. -optional config fields have to use cmdlime::optional instead of std::optional -added the ability to use any sequence containers for parameter and argument lists; -bundled parts of sfun and gsl library replaced with fetched versions, disabled system wide installation of nameof library

    opened by kamchatka-volcano 0
  • WithoutShortName{} only works with FLAGs, not with PARAMs

    WithoutShortName{} only works with FLAGs, not with PARAMs

    If you modify ex05.cpp, line 8 to be

    PARAM(name, std::string) << cmdlime::WithoutShortName{}; You get this error with Clang:

    $› clang++ -I ../include/ -std=c++17 ex05.cpp 
    In file included from ex05.cpp:1:
    In file included from ../include/cmdlime/config.h:2:
    In file included from ../include/cmdlime/gnuconfig.h:3:
    In file included from ../include/cmdlime/configreader.h:5:
    In file included from ../include/cmdlime/detail/configmacro.h:2:
    ../include/cmdlime/detail/param.h:126:39: error: 'format' is a private member of 'cmdlime::detail::Config<cmdlime::detail::FormatType::GNU>'
            static_assert(Format<TConfig::format>::shortNamesEnabled, "Current command line format doesn't support short names");
                                          ^
    ex05.cpp:8:37: note: in instantiation of member function 'cmdlime::detail::ParamCreator<std::__1::basic_string<char>, Cfg>::operator<<'
          requested here
            PARAM(name, std::string)    << cmdlime::WithoutShortName{};
                                        ^
    ../include/cmdlime/detail/config.h:142:33: note: declared private here
        constexpr static FormatType format = formatType;
                                    ^
    1 error generated.
    
    $>
    
    

    Changing the format static member in include/cmdlime/detail/config.h:142:33 from private to public fixes the issue.

    bug 
    opened by MtnViewJohn 0
  • 2do: add support for subcommands

    2do: add support for subcommands

    Possible interface:

    struct CommandCfg: public cmdlime::Config{
        //FLAG, PARAM, ARG, etc
    };
    
    struct Cfg : public cmdlime::Config {
        //FLAG, PARAM, ARG, etc
        COMMAND(commandName, CommandCfg); //creates std::optional<CommandCfg> commandName;
    };
    
    enhancement 
    opened by kamchatka-volcano 0
  • Still no support for MSVC

    Still no support for MSVC

    image

    When trying to register a simple configuration like this:

    struct IConfiguration
    {
        virtual ~IConfiguration() noexcept = default;
    };
    
    struct ProgramConfig : public cmdlime::Config, public IConfiguration
    {
        CMDLIME_PARAM(...);
    };
    
    opened by tripleslash 1
Releases(v2.1.1)
  • v2.1.1(Dec 17, 2022)

    • Updated the nameof library, now cmdlime can be used with MSVC compiler when functionality provided by nameof is enabled (C++20 is required)
    Source code(tar.gz)
    Source code(zip)
  • v2.1.0(Oct 24, 2022)

    • Fixed issues on MSVC compiler
    • Changed pragma once to macro include guards
    • Updated CMake config to use seal_lake library
    • Added GitHub Actions configuration for CI pipeline
    Source code(tar.gz)
    Source code(zip)
  • v2.0.1(Aug 23, 2022)

  • v2.0.0(Aug 18, 2022)

    • BREAKING Now configs are created and read with a CommandLineReader object. So instead of
    auto cfg = MyCfg{};
    cfg.readCommandLine(argc, argv);
    

    you need to write it like that:

    auto reader = cmdlime::CommandLineReader{};
    auto cfg = reader.read<MyCfg>(argc, argv);
    

    This change was required for placing all config structure registration data outside the config object. Now cmdlime::Config contains only a single pointer, besides the user provided data, so by default the config object is a copyable aggregate that can be used however you want. ConfigReader was removed. Its functionally providing automatic error handling and --help and --version flags handling support was moved to CommandLineReader::exec() method. Read about it here

    • BREAKING Now PARAMLIST and ARGLIST macros are required to provide a container type, it can be any sequence container, supporting emplace_back operation(within STL it's vector, deque, and list). So instead of
    struct MyCfg : cmdlime::Config{
       PARAMLIST(pList, int);
       ARGLIST(aList, std::string);
    }
    

    you need to write it like that:

    struct MyCfg : cmdlime::Config{
       PARAMLIST(pList, std::vector<int>);
       ARGLIST(aList, std::list<string>);
    }
    

    Registration without macros can also use any dynamic sequence container from now on:

    struct MyCfg : cmdlime::Config{
       std::deque<int> pList = paramList<&MyCfg::pList>();
       std::list<string> aList = argList<&MyCfg::aList>();
    }
    
    • BREAKING Now optional config fields have to use cmdlime::optional instead of std::optional. cmdlime::optional - is a std::optional-like wrapper with similar interface.
    • bundled parts of sfun and gsl library were replaced with their fetched versions, system wide installation of nameof library was disabled.
    Source code(tar.gz)
    Source code(zip)
  • v1.1.0(Jul 14, 2022)

    • Fixed the issue #19
    • Renamed CMakeLists variable USE_NAMEOF to CMDLIME_USE_NAMEOF
    • Added cmdlime/shortmacros.h header containing registration macros without prefix CMDLIME_ (use them if there's no possible macros name conflicts)
    Source code(tar.gz)
    Source code(zip)
  • v1.0.1(Apr 17, 2022)

  • v1.0.0(Feb 13, 2022)

    • BREAKING To support user defined types, now it's required to provide specialization of StringConverter class, instead of overloads of << and >> operators. Read about it here
      While technically, it's not a breaking change (as providing << and >> overloads will work with default StringConverter implementation) it's recommended to update your code.
    • BREAKING Signatures of usageInfo() and usageInfoDetailed() were changed - program name and output format structure are now set to config with setProgramName and setUsageInfoFormat methods.
    • Added validation of command line options. Read about it here.
    • Added macros free registration of command line options. Read about it here.
    • Vendored sfun and GSL libraries, now the only external dependency is the nameof library which is optional and disabled by default. (nameof is required for concise macros free registration).
    Source code(tar.gz)
    Source code(zip)
  • v0.10.0(Jan 23, 2022)

    • Added package configs to CMakeLists.txt, now installed library can be found by CMake with find_package() command.
    • Fixed a CMake error, now there is no conflict when parent project also uses the sfun library.
    Source code(tar.gz)
    Source code(zip)
  • v0.9.0(Dec 21, 2021)

Owner
null
easy to use, powerful & expressive command line argument parsing for modern C++ / single header / usage & doc generation

clipp - command line interfaces for modern C++ Easy to use, powerful and expressive command line argument handling for C++11/14/17 contained in a sing

André Müller 977 Dec 29, 2022
Command-line flag parsing in C

flag.h Inspired by Go's flag module: https://pkg.go.dev/flag WARNING! The design of the library is not finished and may be a subject to change. Quick

Tsoding 41 Nov 10, 2022
led is a line-oriented text editor in command line

led is a line-oriented text editor in command line. This editor is similar to the standard program on unix systems - GNU ed. But i'm not going to make an exact clone of that program, it's just a pet project.

Artem Mironov 16 Dec 27, 2022
CLIp is a clipboard emulator for a command line interface written in 100% standard C only. Pipe to it to copy, pipe from it to paste.

CLIp v2 About CLIp is a powerful yet easy to use and minimal clipboard manager for a command line environment, with no dependencies or bloat. Usage Sy

A.P. Jo. 12 Sep 18, 2021
A command parsing library

LampOpt操作文档 概述 LampOpt是一个基于C++的控制台命令解析库,优点是体型小、适应全平台、方便易用。 引用 可选择在IDE中直接在引用目录中添加odt.h,或直接与需编译文件放在同一目录下,并引用: #include "odt.h" 使用 odt.h头文件内定义了一个名为LampOp

东灯 1 Aug 21, 2022
A simple command line application in order to create new Code workspaces.

mkcws Summary A simple command line application in order to create new Code workspaces. License This project's license is GPL 2. The whole license tex

Kevin Matthes 0 Apr 1, 2022
A command-line tool to generate Linux manual pages from C source code.

mangen A command-line tool to generate Linux manual pages from C source code. Description mangen is, as said above, a program to generate Linux manual

null 2 Nov 15, 2021
nicegraf-shaderc is a command-line tool that transforms HLSL code into shaders for various graphics APIs.

User Manual Table of Contents Introduction Project Status Obtaining the Source Code and Building Running Defining Techniques Generated Header File Pip

nicebyte 94 Dec 8, 2022
C++ Library for pulling system and hardware information, without hitting the command line.

infoware C++ Library for pulling system and hardware information, without hitting the command line. Requirements No non-built-in ones by default. Some

The Phantom Derpstorm 323 Dec 23, 2022
A library for interactive command line interfaces in modern C++

cli A cross-platform header only C++14 library for interactive command line interfaces (Cisco style) Features Header only Cross-platform (linux and wi

Daniele Pallastrelli 888 Dec 31, 2022
CLI11 is a command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface.

CLI11: Command line parser for C++11 What's new • Documentation • API Reference CLI11 is a command line parser for C++11 and beyond that provides a ri

null 2.4k Dec 30, 2022
A simple to use, composable, command line parser for C++ 11 and beyond

Clara v1.1.5 !! This repository is unmaintained. Go here for a fork that is somewhat maintained. !! A simple to use, composable, command line parser f

Catch Org 648 Dec 27, 2022
A simple to use, composable, command line parser for C++ 11 and beyond

Lyra A simple to use, composing, header only, command line arguments parser for C++ 11 and beyond. Obtain License Standards Stats Tests License Distri

Build Frameworks Group 388 Dec 22, 2022
Rizin - UNIX-like reverse engineering framework and command-line toolset.

Rizin - UNIX-like reverse engineering framework and command-line toolset.

Rizin Organization 1.7k Dec 30, 2022
Simple command line utilities for extracting data from Fallout 4 and 76 files

fo76utils Simple command line utilities for extracting data from Fallout 4 and 76 files. baunpack - list the contents of, or extract from .BA2 archive

null 15 Dec 6, 2022
A command line tool for numerically computing Out-of-time-ordered correlations for N=4 supersymmetric Yang-Mills theory and Beta deformed N=4 SYM.

A command line tool to compute OTOC for N=4 supersymmetric Yang–Mills theory This is a command line tool to numerically compute Out-of-time-ordered co

Gaoli Chen 1 Oct 16, 2021
EAMain provides a multi-platform entry point used for platforms that don't support console output, return codes and command-line arguments.

EAMain provides a multi-platform entry point used for platforms that don't support console output, return codes and command-line arguments.

Electronic Arts 34 Oct 1, 2022
Lightweight C++ command line option parser

Release versions Note that master is generally a work in progress, and you probably want to use a tagged release version. Version 3 breaking changes I

null 3.3k Dec 30, 2022
pbr2gltf2 is a command line tool for converting PBR images to a glTF 2.0 material.

pbr2gltf2 is a command line tool for converting PBR images to a glTF 2.0 material. The tool is detecting depending on the filename, which PBR information is stored. It swizzles the images and does reassign the channels to a glTF 2.0 image. The tool stores the images plus a minimal, valid glTF 2.0 file containing the required material, textures and images.

UX3D GmbH 23 Jul 31, 2022