{"id":13733854,"url":"https://github.com/kongaskristjan/fire-hpp","last_synced_at":"2025-05-08T10:30:27.715Z","repository":{"id":55621529,"uuid":"263233019","full_name":"kongaskristjan/fire-hpp","owner":"kongaskristjan","description":"A C++ library that uses clever tricks to create super low-code, yet fully functional CLIs","archived":false,"fork":false,"pushed_at":"2024-09-15T15:45:27.000Z","size":251,"stargazers_count":446,"open_issues_count":0,"forks_count":18,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-11-15T02:34:27.827Z","etag":null,"topics":["argument-parser","cli","library"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kongaskristjan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-12T04:34:34.000Z","updated_at":"2024-10-31T09:05:45.000Z","dependencies_parsed_at":"2024-09-15T17:04:06.599Z","dependency_job_id":"9b0d8703-d773-4334-ad64-eb58435c0bee","html_url":"https://github.com/kongaskristjan/fire-hpp","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kongaskristjan%2Ffire-hpp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kongaskristjan%2Ffire-hpp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kongaskristjan%2Ffire-hpp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kongaskristjan%2Ffire-hpp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kongaskristjan","download_url":"https://codeload.github.com/kongaskristjan/fire-hpp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253045509,"owners_count":21845713,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["argument-parser","cli","library"],"created_at":"2024-08-03T03:00:50.158Z","updated_at":"2025-05-08T10:30:27.472Z","avatar_url":"https://github.com/kongaskristjan.png","language":"C++","funding_links":[],"categories":["C++","Argument Parsers"],"sub_categories":[],"readme":"\n[![CI status](https://github.com/kongaskristjan/fire-hpp/actions/workflows/cmake.yml/badge.svg)](https://github.com/kongaskristjan/fire-hpp/actions/workflows/cmake.yml)\n\n# Fire for C++\n\nFire for C++ is a single header library that creates a command line interface from a function signature. Here's the whole program for adding two numbers with command line:\n```c++\n#include \u003ciostream\u003e\n#include \u003cfire-hpp/fire.hpp\u003e\n\nint fired_main(int x = fire::arg(\"-x\"), int y = fire::arg(\"-y\")) {\n    std::cout \u003c\u003c x + y \u003c\u003c std::endl;\n    return 0;\n}\n\nFIRE(fired_main)\n```\n\nThat's all. Usage:\n\n```\n$ ./add -x=1 -y=2\n3\n```\n\nAs you likely expect,\n* `--help` prints a meaningful message with required arguments and their types.\n* an error message is displayed for incorrect usage.\n* the program runs on Linux, Windows and Mac OS.\n\n### What's covered?\n\n* [flags](#flag); [named and positional](#identifier) parameters; [variadic parameters](#variadic)\n* [optional parameters](#optional)/[default values](#default)\n* conversions to [integer, floating-point and `std::string`](#standard) with automatic error checking\n* [program](#fire)/[parameter](#identifier) descriptions\n* standard constructs, such as `-abc \u003c=\u003e -a -b -c` and `-x=1 \u003c=\u003e -x 1`\n\n### Why yet another CLI library?!\n\nWith most libraries, creating a CLI roughly follows this pattern:\n1. define arguments\n2. call `parse(argc, argv);`\n3. check whether errors are detected by `parse()`, print them and return (optional)\n4. check for `-h` and `--help`, print the help message and return (optional)\n5. for each argument:\n    1. get argument from the map and if necessary convert to the right type\n    2. try/catch for errors in conversion and return (optional)\n\nThat's a non-trivial amount of boilerplate, especially for simple scripts. Because of that, programmers (and a lot of library examples) tend to skip the optional parts, however this incurs a significant usability cost. Also, many libraries don't help at all with the conversion step.\n\nWith fire-hpp, you only call `FIRE(fired_main)` and define arguments as function parameters. When `fired_main()` scope begins, all steps have already been completed.\n\n## Q. Quickstart\n\n### Q.1 Requirements\n\n* Using fire.hpp: compiler compatible with C++ version 11, 14, 17, 20, or 23.\n* Compiling examples: CMake 3.5+.\n* Compiling/running tests: CMake 3.14+ and Python 3.5-3.12. GTest is downloaded, compiled and linked automatically.\n\n### Q.2 Running examples\n\nSteps to run examples:\n* Clone repo: `git clone https://github.com/kongaskristjan/fire-hpp`\n* Create build and change directory: `cd fire-hpp \u0026\u0026 mkdir build \u0026\u0026 cd build`\n* Configure/build: `cmake .. \u0026\u0026 cmake --build .` (or substitute the latter command with appropriate build system invocation, eg. `make -j8` or `ninja`)\n* If errors are encountered, clear the build directory and disable pedantic warnings as errors with `cmake -D DISABLE_PEDANTIC= ..` (you are encouraged to open an issue).\n* Run: `./examples/add --help` or `./examples/add -x=3 -y=5`\n\n### Q.3 Usage with cmake\n\n* [See here](https://github.com/kongaskristjan/fire-hpp/tree/master/docs/cmake.md)\n\n## T. Tutorial\n\nLet's go through each part of the following example.\n\n```c++\nint fired_main(int x = fire::arg(\"-x\"), int y = fire::arg(\"-y\")) { // Define and convert arguments\n    std::cout \u003c\u003c x + y \u003c\u003c std::endl; // Use x and y, they're ints!\n    return 0;\n}\n\nFIRE(fired_main) // call fired_main()\n```\n\n* __FIRE(function name)__\n`FIRE(fired_main)` expands into the actual `main()` function that defines your program's entry point and fires off `fired_main()`. `fired_main` is called without arguments, thus compiler is forced to use the default `fire::arg` values.\n\n* __fire::arg(identifiers[, default value])__\n A constructor that accepts the name/shorthand/description/position of the argument. Use a brace enclosed list for several of them (eg. `fire::arg({\"-x\", \"--longer-name\", \"description of the argument\"})` or `fire::arg({0, \"zeroth element\"})`. The library expects a single dash for single-character shorthands, two dashes for multi-character names, and zero dashes for descriptions. `fire::arg` objects should be used as default values for fired function parameters. See [documentation](#fire_arg) for more info.\n\n* __int fired_main(arguments)__\nThis is what you perceive as the program entry point. All arguments must be `bool`, integral, floating-point, `fire::optional\u003cT\u003e`, `std::string` or `std::vector\u003cT\u003e` type and default initialized with `fire::arg` objects (failing to initialize properly results in undefined behavior!). See [conversions](#conversions) to learn how each of them affects the CLI.\n\n## D. Documentation\n\n### \u003ca id=\"fire\"\u003e\u003c/a\u003e D.1 FIRE(fired_main[, program_description]) and variants\n\n* `FIRE(fired_main[, program_description])` creates the main function that parses arguments and calls `fired_main`.\n* `FIRE_NO_EXCEPTIONS(...)` is similar, but can be used even if compiler has exceptions disabled. However, this imposes limitations on what the library can parse. Specifically, it disallows space assignment, eg. `-x 1` must be written as `-x=1`.\n* `FIRE_ALLOW_UNUSED(...)` is similar to `FIRE(...)`, but allows unused arguments. This is useful when [raw arguments](#raw_args) are accessed (eg. for another library).\n\nProgram description can be supplied as the second argument:\n```\nFIRE(fired_main, \"Hello there\")\n```\n\n### D.2 \u003ca id=\"fire_arg\"\u003e\u003c/a\u003e fire::arg(identifiers[, default_value]])\n\n#### \u003ca id=\"identifier\"\u003e\u003c/a\u003e D.2.1 Identifiers\n\nIdentifiers are used to find arguments from command line and provide a description. In general, it's a brace enclosed list of elements (braces can be omitted for a single element):\n* `\"-s\"` shorthand name for argument\n* `\"--multicharacter-name\"`\n* `0` index of a positional argument\n* `\"\u003cname of the positional argument\u003e\"`\n* any other string: `\"description of any argument\"`\n* variadic arguments: `fire::variadic()`\n\n--------\n\n* Example: `int fired_main(int x = fire::arg(\"-x\"));`\n    * CLI usage: `program -x=1`\n\n\n* Example: `int fired_main(int x = fire::arg({\"-x\", \"--long-name\"}));`\n    * CLI usage: `program -x=1`\n    * CLI usage: `program --long-name=1`\n\n\n* Example: `int fired_main(int x = fire::arg({0, \"\u003cname of argument\u003e\", \"description\"}));`\n    * CLI usage: `program 1`\n    * `\u003cname of argument\u003e` and `description` appear in help messages\n\n\n* Example: `int fired_main(vector\u003cint\u003e x = fire::arg(fire::variadic()));`\n    * CLI usage: `program 1 2 3`\n\n#### \u003ca id=\"default\"\u003e\u003c/a\u003e D.2.2 Default value (optional)\n\nDefault value if no value is provided through command line. Can be either `std::string`, integral or floating-point type and `fire::arg` must be converted to that same type. This default is also displayed on the help page.\n\n* Example: `int fired_main(int x = fire::arg({\"-x\", \"--long-name\"}, 0));`\n    * CLI usage: `program` -\u003e `x==0`\n    * CLI usage: `program -x=1` -\u003e `x==1`\n\nFor an optional argument without a default, see [fire::optional](#optional).\n\n#### \u003ca id=\"constraints\"\u003e\u003c/a\u003e D.2.3 Constraints\n\nConstraints can be applied to arguments by calling `fire:arg`'s constraint methods:\n* `fire::arg().min(T minimum)` - specifies minimum value\n* `fire::arg().max(T maximum)` - specifies maximum value\n* `fire::arg().bounds(T minimum, T maximum)` - specifies both minimum and maximum values\n* `fire::arg().one_of({...})` - specifies possible values\n\nThese methods\n1) check whether user supplied value fits the constraint and emit a proper error message if condition is not met\n2) append the constraint to argument's help message\n\n* Example: `int fired_main(int x = fire::arg(\"-x\").bounds(-1000, 1000));`\n  * CLI usage: `program -x=100` -\u003e `x==100`\n  * CLI usage: `program -x=-10000` -\u003e `Error: argument -x value -10000 must be at least -1000`\n  * CLI usage: `program --help` -\u003e `-x=INTEGER  description [-1000 \u003c= x \u003c= 1000]` (one of the lines)\n\n* Example: `int fired_main(std::string s = fire::arg(\"-s\").one_of({\"hi\", \"there\"}));`\n  * CLI usage: `program -s=hi` -\u003e `s==hi`\n  * CLI usage: `program -s=hello` -\u003e `Error: argument -s value must be one of (hi, there), but given was 'hello'`\n\n### \u003ca id=\"conversions\"\u003e\u003c/a\u003e D.3 fire::arg conversions\n\nTo conveniently obtain arguments with the right type and automatically check the validity of input, `fire::arg` class defines several implicit conversions.\n\n#### \u003ca id=\"standard\"\u003e\u003c/a\u003e D.3.1 std::string, integral, or floating point\n\nConverts the argument value on command line to the respective type. Displays an error if the conversion is impossible or default value has wrong type.\n\n* Example: `int fired_main(std::string name = fire::arg(\"--name\"));`\n    * CLI usage: `program --name=fire` -\u003e `name==\"fire\"`\n\n\n* Example: `int fired_main(double x = fire::arg(\"-x\"));`\n    * CLI usage: `program -x=2.5` -\u003e `x==2.5`\n    * CLI usage: `program -x=blah` -\u003e `Error: value blah is not a real number`\n\n#### \u003ca id=\"optional\"\u003e\u003c/a\u003e D.3.2 fire::optional\n\nUsed for optional arguments without a reasonable default value. This way the default value doesn't get printed in a help message. The underlying type can be `std::string`, integral or floating-point.\n\n`fire::optional` is a tear-down version of [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional), with compatible implementations for [`has_value()`](https://en.cppreference.com/w/cpp/utility/optional/operator_bool), [`value_or()`](https://en.cppreference.com/w/cpp/utility/optional/value_or) and [`value()`](https://en.cppreference.com/w/cpp/utility/optional/value).\n\n* Example: `int fired_main(fire::optional\u003cstd::string\u003e name = fire::arg(\"--name\"));`\n    * CLI usage: `program` -\u003e `name.has_value()==false`, `name.value_or(\"default\")==\"default\"`\n    * CLI usage: `program --name=\"fire\"` -\u003e `name.has_value()==true` and `name.value_or(\"default\")==name.value()==\"fire\"`\n\nFor an optional argument with a sensible default value, see [default value](#default).\n\n#### \u003ca id=\"flag\"\u003e\u003c/a\u003e D.3.3 bool: flag argument\n\nBoolean flags are `true` when they exist on command line and `false` when they don't. Multiple single-character flags can be packed on command line by prefixing with a single hyphen: `-abc \u003c=\u003e -a -b -c`\n\n* Example: `int fired_main(bool flag = fire::arg(\"--flag\"));`\n    * CLI usage: `program` -\u003e `flag==false`\n    * CLI usage: `program --flag` -\u003e `flag==true`\n\n#### \u003ca id=\"variadic\"\u003e\u003c/a\u003e D.3.4 std::vector\u003cT\u003e: variadic argument\n\nA method for getting all positional arguments as a vector. The `fire::arg` object can be converted to `std::vector\u003cstd::string\u003e`, `std::vector\u003cintegral type\u003e` or `std::vector\u003cfloating-point type\u003e`. Using variadic argument forbids extracting positional arguments with `fire::arg(index)`.\n\nIn this case, identifier should be `fire::variadic()`. Description can be supplied in the usual way.\n\n* Example: `int fired_main(vector\u003cstd::string\u003e params = fire::arg({fire::variadic(), \"description\"}));`\n    * CLI usage: `program abc xyz` -\u003e `params=={\"abc\", \"xyz\"}`\n    * CLI usage: `program` -\u003e `params=={}`\n\n### \u003ca id=\"post_functions\"\u003e\u003c/a\u003e D.4 Post fired_main() functions\n\n#### \u003ca id=\"\"\u003e\u003c/a\u003e D.4.1.1 Print help or error message with fire formatting\n\n* `fire::print_help()` - print the help message\n* `fire::input_error(const string \u0026msg)` - print error message and exit program\n* `fire::input_assert(bool pass, const std::string \u0026msg)` - if `pass` is not satisfied, print error message and exit program\n\n#### \u003ca id=\"\"\u003e\u003c/a\u003e D.4.1.2 Helper function for getting named argument names in assert messages:\n\n`std::string fire::helpful_name(const string \u0026name)` - return user called name of the specified argument (given by one name) if it exists, otherwise return empty string.\n\n* Example: `int fired_main(optional\u003cint\u003e value = fire::arg({\"-v\", \"--value\"}));`\n  * CLI usage: `program` -\u003e `fire::helpful_name(\"--value\") == \"\"`\n  * CLI usage: `program -v=2` -\u003e `fire::helpful_name(\"--value\") == \"-v\"`\n  * CLI usage: `program --value=2` -\u003e `fire::helpful_name(\"--value\") == \"--value\"`\n* Typical usage: `fire::input_assert(value \u003e 0, \"Argument \" + fire::helpful_name(\"--value\") + \" must be greater than 0\")`\n\n`std::string fire::helpful_name(int pos)` - return correctly formatted positional argument number\n\n#### \u003ca id=\"raw_args\"\u003e\u003c/a\u003e D.4.2 Accessing raw arguments\n\nSome third party libraries require access to raw argc/argv. This is gained through `fire::raw_args` (of type `fire::c_args`), which has `argc()` and `argv()` methods for accessing the arguments.\n\nExamples:\n\n* Usage without modification:\n```c++\nint argc = fire::raw_args.argc();\nchar ** argv = fire::raw_args.argv();\nnon_modifying_call(argc, argv);\n```\n\n* Usage with modification:\n```c++\nfire::c_args raw_args_copy = fire::raw_args; \nint\u0026 argc = raw_args_copy.argc();\nchar ** argv = raw_args_copy.argv();\nmodifying_call(argc, argv);\n// Once out of scope, raw_args_copy releases argv strings\n```\n\nYou also need [`FIRE_ALLOW_UNUSED(...)`](#fire) if the third party library processes it's own arguments.\n\n## G. Guides\n\n* [CMake usage](https://github.com/kongaskristjan/fire-hpp/blob/master/docs/cmake.md)\n* [Conan usage](https://github.com/kongaskristjan/fire-hpp/blob/master/docs/conan.md)\n* [Development](https://github.com/kongaskristjan/fire-hpp/blob/master/docs/development.md)\n* [Fire-hpp's algorithm explanation (how does it work?)](https://github.com/kongaskristjan/fire-hpp/blob/master/docs/algorithm.md)\n\n## L. Links\n\nOther libraries you might find useful:\n\n* [python-fire](https://github.com/google/python-fire): something similar in Python, I got my inspiration from there\n* [fire-llvm](https://github.com/Time0o/fire-llvm): an even neater interface (you can write parameters as `(int x, int y)` instead of `(int x = fire::arg(\"-x\"), int y = fire::arg(\"-y\"))`). Also adds subcommands support. The downside is that it's not pure C++, and requires you to compile an llvm plugin to actually compile your code.\n* [CLI11](https://github.com/CLIUtils/CLI11): A somewhat more conventional C++ CLI library. IMHO A very good interface while avoiding unconventional trickery (like hijacking `main()`).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkongaskristjan%2Ffire-hpp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkongaskristjan%2Ffire-hpp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkongaskristjan%2Ffire-hpp/lists"}