Skip to content

Commit

Permalink
Updated README and added simplified example
Browse files Browse the repository at this point in the history
  • Loading branch information
opokatech committed May 5, 2024
1 parent f9f5aba commit 7fb2c88
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 30 deletions.
8 changes: 4 additions & 4 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@
]
},
{
"name": "Example call (gdb)",
"name": "Example full call (gdb)",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build_example_debug/example",
"program": "${workspaceFolder}/build_example_debug/example_full",
"args": ["-c", "some_config", "-v", "-d", "5.12"],
"stopAtEntry": false,
"cwd": "${fileDirname}",
Expand All @@ -52,10 +52,10 @@
]
},
{
"name": "Example call (lldb)",
"name": "Example full call (lldb)",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build_example_debug/example",
"program": "${workspaceFolder}/build_example_debug/example_full",
"args": ["-c", "some_config", "-v", "-d", "5.12"],
"stopAtEntry": false,
"cwd": "${fileDirname}",
Expand Down
13 changes: 12 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,21 @@
"build_*/**": true
},
"cSpell.words": [
"boolalpha",
"bugprone",
"Constexpr",
"cout",
"cppcoreguidelines",
"cstdint",
"endl",
"iostream",
"positionals",
"Struct"
]
],
"files.associations": {
".clang-tidy": "yaml",
".clang-format": "yaml",
"*.yml": "yaml",
"ostream": "cpp"
}
}
110 changes: 93 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,114 @@
[![Cpp Standard](https://img.shields.io/badge/C%2B%2B-11-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B11)
[![Coverage Status](https://coveralls.io/repos/github/opokatech/options/badge.svg?branch=master)](https://coveralls.io/github/opokatech/options?branch=master)

This is yet another alternative for parsing command line options in C++.
This is yet another alternative for parsing command line arguments in C++.

The main design criteria were:

* fast to recompile,
* ease of declaring options and accessing them,
* only C++11 or higher
* Fast to recompile the main. The library has very small footprint as regards to implicitly included headers. The main using this library includes indirectly only `string` and `cstdint`.
* Easily allows validation of passed values.
* Ease of declaring options and accessing them.
* Requires C++11 or higher.

An example with comments can be found in [src/example/example.cpp](src/example/example.cpp).
## How to use it

Some notes:
A [simple example](src/example/example_simple.cpp), without validation looks as follows:

* an option may have an argument or not. An option without an argument is a flag,
* an option is always identified by a long name. A long name is used with 2 dashes in front of it,
like so `--something`. It may have a single character short version which is then used with a
single dash in front of it, like so `-s`.
```cpp
#include <iostream>

* an option can be mandatory - which means it must be specified,
* it is **not** checked if the defined options are unique,
* it is **not** checked if the default value passes the validation (if used).
#include "options/Parser.hpp"

int main(int argc, char *argv[])
{
Options::Parser args_parser;

args_parser.add_mandatory("config", 'c', "Configuration file"); // --config, -c
args_parser.add_optional("count", "Number of iterations", "10"); // --count
args_parser.add_optional("threshold", 't', "Some threshold", "3.14"); // --threshold, -t
args_parser.add_flag("help", 'h', "This help is accessible via short and long option");

using std::cout;
using std::endl;

if (!args_parser.parse(argc, argv) || args_parser.as_bool("help"))
{
cout << "Usage: " << argv[0] << " [options] [-- [positional arguments]]" << endl;
cout << args_parser.get_possible_options() << endl;
return -1;
}

// Print all options
cout << std::boolalpha;
cout << "Options:" << endl;
cout << " config : " << args_parser.as_string("config") << endl;
cout << " count : " << args_parser.as_int("count") << endl;
cout << " threshold : " << args_parser.as_double("threshold") << endl;

// positional arguments are all the strings after "--" separator, for example:
// ./example_simple -c config_file.txt -- these strings are positional arguments
if (args_parser.positional_count() > 0)
{
cout << "Positional parameters:" << endl;
for (size_t i = 0; i < args_parser.positional_count(); ++i)
cout << " [" << i << "]: " << args_parser.positional(i) << endl;
}
else
cout << "No positional parameters." << endl;

return 0;
}
```
An example "session" on the console may look like this:
```bash
./build_example_debug/example_simple
Usage: ./build_example_debug/example_simple [options] [-- [positional arguments]]
-c, --config M Configuration file
--count Number of iterations (default: 10)
-t, --threshold Some threshold (default: 3.14)
-h, --help This help is accessible via short and long option
## Usage
./build_example_debug/example_simple -c some_file.cfg -- this is a string
Options:
config : some_file.cfg
count : 10
threshold : 3.14
Positional parameters:
[0]: this
[1]: is
[2]: a
[3]: string
```

A similar example, but with validation of data can be found [here](src/example/example_full.cpp).

## How to compile it

This library can be used in a few ways:

A simplest way is to copy `src/options` directory to your project and add the files in there to the compilation process either by hand OR by including only this directory via `add_subdirectory`:
A simplest and not recommended way is to copy `src/options` directory to your project and add the files in there to the compilation process either by hand OR by including only this directory via `add_subdirectory`. After all the whole library is just 7 files.

Another option is to clone the whole repository and add it via `add_subdirectory`:

```cmake
add_subdirectory(external/options) # location of cloned repository
add_subdirectory(external/options) # location of the cloned repository
add_executable(example example.cpp)
add_executable(example my_main.cpp)
target_link_libraries(example PRIVATE options)
```

## Some notes:

* an option may have an argument or not. An option without an argument is a flag,
* an option is always identified by a long name. A long name is used with 2 dashes in front of it,
like so `--something`. It may have a single character short version which is then used with a
single dash in front of it, like so `-s`.

* an option can be mandatory - which means it must be specified,
* it is **not** checked if the defined options are unique,
* it is **not** checked if the default value passes the validation (if used).
7 changes: 5 additions & 2 deletions src/example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
add_executable(example example.cpp)
target_link_libraries(example PRIVATE options options_compile_flags)
add_executable(example_simple example_simple.cpp)
target_link_libraries(example_simple PRIVATE options options_compile_flags)

add_executable(example_full example_full.cpp)
target_link_libraries(example_full PRIVATE options options_compile_flags)
12 changes: 6 additions & 6 deletions src/example/example.cpp → src/example/example_full.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@ int main(int argc, char *argv[])
{
Options::Parser args_parser;

args_parser.add_flag("help", 'h', "This help is accessible via short and long option");
args_parser.add_flag("verbose", 'v', "Verbose - accessible via -v and --verbose");
args_parser.add_mandatory("config", 'c', "Configuration file");
args_parser.add_optional(
"level", "Debug level, one of none, debug, error - it is checked by the validator", "none",
[](const std::string &value) { return (value == "none" || value == "debug" || value == "error"); });

args_parser.add_mandatory("config", 'c', "Configuration file");
args_parser.add_optional("int", 'i', "Some small integer in range <-10..10> as checked by validator", "4",
[](const std::string &value) {
int32_t num = Options::as_int(value);
Expand All @@ -26,6 +24,8 @@ int main(int argc, char *argv[])

args_parser.add_optional("bf", "Boolean value", "false");
args_parser.add_optional("bt", "Boolean value", "true");
args_parser.add_flag("help", 'h', "This help is accessible via short and long option");
args_parser.add_flag("verbose", 'v', "Verbose - accessible via -v and --verbose");

using std::cout;
using std::endl;
Expand All @@ -39,16 +39,16 @@ int main(int argc, char *argv[])

cout << std::boolalpha;
cout << "Options:" << endl;
cout << " verbose : " << args_parser.as_bool("verbose") << endl;
cout << " level : " << args_parser.as_string("level") << endl;
cout << " config : " << args_parser.as_string("config") << endl;
cout << " level : " << args_parser.as_string("level") << endl;
cout << " int : " << args_parser.as_int("int") << endl;
cout << " double : " << args_parser.as_double("double") << endl;
cout << " bf : " << args_parser.as_bool("bf") << endl;
cout << " bt : " << args_parser.as_bool("bt") << endl;
cout << " verbose : " << args_parser.as_bool("verbose") << endl;

// positional arguments are all the strings after "--" separator, for example:
// ./example -c config_file.txt -v -- these strings are positional arguments
// ./example_full -c config_file.txt -v -- these strings are positional arguments
if (args_parser.positional_count() > 0)
{
cout << "Positional parameters:" << endl;
Expand Down
43 changes: 43 additions & 0 deletions src/example/example_simple.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include <iostream>

#include "options/Parser.hpp"

int main(int argc, char *argv[])
{
Options::Parser args_parser;

args_parser.add_mandatory("config", 'c', "Configuration file"); // --config, -c
args_parser.add_optional("count", "Number of iterations", "10"); // --count
args_parser.add_optional("threshold", 't', "Some threshold", "3.14"); // --threshold, -t
args_parser.add_flag("help", 'h', "This help is accessible via short and long option");

using std::cout;
using std::endl;

if (!args_parser.parse(argc, argv) || args_parser.as_bool("help"))
{
cout << "Usage: " << argv[0] << " [options] [-- [positional arguments]]" << endl;
cout << args_parser.get_possible_options() << endl;
return -1;
}

// Print all options
cout << std::boolalpha;
cout << "Options:" << endl;
cout << " config : " << args_parser.as_string("config") << endl;
cout << " count : " << args_parser.as_int("count") << endl;
cout << " threshold : " << args_parser.as_double("threshold") << endl;

// positional arguments are all the strings after "--" separator, for example:
// ./example_simple -c config_file.txt -- these strings are positional arguments
if (args_parser.positional_count() > 0)
{
cout << "Positional parameters:" << endl;
for (size_t i = 0; i < args_parser.positional_count(); ++i)
cout << " [" << i << "]: " << args_parser.positional(i) << endl;
}
else
cout << "No positional parameters." << endl;

return 0;
}

0 comments on commit 7fb2c88

Please sign in to comment.