5.5 KiB
args.h
Single header command line argument parser for C and C++ loosely based on python's argparse module.
Usage:
Define implementation macro before you include this file in one C or C++ file:
#define ARGS_IMPLEMENTATION
Should look like this:
#include ...
#include ...
#define ARGS_IMPLEMENTATION
#include "args.h"
This will expand the implementation into that file, in other files you can just include the header without the macro.
#include "args.h"
Example:
void example(int argc, char **argv) {
args_t args = {
.program = argv[0],
.description = "this program does very cool things on many files and outputs it somewhere",
.epilogue = "it also shows cool epilogue text in it's help",
};
args_add(&args, (args_desc_t){"filenames", .nargs = "+" });
args_add(&args, (args_desc_t){"--output", "-o" });
args_add(&args, (args_desc_t){"--force", "-f", .store_value = "1", .default_value = "0"});
args_add(&args, (args_desc_t){"--recursive", "-r", .store_value = "1", .default_value = "0"});
args_add(&args, (args_desc_t){"--help", "-h", .store_value = "1", .default_value = "0"});
args_parse(&args, argc, argv);
const char **filenames = args_get(&args, "filenames");
const char **output = args_get(&args, "--output");
int force = atoi(args_get(&args, "-f")[0]);
int recursive = atoi(args_get(&args, "--recursive")[0]);
int help = atoi(args_get(&args, "--help")[0]);
if (help) {
args_print_help(&args);
return;
}
// ..
for (int i = 0; filenames[i]; i += 1) {
// ..
}
}
Execute like this:
app.exe file1 file2 file3 --force --output /folder/.
Help menu:
usage: ./a.out [options] filenames ...
this program does very cool things on many files and outputs it somewhere
options:
filenames <arg> ... list of filenames to process
--output -o <arg> path to the folder where output will be gathered
--force -f force the processing
--recursive -r apply recursively to files in current directory
--help -h show this help window
it also shows cool epilogue text in it's help
How To's
- Single positional argument
program.exe argument
Specifying .nargs = "<digit>" enforces a strict number
of arguments, so supplying less or more will result in
an error.
args_t args = {0};
args_add(&args, (args_desc_t){"argument", .nargs = "1"});
args_parse(&args, argc, argv);
const char *argument = args_get(&args, "argument")[0];
- One or more positional arguments
program.exe a b c
You need to set .nargs = "+" this will make it so that one
or more arguments are expected. Alternatively .nargs = "*",
which makes it so that having even one argumenti is optional.
args_t args = {0};
args_add(&args, (args_desc_t){"arguments", .nargs = "+"});
args_parse(&args, argc, argv);
const char **arguments = args_get(&args, "arguments");
- Help menu, help flag
program.exe --help
args_t args = {0};
args_add(&args, (args_desc_t){"--help", "-h", .store_value = "1", .default_value = "0"});
args_parse(&args, argc, argv);
int help = atoi(args_get(&args, "help")[0]);
if (help) {
args_print_help(&args);
exit(0);
}
- Typechecked optional list of integers
program.exe --list 1 2 3 program.exe
args_t args = {0};
args_add(&args, (args_desc_t){"--list", "-l", .type = ARGS_TYPE_INT, .nargs = "*"});
args_parse(&args, argc, argv);
const char **list = args_get(&args, "--list");
- Custom logging
You can override the ARGS_PRINTF macro, at compile time the library will use that. By default it uses fprintf(stderr)
#define ARGS_PRINTF(...) fprintf(stdout, __VA_ARGS__)
#define ARGS_IMPLEMENTATION
#include "args.h"
- User defined memory allocator
#define ARGS_MALLOC(x) my_malloc(x)
#define ARGS_FREE(x) my_free(x)
#define ARGS_IMPLEMENTATION
#include "args.h"
- C++
The library works in C++ but leverages special C syntax for aggregate types, in later C++ the syntax might be simiar so no issues there but for older versions you will either need to write some small wrapper functions or just bare with initializing structures inline. For example:
void args_add_bool(args_t *args, const char *name, const char *alt_name, const char *help) {
args_desc_t desc = {name, alt_name};
desc.help = help;
desc.store_value = "1";
desc.default_value = "0";
args_add(args, desc);
}
void args_add_nargs(args_t *args, const char *name, const char *alt_name, const char *nargs, const char *help) {
args_desc_t desc = {name, alt_name};
desc.nargs = nargs;
desc.help = help;
args_add(args, desc);
}
int main(int argc, char **argv) {
args_t args = {
.program = argv[0],
.description = "this program does very cool things on many files and outputs it somewhere",
.epilogue = "it also shows cool epilogue text in it's help",
};
args_add_nargs(&args, "filenames", NULL, "+", "list of filenames to process");
args_add_nargs(&args, "--output", "-o", "?", "path to the folder where output will be gathered");
args_add_bool(&args, "--force", "-f", "force the processing");
args_add_bool(&args, "--recursive", "-r", "apply recursively to files in current directory");
args_add_bool(&args, "--help", "-h", "show this help window");
args_parse(&args, argc, argv);
}
License
Library is in public domain. See end of code file for license.