{"id":13441367,"url":"https://github.com/adishavit/argh","last_synced_at":"2025-05-14T14:09:05.589Z","repository":{"id":10292293,"uuid":"65051781","full_name":"adishavit/argh","owner":"adishavit","description":"Argh! A minimalist argument handler.","archived":false,"fork":false,"pushed_at":"2025-01-21T09:38:36.000Z","size":263,"stargazers_count":1362,"open_issues_count":21,"forks_count":94,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-04-14T02:57:43.088Z","etag":null,"topics":["argument-parser","cli","cli-args","command-line","command-line-parser","cpp11","getopt","getopts","header-only","single-file"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adishavit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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":"2016-08-05T22:02:00.000Z","updated_at":"2025-04-13T15:26:30.000Z","dependencies_parsed_at":"2024-01-31T09:01:45.790Z","dependency_job_id":"08c189d0-b573-4926-ac20-7c8b27d1d368","html_url":"https://github.com/adishavit/argh","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adishavit%2Fargh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adishavit%2Fargh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adishavit%2Fargh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adishavit%2Fargh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adishavit","download_url":"https://codeload.github.com/adishavit/argh/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254160196,"owners_count":22024567,"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","cli-args","command-line","command-line-parser","cpp11","getopt","getopts","header-only","single-file"],"created_at":"2024-07-31T03:01:33.171Z","updated_at":"2025-05-14T14:09:00.581Z","avatar_url":"https://github.com/adishavit.png","language":"C++","readme":"![logo](assets/argh_logo_small.png)\n\n\u003e *Frustration-free command line processing*\n\n[![Language](https://img.shields.io/badge/language-C++-blue.svg)](https://isocpp.org/)\n[![Standard](https://img.shields.io/badge/C%2B%2B-11-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization)\n[![License](https://img.shields.io/badge/license-BSD-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)\n[![Try it online](https://img.shields.io/badge/try%20it-online-orange.svg)](http://melpon.org/wandbox/permlink/ralxPN49F7cUY2yw)\n[![Build Status](https://travis-ci.org/adishavit/argh.svg?branch=master)](https://travis-ci.org/adishavit/argh)\n\nSo many different command line processing libraries out there and none of them just work!  \nSome bring their whole extended family of related and unrelated external dependencies (*yes, I'm looking at you Boost*).  \nSome require quirky syntax and/or very verbose setups that sacrifice simplicity for the generation of a cute usage message and validation. Many come to dominate your `main()` file and yet others do not build on multiple plaforms - for some even their own tests and trivial usage cause crashes on some systems. *Argh!*\n\nIf you're writing a highly-sophisticated command line tool, then `Boost.Program_options` and its kind might give you many advanced options. However, if you need to get up and running quickly, effectively and with minimal fuss, give the single header-file `argh` a try.\n\n## TL;DR\nIt doesn't get much simpler than this:\n```cpp\n#include \u003ciostream\u003e\n#include \"argh.h\"\n\nint main(int, char* argv[])\n{\n    argh::parser cmdl(argv);\n\n    if (cmdl[{ \"-v\", \"--verbose\" }])\n        std::cout \u003c\u003c \"Verbose, I am.\\n\";\n\n    return EXIT_SUCCESS;\n}\n```\n#### TL;DR Videos \n- [Arguments over Arguments - Adi Shavit - Core C++ 2019](https://youtu.be/hCbEHzDvLno)\n- [Arguments over Arguments, but you already know this... - Adi Shavit - CppCon 2019](https://youtu.be/KkjKkGuQUqU)\n\n## Philosophy\n\nContrary to many alternatives, `argh` takes a minimalist *laissez-faire* approach, very suitable for fuss-less prototyping with the following rules:\n\nThe API is:\n - Minimalistic but expressive:\n    - No getters nor binders\n    - Just the `[]` and `()` operators\n    - Easy iteration (range-`for` too)\n - You don't pay for what you don't use\n - Conversion to typed variables happens (via `std::istream \u003e\u003e`) on the user side *after* the parsing phase\n - No exceptions thrown for failures\n - Liberal BSD license\n - Single header file\n - No non-`std` dependencies\n\n`argh` does **not** care about:\n\n - How many '`-`' preceded your option\n - Which flags and options you support - that is your responsibility\n - Syntax validation: *any* command line is a valid (*not necessarily unique*) combination of positional *parameters*, *flags* and *options*\n - Automatically producing a usage message\n\n## Tutorial\n\nCreate parser:\n\n```cpp\nauto cmdl = argh::parser(argc, argv);\n```\nIn fact, you can even drop `argc`. This will also work:  \n```cpp\nargh::parser cmdl(argv);\n```\n\nPositional argument access by (integer) index with `[\u003csize_t\u003e]`:\n```cpp\ncout \u003c\u003c \"Exe name is: \" \u003c\u003c cmdl[0] \u003c\u003c '\\n';\n                               ^^^\nassert(cmdl[10000].empty()); // out-of-bound index returns empty string\n            ^^^^^\n```\nBoolean flag argument access by (string) name with `[\u003cstd::string\u003e]`:\n```cpp\ncout \u003c\u003c \"Verbose mode is \" \u003c\u003c ( cmdl[\"verbose\"] ? \"ON\" : \"OFF\" ) \u003c\u003c '\\n';\n                                    ^^^^^^^^^^^\n```\nAny dashes are trimmed so are not required.  \n\nYour flag can have several alternatives, just list them with `[{ \"\u003cname-1\u003e\", \"\u003cname-2\u003e\", ... }]`:\n```cpp\ncout \u003c\u003c \"Verbose mode is \" \u003c\u003c ( cmdl[{ \"-v\", \"--verbose\" }] ? \"ON\" : \"OFF\" ) \u003c\u003c '\\n';\n                                    ^^^^^^^^^^^^^^^^^^^^^^^\n```\nBeyond `bool` and `std::string` access with `[]`, as shown above, we can also access the argument values as an `std::istream`. This is very useful for type conversions.\n\n`std::istream` positional argument access by (integer) index with `(\u003csize_t\u003e)`:\n```cpp\nstd::string my_app_name;\ncmdl(0) \u003e\u003e my_app_name; // streaming into a string\n    ^^^\ncout \u003c\u003c \"Exe name is: \" \u003c\u003c my_app_name \u003c\u003c '\\n';\n```\nWe can also check if a particular positional arg was given or not (this is like using `[\u003cstd::string\u003e]` above):\n```cpp\nif (!cmdl(10))\n  cerr \u003c\u003c \"Must provide at least 10 arguments!\" \u003c\u003c '\\n';\nelse if (cmdl(11))\n  cout \u003c\u003c \"11th argument  is: \" \u003c\u003c cmdl[11] \u003c\u003c '\\n';\n```\nBut we can also set default values for positional arguments. These are passed as the second argument:\n```cpp\nfloat scale_factor;\ncmdl(2, 1.0f) \u003e\u003e scale_factor;\n     ^^^^^^^\n```\nIf the position argument was not given or the streaming conversion failed, the default value will be used.  \n\nSimilarly, parameters can be accessed by name(s) (i.e. by string or list of string literals) with:  \n`(\u003cstd::string\u003e [, \u003cdefault value\u003e])` or   `({ \"\u003cname-1\u003e\", \"\u003cname-2\u003e\", ... } [, \u003cdefault value\u003e])`:  \n```cpp\nfloat scale_factor;\ncmdl(\"scale\", 1.0f) \u003e\u003e scale_factor; // Use 1.0f as default value\n     ^^^^^^^^^^^^^\n\nfloat threshold;\nif (!(cmdl({ \"-t\", \"--threshold\"}) \u003e\u003e threshold)) // Check for missing param and/or bad (inconvertible) param value\n  cerr \u003c\u003c \"Must provide a valid threshold value! Got '\" \u003c\u003c cmdl(\"threshold\").str() \u003c\u003c \"'\" \u003c\u003c endl;\nelse                                                                        ^^^^^^\n  cout \u003c\u003c \"Threshold set to: \" \u003c\u003c threshold \u003c\u003c '\\n';\n```\nAs shown above, use `std::istream::str()` to get the param value as a `std:string` or just stream the value into a variable of a suitable type. Standard stream state indicates failure, including when the argument was not given.  \nWhen using multiple names, the first value found will be returned.\n\nPositional arguments can be iterated upon directly using *range-for*:\n```cpp\ncout \u003c\u003c \"Positional args:\\n\";\nfor (auto\u0026 pos_arg : cmdl)\n  cout \u003c\u003c '\\t' \u003c\u003c pos_arg \u003c\u003c '\\n';\n```\nSimilarly, `cmdl.size()` will return the count of *positional* arguments. \n\nPositional arguments, flags *and* parameters are accessible as \"ranges\":\n```cpp\ncout \u003c\u003c \"Positional args:\\n\";\nfor (auto\u0026 pos_arg : cmdl.pos_args())\n  cout \u003c\u003c '\\t' \u003c\u003c pos_arg \u003c\u003c '\\n';\n\ncout \u003c\u003c \"\\nFlags:\\n\";\nfor (auto\u0026 flag : cmdl.flags())\n  cout \u003c\u003c '\\t' \u003c\u003c flag \u003c\u003c '\\n';\n\ncout \u003c\u003c \"\\nParameters:\\n\";\nfor (auto\u0026 param : cmdl.params())\n  cout \u003c\u003c '\\t' \u003c\u003c param.first \u003c\u003c \" : \" \u003c\u003c param.second \u003c\u003c '\\n';\n```\n\nIf a parameter appears several times in the command line, all its duplicates may be accessed, in order, like so:\n\n```cpp\ncout \u003c\u003c \"\\nValues for all `--input` parameters:\\n\";\nfor (auto\u0026 param : cmdl.params(\"input\"))  // iterate on all params called \"input\"\n  cout \u003c\u003c '\\t' \u003c\u003c param.first \u003c\u003c \" : \" \u003c\u003c param.second \u003c\u003c '\\n';\n```\n\n\nBy default, options are assumed to be boolean flags. \nWhen this is not what you want, there are several ways to specify when an option is a parameter with an associated value.  \n\n 1. Specify **`PREFER_PARAM_FOR_UNREG_OPTION`** mode to interpret *any* `\u003coption\u003e \u003cnon-option\u003e` as `\u003cparameter-name\u003e \u003cparameter-value\u003e`:\n    ```cpp\n    using namespace argh;\n    auto cmdl = parser(argc, argv, parser::PREFER_PARAM_FOR_UNREG_OPTION);\n                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    cout \u003c\u003c cmdl(\"--threshold\").str() \u003c\u003c '\\n';\n    ```\n    \n 2. Pre-register an expected parameter name with `add_param()` (before calling `parse()`):\n    ```cpp\n    argh::parser cmdl;\n    cmdl.add_param(\"threshold\"); // pre-register \"threshold\" as a param: name + value\n    cmdl.parse(argc, argv);\n    cout \u003c\u003c cmdl(\"threshold\").str() \u003c\u003c '\\n';\n    ```\n    You may also *batch* pre-register multiple options as parameters with `add_params({ ... })`:\n    ```cpp\n    argh::parser cmdl;\n    cmdl.add_params({ \"-t\", \"--threshold\", \"-s\", \"--scale\" }); // batch pre-register multiple params: name + value\n    cmdl.parse(argc, argv);\n    cout \u003c\u003c cmdl(\"threshold\").str() \u003c\u003c '\\n';\n    ```\n    _Unregistered_ options will default to boolean flags.\n    \n 3. Since pre-registration has to be done *before* parsing, we might as well just use the ctor:\n    ```cpp\n    argh::parser cmdl({ \"-t\", \"--threshold\", \"-s\", \"--scale\" }); // batch pre-register multiple params: name + value\n    cmdl.parse(argc, argv);\n    cout \u003c\u003c cmdl(\"threshold\").str() \u003c\u003c '\\n';\n    ```\n    \n 4. Use a `=` with no spaces around it within the option when *calling* the app:\n    ```cpp\n    \u003e\u003e my_app --threshold=42\n    42\n    ```\n    This will automatically be interpreted as a named parameter-value pair. \n    \n    \n### Tips\n- By default, arguments of the form `--\u003cname\u003e=\u003cvalue\u003e` (with no spaces, one or more dashes), e.g. `--answer=42`, will be parsed as `\u003cparameter-name\u003e \u003cparameter-value\u003e`.\nTo disable this specify the **`NO_SPLIT_ON_EQUALSIGN`** mode.\n- Specifying the **`SINGLE_DASH_IS_MULTIFLAG`** mode, a.k.a _Compound Arguments_, will split a single-hyphen argument into multiple single-character flags (as is common in various POSIX tools).\n- When using **`SINGLE_DASH_IS_MULTIFLAG`**, you can still pre-register the last character as a param with the value, such that if we pre-register `f` as a param, `\u003e\u003e myapp -xvf 42` will be parsed with two boolean flags `x` and `v` and a one param `f`=`42`.\n- When parsing parameter values as strings that may contain spaces (e.g. `--config=\"C:\\Folder\\With Space\\Config.ini\"`), prefer using `.str()` instead of `\u003e\u003e` to avoid the default automatic whitespace input stream tokenization:  \n`cout \u003c\u003c cmdl({ \"-c\", \"--config\" }).str()`.\n\n## Terminology\nAny command line is composed of **2** types of *Args*:\n\n1. ***Positional Args***:  \n    Free standing, in-order, values  \n    e.g. `config.json`  \n2. ***Options***:  \n    Args beginning with `-` (and that are not negative numbers).  \n    We identify ***2*** kinds of *Options*:  \n    1. ***Flags***:  \n       Boolean options =\u003e  (appear ? true : false)  \n       e.g. `-v`, `--verbose`  \n    2. ***Parameters***:  \n       A named value followed by a *non*-option value  \n       e.g. `--gamma 2.2`  \n\nThus, any command line can always be broken into some combination of *(1) positional args* *(2) flags* and *(3) parameters*.  \n\n## API Summary\n### Parsing\nParse the command line using either\n- The `parse()` method: `parser::parse([argc,] argv [, mode])`; or\n- The shorter form using the ctor directly:\n  `argh::parser([argc,] argv [, mode]);`\n- The shortest form does not even require `argc`, so in default `mode` just use:   \n  `parser(argv);`\n\n### Special Parsing Modes\nExtra flexibility can be added by specifying parsing modes:\n- **`NO_SPLIT_ON_EQUALSIGN`**:\n   By default, an option of the form `--pi=22/7` will be parsed as a *parameter* `pi` with an associated value `\"22/7\"`.\n   By setting this mode, it will be not be broken at the `=`.\n- **`PREFER_FLAG_FOR_UNREG_OPTION`**:\n  Split `\u003coption\u003e \u003cnon-option\u003e` into `\u003cflag\u003e` and `\u003cpos_arg\u003e`.\n  e.g. `myapp -v config.json` will have `v` as a lit flag and `config.json` as a positional arg.\n  *This is the default mode.*\n- **`PREFER_PARAM_FOR_UNREG_OPTION`**:\n  Interpret `\u003coption\u003e \u003cnon-option\u003e` as `\u003cparameter-name\u003e \u003cparameter-value\u003e`.\n  e.g. `myapp --gamma 2.2` will have `gamma` as a parameter with the value \"2.2\".\n- **`SINGLE_DASH_IS_MULTIFLAG`**:\n  Splits an option with a *single* dash into separate boolean flags, one for each letter (a.k.a _Compound Arguments_).\n  e.g. in this mode, `-xvf` will be parsed as 3 separate flags: `x`, `v`, `f`.\n\n### Argument Access\n- Use *bracket operators* to access *flags* and *positional* args:\n    - Use `operator[index]` to access *position* arg strings by *index*:\n        - e.g. `assert(cmdl[0] == argv[0])`, the app name.\n    - Use `operator[string]` to access boolean *flags* by *name*:\n        - e.g. `if (cmdl[\"v\"]) make_verbose();`\n    - Use `operator[{...}]` to access boolean *flags* by *multiple names*:\n        - e.g. `if (cmdl[{ \"v\", \"verbose\" }]) make_verbose();`\n  \n- Use the *parenthesis operators* to get an `std::istream` to stream values from *parameters* and *positional* args:\n    - Use `operator(index)` to access position arg `istream` by index:\n        - e.g. `cmdl(0) \u003e\u003e my_app_name`.\n    - Use `operator(string)` to access *parameter* values by *name*:\n        - e.g. `cmdl(\"scale\") \u003e\u003e scale_factor;`\n    - Use `operator({...})` to access *parameter* values by *multiple names*:\n        - e.g. `cmdl({ \"-s\", \"--scale\" }) \u003e\u003e scale_factor;`        \n    - Use `operator(index, \u003cdefault\u003e)` and `operator(string/{list}, \u003cdefault\u003e)` to stream a default value if the arg did not appear on the command line:\n        - e.g. `cmdl(\"scale\", 1.0f) \u003e\u003e scale_factor;`\n\nThe streaming happens at the user's side, so conversion failure can be checked there:\ne.g\n\n```cpp\nif (!(cmdl(\"scale\") \u003e\u003e scale_factor))\n  cerr \u003c\u003c \"Must provide valid scale factor!\" \u003c\u003c '\\n';\n```\n\nUse the `.str()` method to get the parameter value as a string: e.g. `cmdl(\"name\").str();`\n\n### More Methods\n\n- Use `parser::add_param()`, `parser::add_params()` or the `parser({...})` constructor to *optionally* pre-register a parameter name when in `PREFER_FLAG_FOR_UNREG_OPTION` mode.\n- Use `parser`, `parser::pos_args()`, `parser::flags()` and `parser::params()` to access and iterate over the Arg containers directly.\n\n## Finding Argh!\n\n* copy `argh.h` somewhere into your projects directories\n* **or** include the repository as a *submodule*\n* **or** use *CMake*!\n\n#### Finding Argh! - CMake\n\nThe provided `CMakeLists.txt` generates targets for tests, a demo application and an install target to install `argh` system-wide and make it known to CMake.  *You can control generation of* test *and* example *targets using the options `BUILD_TESTS` and `BUILD_EXAMPLES`. Only `argh` alongside its license and readme will be installed - not tests and demo!*\n\n\nAdd `argh` to your CMake-project by using\n```cmake\nfind_package(argh)\n```\nThe package exports `argh` *INTERFACE* library target and `argh_INCLUDE_DIR` variable. Make `argh.h` known to your compiler by using one of the following methods; both will make the location of `argh.h` known to the compiler, not link in a precompiled library - even when using `target_link_libraries()`.\n```cmake\ntarget_include_directories(${MY_TARGET_NAME} PRIVATE \"${argh_INCLUDE_DIR}\")\n#OR\ntarget_link_libraries(${MY_TARGET_NAME} argh)\n\n```\n\n\n### Additional Build Systems\n\u003cdetails\u003e\n    \u003csummary\u003e\u003cb\u003eBuck\u003c/b\u003e\u003c/summary\u003e\n\n\u003ctable\u003e\n\u003ctd\u003e  \n    \n[Buck](https://buckbuild.com/) support: \n\nRun the example: \n\n```bash=\nbuck run :example\n```\n\nRun the tests: \n\n```bash=\nbuck run :tests\nbuck run test_package\n```\nIf you take `argh` as a submodule, then the visible target is `//:argh`. \n\u003c/td\u003e\n\u003c/table\u003e\n\n    \n\u003c/details\u003e\n\n## Colophon\n\nI ❤ your feedback.  \nIf you found Argh! useful - do Tweet about it to let [me](https://twitter.com/AdiShavit) know.  \nIf you found it lacking, please post an [issue](https://github.com/adishavit/argh/issues).\n","funding_links":[],"categories":["CLI","C++","Uncategorized","Argument Parsers","cli","[C++](https://www.cpp-lang.net/)"],"sub_categories":["Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadishavit%2Fargh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadishavit%2Fargh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadishavit%2Fargh/lists"}