C++ API

To use the C++ API, include the CLIParser.hpp header. This will include the entire library, except for the C API.

Setting up and configuring the parser

The Parser class is placed under the UCLI namespace. First, create an instance and initialise it like this:

UCLI::Parser parser{}

You can also call the init member function to initialise it.

Unknown arguments

You can provide a callback, that will be called when an unknown argument is found. To do this, call the setUnknownArgumentCallback function like this:

parser.setUnknownArgumentCallback([](const char* name, void* data) -> void {
    std::cout << "Unknown argument: "<< name << std::endl;
}, nullptr);

The name parameter is the name of the argument, while the data argument is a void*, that's passed to the function via the last argument of setUnknownArgumentCallback.

Changing the delimiter

In most cases, the delimiter for arguments is -, however, you can change it by calling the setDelimiter function like this:

parser.setDelimiter('/');

Setting to Windows mode

Some applications use Windows style arguments, which use / as the delimiter. Windows mode doesn't support bundling of short arguments like this -abCDef, however. To use Windows mode call the setArgumentStyleWindows function and set the delimiter to /:

parser.setDelimiter('/');
parser.setArgumentStyleWindows(true);

Boolean flipping

By default, all boolean arguments are set to true, when the argument is encountered, you can change this behaviour to flip the boolean instead, using the setBooleanFlipping function, like this:

parser.setBooleanFlipping(true);

Providing data to the parser

Boolean flag

To add a boolean flag to the parser, create an array of type UCLI_Parser_BooleanFlag, then fill the array with your data. The struct looks like this:

struct UCLI_Parser_BooleanFlag
{
    const char* longType;
    const char* shortType;
    bool* flag;
};
  1. longType - The long name of the argument i.e. --{longType}
  2. shortType - The short name of the argument i.e. -{shortType}.
  3. flag - A boolean pointer that points to the boolean which will be set when this argument is encountered. Depending on the bool flipping setting, it may flip the bool, which might introduce strange behaviour, if the same argument is encountered 2 times in a command

Using this code, we can create a couple of boolean arguments:

bool a = false;
bool b = false;
bool c = false;

UCLI_Parser_BooleanFlag booleanFlags[] = 
{
    {
        .longType = "letter-c",
        .shortType = "c",
        .flag = &c
    },
    {
        .longType = "letter-b",
        .shortType = "b",
        .flag = &b
    },
    {
        .longType = "letter-a",
        .shortType = "a",
        .flag = &a
    },
};

Scroll down to the "Parsing" section, to learn how you can pass this to the parser.

Boolean flag with function

You can also enable boolean flags with a function. These flags should be stored in an array of type UCLI_Parser_BooleanFlagWithFunc, like this:

UCLI_Parser_BooleanFlagWithFunc flags[] =
{
    {
        .longType = "test6",
        .shortType = "",
        .func = t,
    },
    {
        .longType = "test5",
        .shortType = "test5",
        .func = t,
    },
    {
        .longType = "test6",
        .shortType = "test6",
        .func = t,
    },
};

where the function t looks something like this:

std::map<std::string, bool> map;

void t(UCLI_Parser_BooleanFlagWithFunc* arg)
{
    auto& tmp = map[arg->shortType];
    tmp = !tmp;
}

The UCLI_Parser_BooleanFlagWithFunc struct looks like this:

struct UCLI_Parser_BooleanFlagWithFunc
{
    const char* longType;
    const char* shortType;
    void* additionalData;
    void(*func)(struct UCLI_Parser_BooleanFlagWithFunc*);
};

The first 2 arguments are the regular short and long names. additionalData is a void* pointing to additional data you want your function to have access to(this can be null).

Finally, the func function pointer is the function that's going to be called and has the following signature void(UCLI_Parser_BooleanFlagWithFunc*)

Pair

A pair is an argument like this: --this=SomeDataHere

The parser can also parse pairs like this. To do this, simply create an array of type UCLI_Parser_Pair and initialise it like this:

UCLI_Parser_Pair pairs[2] = 
{
    {
        .longType = "arg3",
    },
    {
        .longType = "arg4",
    }
};

The UCLI_Parser_Pair struct looks like this:

struct UCLI_Parser_Pair
{
    const char* longType;

    // This is internal and should always be set to false
    bool InternalbFound;
    // Do not touch this, we will copy the data into here
    const char* data;
};

You should, however, only use the longType variable, as the other 2 are internal data and should ideally only be read.

When the argument is encountered, we will allocate memory for the string in the data field, you can then use the data string. Once you're done using the string, you should deallocate it using the cleanupPairs function like this:

UCLI::Parser::cleanupPairs(pairs, 2);

Pairs with function

To use pairs with functions, create an array of type UCLI_Parser_PairWithFunc like this:

struct UCLI_Parser_PairWithFunc
{
    const char* longType;
    void* additionalData;
    void(*func)(struct UCLI_Parser_PairWithFunc*, const char*);
};

The additionalData field is a void*, which points to any additional data you want the function to have access to(can be null)

The func field is a function pointer with the following signature: void(UCLI_Parser_PairWithFunc*, const char*) where the first argument is the struct containing the function pointer, and the second the value of the pair

Array flags

Finally, the library also provides support for array flags i.e. flags like these --arr a b c d e f, where a to f are part of an array. To create flags like this, create an array of type UCLI_Parser_ArrayFlag like this:

UCLI_Parser_ArrayFlag arrayFlags[4] = 
{
    {
        .longType = "arr",
        .shortType = "",
        .func = [](UCLI_Parser_ArrayFlag*, char** args, size_t size) -> void 
        {
            for (size_t i = 0; i < size; i++)
                std::cout << args[i] << std::endl;
        },
    },
    {
        .longType = "arg1",
        .shortType = "",
        .func = [](UCLI_Parser_ArrayFlag*, char** args, size_t size) -> void 
        {
            for (size_t i = 0; i < size; i++)
                std::cout << args[i] << std::endl;
        },
    },
    {
        .longType = "arg2",
        .shortType = "",
        .func = [](UCLI_Parser_ArrayFlag*, char** args, size_t size) -> void 
        {
            for (size_t i = 0; i < size; i++)
                std::cout << args[i] << std::endl;
        },
    },
    {
        .longType = "",
        .shortType = "Z",
        .func = [](UCLI_Parser_ArrayFlag*, char** args, size_t size) -> void 
        {
            for (size_t i = 0; i < size; i++)
                std::cout << args[i] << std::endl;
        },
    }
};

The UCLI_Parser_ArrayFlag struct looks like this:

struct UCLI_Parser_ArrayFlag
{
    const char* longType;
    const char* shortType;
    void* additionalData;
    UCLI_Parser_ArrayFlagFunc func;
};

The first 2 fields are the standard argument names, the 3rd is a void* pointing to additional data for the function.

The 4th argument is the function of type UCLI_Parser_ArrayFlagFunc, which evaluates to a function with the following signature void(UCLI_Parser_ArrayFlag*, char**, size_t), where the first argument is the struct containing the function, the second is the array of strings and the third, the size of the array

Setting the default array

By default, an application will have a default array, already set up that will do nothing. This is there in cases like this: https://madladsquad.com/app a b c d e f. You can change this by calling the setDefaultArray function, like this:

parser.setDefaultArray(nullptr, [](UCLI_Parser_ArrayFlag*, char** args, size_t size) -> void 
{
    for (size_t i = 0; i < size; i++)
        std::cout << "Default array: " << args[i] << std::endl;
});

where the first argument of the function is the void*, and the second the function, part of the UCLI_Parser_ArrayFlag struct type

Parsing the args

To parse the args call the parse function, that looks like this:

void parse(int argc, char** argv,
           UCLI_Parser_ArrayFlag* arrayFlags = nullptr, size_t arrayFlagsSize = 0,
           UCLI_Parser_BooleanFlag* booleanFlags = nullptr, size_t booleanFlagsSize = 0,
           UCLI_Parser_BooleanFlagWithFunc* booleanFlagsWithFunc = nullptr, size_t booleanFlagsWithFuncSize = 0,
           UCLI_Parser_Pair* pairs = nullptr, size_t pairsSize = 0,
           UCLI_Parser_PairWithFunc* pairsWithFunc = nullptr, size_t pairsWithFuncSize = 0) noexcept;

The first 2 arguments are the int argc and char** argv that are already passed to the main function of your C/C++ application. After that, we have pairs of array and size ordered like this:

UCLI_Parser_ArrayFlag, size
UCLI_Parser_BooleanFlag, size
UCLI_Parser_BooleanFlagWithFunc, size
UCLI_Parser_Pair, size
UCLI_Parser_PairWithFunc, size

Now you can pass all the arrays we created before. It can look something like this:

parser.parse(argc, argv,
             arrayFlags, 4,
             nullptr, 0,
             flags, 6,
             pairs, 2,
             nullptr, 0);

You can also use the overload of parse that takes std::vector that looks like this:

void parse(int argc, char** argv,
           const std::vector<ArrayFlag>& arrayFlags = {},
           const std::vector<BooleanFlag>& booleanFlags = {},
           const std::vector<BooleanFlagWithFunc>& booleanFlagsWithFunc = {},
           const std::vector<Pair>& pairs = {},
           const std::vector<PairWithFunc>& pairsWithFunc = {}) noexcept;

C API

The C API is basically the same as the C++ API, except that it doesn't use a class to handle functions. It looks like this:

void UCLI_Parser_init(struct UCLI_Parser_Data* data);

void UCLI_Parser_setDelimiter(struct UCLI_Parser_Data* data, char del);
void UCLI_Parser_setArgumentStyleWindows(struct UCLI_Parser_Data* data, bool bWindows);

void UCLI_Parser_setUnknownArgumentCallback(struct UCLI_Parser_Data* data, UCLI_Parser_UnknownArgumentsCallback func, void* callbackData);
void UCLI_Parser_setDefaultArray(struct UCLI_Parser_Data* data, void* additionalData, UCLI_Parser_ArrayFlagFunc func);

void UCLI_Parser_setBooleanFlipping(struct UCLI_Parser_Data* data, bool bFlip);

void UCLI_Parser_parse(struct UCLI_Parser_Data* data, int argc, char** argv,
                       struct UCLI_Parser_ArrayFlag* arrayFlags, size_t arrayFlagsSize,
                       struct UCLI_Parser_BooleanFlag* booleanFlags, size_t booleanFlagsSize,
                       struct UCLI_Parser_BooleanFlagWithFunc* booleanFlagsWithFunc, size_t booleanFlagsWithFuncSize,
                       struct UCLI_Parser_Pair* pairs, size_t pairsSize,
                       struct UCLI_Parser_PairWithFunc* pairsWithFunc, size_t pairsWithFuncSize);

void UCLI_Parser_cleanupPairs(struct UCLI_Parser_Pair* pairs, size_t pairsSize);

as you can see the functions are the same, except that their first argument is a pointer to a struct of type UCLI_Parser_Data. This struct is used to store the data of the parser and members should not be accessed.

The only actual difference between the C and C++ API is initializing the parser. To initialise the parser in the C API, simply create a new variable of type UCLI_Parser_Data and call UCLI_Parser_init on it, like this:

struct UCLI_Parser_Data data;
UCLI_Parser_init(&data);