@ -1 +1 @@ | |||||
Subproject commit f44df4b7cc602875602e9eeecab28b9a58ead097 | |||||
Subproject commit 03dda20c2bfc481313ba446e34e4917dad0ec0bd |
@ -0,0 +1,8 @@ | |||||
*.swp | |||||
build* | |||||
CMakeCache.txt | |||||
Makefile | |||||
CMakeFiles/ | |||||
Testing/ | |||||
CTestTestfile.cmake | |||||
cmake_install.cmake |
@ -0,0 +1,70 @@ | |||||
sudo: required | |||||
dist: trusty | |||||
language: cpp | |||||
os: | |||||
- linux | |||||
matrix: | |||||
include: | |||||
- os: linux | |||||
env: COMPILER=g++-4.9 | |||||
addons: | |||||
apt: | |||||
packages: | |||||
- g++-4.9 | |||||
sources: &sources | |||||
- llvm-toolchain-trusty-3.8 | |||||
- llvm-toolchain-trusty-5.0 | |||||
- ubuntu-toolchain-r-test | |||||
- os: linux | |||||
env: COMPILER=g++-4.9 UNICODE_OPTIONS=-DCXXOPTS_USE_UNICODE_HELP=Yes | |||||
addons: | |||||
apt: | |||||
packages: | |||||
- g++-4.9 | |||||
sources: *sources | |||||
- os: linux | |||||
env: COMPILER=g++-5 | |||||
addons: | |||||
apt: | |||||
packages: | |||||
- g++-5 | |||||
sources: *sources | |||||
- os: linux | |||||
env: COMPILER=g++-5 UNICODE_OPTIONS=-DCXXOPTS_USE_UNICODE_HELP=Yes | |||||
addons: | |||||
apt: | |||||
packages: | |||||
- g++-5 | |||||
sources: *sources | |||||
- os: linux | |||||
env: COMPILER=clang++-3.8 CXXFLAGS=-stdlib=libc++ | |||||
addons: | |||||
apt: | |||||
packages: | |||||
- clang-3.8 | |||||
- libc++-dev | |||||
sources: *sources | |||||
- os: linux | |||||
env: COMPILER=clang++-3.8 CXXFLAGS=-stdlib=libc++ UNICODE_OPTIONS=-DCXXOPTS_USE_UNICODE_HELP=Yes | |||||
addons: | |||||
apt: | |||||
packages: | |||||
- clang-3.8 | |||||
- libc++-dev | |||||
sources: *sources | |||||
- os: linux | |||||
env: COMPILER=clang++-5.0 CMAKE_OPTIONS=-DCXXOPTS_CXX_STANDARD=17 | |||||
addons: | |||||
apt: | |||||
packages: | |||||
- clang-5.0 | |||||
- g++-5 | |||||
sources: *sources | |||||
script: > | |||||
cmake -DCXXOPTS_BUILD_TESTS=ON -DCMAKE_CXX_COMPILER=$COMPILER | |||||
-DCMAKE_CXX_FLAGS=$CXXFLAGS $UNICODE_OPTIONS $CMAKE_OPTIONS . | |||||
&& make && make ARGS=--output-on-failure test | |||||
before_install: | |||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi | |||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install icu4c; fi |
@ -0,0 +1,97 @@ | |||||
# Changelog | |||||
This is the changelog for `cxxopts`, a C++11 library for parsing command line | |||||
options. The project adheres to semantic versioning. | |||||
## Next version | |||||
### Changed | |||||
* Only search for a C++ compiler in CMakeLists.txt. | |||||
* Allow for exceptions to be disabled. | |||||
* Fix duplicate default options when there is a short and long option. | |||||
* Add `CXXOPTS_NO_EXCEPTIONS` to disable exceptions. | |||||
* Fix char parsing for space and check for length. | |||||
## 2.2 | |||||
### Changed | |||||
* Allow integers to have leading zeroes. | |||||
* Build the tests by default. | |||||
* Don't check for container when showing positional help. | |||||
### Added | |||||
* Iterator inputs to `parse_positional`. | |||||
* Throw an exception if the option in `parse_positional` doesn't exist. | |||||
* Parse a delimited list in a single argument for vector options. | |||||
* Add an option to disable implicit value on booleans. | |||||
### Bug Fixes | |||||
* Fix a warning about possible loss of data. | |||||
* Fix version numbering in CMakeLists.txt | |||||
* Remove unused declaration of the undefined `ParseResult::get_option`. | |||||
* Throw on invalid option syntax when beginning with a `-`. | |||||
* Throw in `as` when option wasn't present. | |||||
* Fix catching exceptions by reference. | |||||
* Fix out of bounds errors parsing integers. | |||||
## 2.1.1 | |||||
### Bug Fixes | |||||
* Revert the change adding `const` type for `argv`, because most users expect | |||||
to pass a non-const `argv` from `main`. | |||||
## 2.1 | |||||
### Changed | |||||
* Options with implicit arguments now require the `--option=value` form if | |||||
they are to be specified with an option. This is to remove the ambiguity | |||||
when a positional argument could follow an option with an implicit value. | |||||
For example, `--foo value`, where `foo` has an implicit value, will be | |||||
parsed as `--foo=implicit` and a positional argument `value`. | |||||
* Boolean values are no longer special, but are just an option with a default | |||||
and implicit value. | |||||
### Added | |||||
* Added support for `std::optional` as a storage type. | |||||
* Allow the help string to be customised. | |||||
* Use `const` for the type in the `argv` parameter, since the contents of the | |||||
arguments is never modified. | |||||
### Bug Fixes | |||||
* Building against GCC 4.9 was broken due to overly strict shadow warnings. | |||||
* Fixed an ambiguous overload in the `parse_positional` function when an | |||||
`initializer_list` was directly passed. | |||||
* Fixed precedence in the Boolean value regex. | |||||
## 2.0 | |||||
### Changed | |||||
* `Options::parse` returns a ParseResult rather than storing the parse | |||||
result internally. | |||||
* Options with default values now get counted as appearing once if they | |||||
were not specified by the user. | |||||
### Added | |||||
* A new `ParseResult` object that is the immutable result of parsing. It | |||||
responds to the same `count` and `operator[]` as `Options` of 1.x did. | |||||
* The function `ParseResult::arguments` returns a vector of the parsed | |||||
arguments to iterate through in the order they were provided. | |||||
* The symbol `cxxopts::version` for the version of the library. | |||||
* Booleans can be specified with various strings and explicitly set false. | |||||
## 1.x | |||||
The 1.x series was the first major version of the library, with release numbers | |||||
starting to follow semantic versioning, after 0.x being unstable. It never had | |||||
a changelog maintained for it. Releases mostly contained bug fixes, with the | |||||
occasional feature added. |
@ -0,0 +1,108 @@ | |||||
# Copyright (c) 2014 Jarryd Beck | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
# of this software and associated documentation files (the "Software"), to deal | |||||
# in the Software without restriction, including without limitation the rights | |||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
# copies of the Software, and to permit persons to whom the Software is | |||||
# furnished to do so, subject to the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included in | |||||
# all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
# THE SOFTWARE. | |||||
cmake_minimum_required(VERSION 3.1) | |||||
# parse the current version from the cxxopts header | |||||
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/cxxopts.hpp" cxxopts_version_defines | |||||
REGEX "#define CXXOPTS__VERSION_(MAJOR|MINOR|PATCH)") | |||||
foreach(ver ${cxxopts_version_defines}) | |||||
if(ver MATCHES "#define CXXOPTS__VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$") | |||||
set(CXXOPTS__VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" CACHE INTERNAL "") | |||||
endif() | |||||
endforeach() | |||||
set(VERSION ${CXXOPTS__VERSION_MAJOR}.${CXXOPTS__VERSION_MINOR}.${CXXOPTS__VERSION_PATCH}) | |||||
message(STATUS "cxxopts version ${VERSION}") | |||||
project(cxxopts VERSION "${VERSION}" LANGUAGES CXX) | |||||
enable_testing() | |||||
option(CXXOPTS_BUILD_EXAMPLES "Set to ON to build examples" ON) | |||||
option(CXXOPTS_BUILD_TESTS "Set to ON to build tests" ON) | |||||
option(CXXOPTS_ENABLE_INSTALL "Generate the install target" ON) | |||||
# request c++11 without gnu extension for the whole project and enable more warnings | |||||
if (CXXOPTS_CXX_STANDARD) | |||||
set(CMAKE_CXX_STANDARD ${CXXOPTS_CXX_STANDARD}) | |||||
else() | |||||
set(CMAKE_CXX_STANDARD 11) | |||||
endif() | |||||
set(CMAKE_CXX_EXTENSIONS OFF) | |||||
if(MSVC) | |||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W2") | |||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "[Cc]lang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") | |||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Wextra -Wshadow") | |||||
endif() | |||||
add_library(cxxopts INTERFACE) | |||||
add_library(cxxopts::cxxopts ALIAS cxxopts) | |||||
# optionally, enable unicode support using the ICU library | |||||
set(CXXOPTS_USE_UNICODE_HELP FALSE CACHE BOOL "Use ICU Unicode library") | |||||
if(CXXOPTS_USE_UNICODE_HELP) | |||||
find_package(PkgConfig) | |||||
pkg_check_modules(ICU REQUIRED icu-uc) | |||||
target_link_libraries(cxxopts INTERFACE ${ICU_LDFLAGS}) | |||||
target_compile_options(cxxopts INTERFACE ${ICU_CFLAGS}) | |||||
target_compile_definitions(cxxopts INTERFACE CXXOPTS_USE_UNICODE) | |||||
endif() | |||||
target_include_directories(cxxopts INTERFACE | |||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> | |||||
$<INSTALL_INTERFACE:include> | |||||
) | |||||
if(CXXOPTS_ENABLE_INSTALL) | |||||
include(CMakePackageConfigHelpers) | |||||
set(CXXOPTS_CMAKE_DIR "lib/cmake/cxxopts" CACHE STRING | |||||
"Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.") | |||||
set(version_config "${PROJECT_BINARY_DIR}/cxxopts-config-version.cmake") | |||||
set(project_config "${PROJECT_BINARY_DIR}/cxxopts-config.cmake") | |||||
set(targets_export_name cxxopts-targets) | |||||
# Generate the version, config and target files into the build directory. | |||||
write_basic_package_version_file( | |||||
${version_config} | |||||
VERSION ${VERSION} | |||||
COMPATIBILITY AnyNewerVersion) | |||||
configure_package_config_file( | |||||
${PROJECT_SOURCE_DIR}/cxxopts-config.cmake.in | |||||
${project_config} | |||||
INSTALL_DESTINATION ${CXXOPTS_CMAKE_DIR}) | |||||
export(TARGETS cxxopts NAMESPACE cxxopts:: | |||||
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake) | |||||
# Install version, config and target files. | |||||
install( | |||||
FILES ${project_config} ${version_config} | |||||
DESTINATION ${CXXOPTS_CMAKE_DIR}) | |||||
install(EXPORT ${targets_export_name} DESTINATION ${CXXOPTS_CMAKE_DIR} | |||||
NAMESPACE cxxopts::) | |||||
# Install the header file and export the target | |||||
install(TARGETS cxxopts EXPORT ${targets_export_name} DESTINATION lib) | |||||
install(FILES ${PROJECT_SOURCE_DIR}/include/cxxopts.hpp DESTINATION include) | |||||
endif() | |||||
add_subdirectory(src) | |||||
add_subdirectory(test) |
@ -0,0 +1,23 @@ | |||||
== System installation == | |||||
This library is header only. So you can either copy `include/cxxopts.hpp` to `/usr/include` or `/usr/local/include`, or add `include` to your search path. | |||||
== Building the examples and tests == | |||||
It is preferable to build out of source. Make a build directory somewhere, and then | |||||
do the following, where `${CXXOPTS_DIR}` is the path that you checked out `cxxopts` | |||||
to: | |||||
cmake ${CXXOPTS_DIR} | |||||
make | |||||
You can use another build tool, such as ninja. | |||||
cmake -G Ninja ${CXXOPTS_DIR} | |||||
ninja | |||||
To run the tests, you have to configure `cxxopts` with another flag: | |||||
cmake -D CXXOPTS_BUILD_TESTS=On ${CXXOPTS_DIR} | |||||
make | |||||
make test |
@ -0,0 +1,19 @@ | |||||
Copyright (c) 2014 Jarryd Beck | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in | |||||
all copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
THE SOFTWARE. |
@ -0,0 +1,224 @@ | |||||
[![Build Status](https://travis-ci.org/jarro2783/cxxopts.svg?branch=master)](https://travis-ci.org/jarro2783/cxxopts) | |||||
# Release versions | |||||
Note that `master` is generally a work in progress, and you probably want to use a | |||||
tagged release version. | |||||
# Quick start | |||||
This is a lightweight C++ option parser library, supporting the standard GNU | |||||
style syntax for options. | |||||
Options can be given as: | |||||
--long | |||||
--long=argument | |||||
--long argument | |||||
-a | |||||
-ab | |||||
-abc argument | |||||
where c takes an argument, but a and b do not. | |||||
Additionally, anything after `--` will be parsed as a positional argument. | |||||
## Basics | |||||
```cpp | |||||
#include <cxxopts.hpp> | |||||
``` | |||||
Create a `cxxopts::Options` instance. | |||||
```cpp | |||||
cxxopts::Options options("MyProgram", "One line description of MyProgram"); | |||||
``` | |||||
Then use `add_options`. | |||||
```cpp | |||||
options.add_options() | |||||
("d,debug", "Enable debugging") // a bool parameter | |||||
("i,integer", "Int param", cxxopts::value<int>()) | |||||
("f,file", "File name", cxxopts::value<std::string>()) | |||||
("v,verbose", "Verbose output", cxxopts::value<bool>()->default_value("false")) | |||||
; | |||||
``` | |||||
Options are declared with a long and an optional short option. A description | |||||
must be provided. The third argument is the value, if omitted it is boolean. | |||||
Any type can be given as long as it can be parsed, with operator>>. | |||||
To parse the command line do: | |||||
```cpp | |||||
auto result = options.parse(argc, argv); | |||||
``` | |||||
To retrieve an option use `result.count("option")` to get the number of times | |||||
it appeared, and | |||||
```cpp | |||||
result["opt"].as<type>() | |||||
``` | |||||
to get its value. If "opt" doesn't exist, or isn't of the right type, then an | |||||
exception will be thrown. | |||||
Note that the result of `options.parse` should only be used as long as the | |||||
`options` object that created it is in scope. | |||||
## Exceptions | |||||
Exceptional situations throw C++ exceptions. There are two types of | |||||
exceptions: errors defining the options, and errors when parsing a list of | |||||
arguments. All exceptions derive from `cxxopts::OptionException`. Errors | |||||
defining options derive from `cxxopts::OptionSpecException` and errors | |||||
parsing arguments derive from `cxxopts::OptionParseException`. | |||||
All exceptions define a `what()` function to get a printable string | |||||
explaining the error. | |||||
## Help groups | |||||
Options can be placed into groups for the purposes of displaying help messages. | |||||
To place options in a group, pass the group as a string to `add_options`. Then, | |||||
when displaying the help, pass the groups that you would like displayed as a | |||||
vector to the `help` function. | |||||
## Positional Arguments | |||||
Positional arguments can be optionally parsed into one or more options. | |||||
To set up positional arguments, call | |||||
```cpp | |||||
options.parse_positional({"first", "second", "last"}) | |||||
``` | |||||
where "last" should be the name of an option with a container type, and the | |||||
others should have a single value. | |||||
## Default and implicit values | |||||
An option can be declared with a default or an implicit value, or both. | |||||
A default value is the value that an option takes when it is not specified | |||||
on the command line. The following specifies a default value for an option: | |||||
```cpp | |||||
cxxopts::value<std::string>()->default_value("value") | |||||
``` | |||||
An implicit value is the value that an option takes when it is given on the | |||||
command line without an argument. The following specifies an implicit value: | |||||
```cpp | |||||
cxxopts::value<std::string>()->implicit_value("implicit") | |||||
``` | |||||
If an option had both, then not specifying it would give the value `"value"`, | |||||
writing it on the command line as `--option` would give the value `"implicit"`, | |||||
and writing `--option=another` would give it the value `"another"`. | |||||
Note that the default and implicit value is always stored as a string, | |||||
regardless of the type that you want to store it in. It will be parsed as | |||||
though it was given on the command line. | |||||
## Boolean values | |||||
Boolean options have a default implicit value of `"true"`, which can be | |||||
overridden. The effect is that writing `-o` by itself will set option `o` to | |||||
`true`. However, they can also be written with various strings using `=value`. | |||||
There is no way to disambiguate positional arguments from the value following | |||||
a boolean, so we have chosen that they will be positional arguments, and | |||||
therefore, `-o false` does not work. | |||||
## `std::vector<T>` values | |||||
Parsing of list of values in form of an `std::vector<T>` is also supported, as long as `T` | |||||
can be parsed. To separate single values in a list the definition `CXXOPTS_VECTOR_DELIMITER` | |||||
is used, which is ',' by default. Ensure that you use no whitespaces between values because | |||||
those would be interpreted as the next command line option. Example for a command line option | |||||
that can be parsed as a `std::vector<double>`: | |||||
~~~ | |||||
--my_list=1,-2.1,3,4.5 | |||||
~~~ | |||||
## Options specified multiple times | |||||
The same option can be specified several times, with different arguments, which will all | |||||
be recorded in order of appearance. An example: | |||||
~~~ | |||||
--use train --use bus --use ferry | |||||
~~~ | |||||
this is supported through the use of a vector of value for the option: | |||||
~~~ | |||||
options.add_options() | |||||
("use", "Usable means of transport", cxxopts::value<std::vector<std::string>>()) | |||||
~~~ | |||||
## Custom help | |||||
The string after the program name on the first line of the help can be | |||||
completely replaced by calling `options.custom_help`. Note that you might | |||||
also want to override the positional help by calling `options.positional_help`. | |||||
## Example | |||||
Putting all together: | |||||
```cpp | |||||
int main(int argc, char** argv) | |||||
{ | |||||
cxxopts::Options options("test", "A brief description"); | |||||
options.add_options() | |||||
("b,bar", "Param bar", cxxopts::value<std::string>()) | |||||
("d,debug", "Enable debugging", cxxopts::value<bool>()->default_value("false")) | |||||
("f,foo", "Param foo", cxxopts::value<int>()->default_value("10")) | |||||
("h,help", "Print usage") | |||||
; | |||||
auto result = options.parse(argc, argv); | |||||
if (result.count("help")) | |||||
{ | |||||
std::cout << options.help() << std::endl; | |||||
exit(0); | |||||
} | |||||
bool debug = result["debug"].as<bool>(); | |||||
std::string bar; | |||||
if (result.count("bar")) | |||||
bar = result["bar"].as<std::string>(); | |||||
int foo = result["foo"].as<int>(); | |||||
return 0; | |||||
} | |||||
``` | |||||
# Linking | |||||
This is a header only library. | |||||
# Requirements | |||||
The only build requirement is a C++ compiler that supports C++11 features such as: | |||||
* regex | |||||
* constexpr | |||||
* default constructors | |||||
GCC >= 4.9 or clang >= 3.1 with libc++ are known to work. | |||||
The following compilers are known not to work: | |||||
* MSVC 2013 | |||||
# TODO list | |||||
* Allow unrecognised options. |
@ -0,0 +1,4 @@ | |||||
@PACKAGE_INIT@ | |||||
include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake) | |||||
check_required_components(cxxopts) |
@ -0,0 +1 @@ | |||||
example |
@ -0,0 +1,24 @@ | |||||
# Copyright (c) 2014 Jarryd Beck | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
# of this software and associated documentation files (the "Software"), to deal | |||||
# in the Software without restriction, including without limitation the rights | |||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
# copies of the Software, and to permit persons to whom the Software is | |||||
# furnished to do so, subject to the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included in | |||||
# all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
# THE SOFTWARE. | |||||
if(CXXOPTS_BUILD_EXAMPLES) | |||||
add_executable(example example.cpp) | |||||
target_link_libraries(example cxxopts) | |||||
endif() |
@ -0,0 +1,168 @@ | |||||
/* | |||||
Copyright (c) 2014 Jarryd Beck | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in | |||||
all copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
THE SOFTWARE. | |||||
*/ | |||||
#include <iostream> | |||||
#include "cxxopts.hpp" | |||||
void | |||||
parse(int argc, char* argv[]) | |||||
{ | |||||
try | |||||
{ | |||||
cxxopts::Options options(argv[0], " - example command line options"); | |||||
options | |||||
.positional_help("[optional args]") | |||||
.show_positional_help(); | |||||
bool apple = false; | |||||
options | |||||
.allow_unrecognised_options() | |||||
.add_options() | |||||
("a,apple", "an apple", cxxopts::value<bool>(apple)) | |||||
("b,bob", "Bob") | |||||
("char", "A character", cxxopts::value<char>()) | |||||
("t,true", "True", cxxopts::value<bool>()->default_value("true")) | |||||
("f, file", "File", cxxopts::value<std::vector<std::string>>(), "FILE") | |||||
("i,input", "Input", cxxopts::value<std::string>()) | |||||
("o,output", "Output file", cxxopts::value<std::string>() | |||||
->default_value("a.out")->implicit_value("b.def"), "BIN") | |||||
("x", "A short-only option", cxxopts::value<std::string>()) | |||||
("positional", | |||||
"Positional arguments: these are the arguments that are entered " | |||||
"without an option", cxxopts::value<std::vector<std::string>>()) | |||||
("long-description", | |||||
"thisisareallylongwordthattakesupthewholelineandcannotbebrokenataspace") | |||||
("help", "Print help") | |||||
("int", "An integer", cxxopts::value<int>(), "N") | |||||
("float", "A floating point number", cxxopts::value<float>()) | |||||
("vector", "A list of doubles", cxxopts::value<std::vector<double>>()) | |||||
("option_that_is_too_long_for_the_help", "A very long option") | |||||
#ifdef CXXOPTS_USE_UNICODE | |||||
("unicode", u8"A help option with non-ascii: à. Here the size of the" | |||||
" string should be correct") | |||||
#endif | |||||
; | |||||
options.add_options("Group") | |||||
("c,compile", "compile") | |||||
("d,drop", "drop", cxxopts::value<std::vector<std::string>>()); | |||||
options.parse_positional({"input", "output", "positional"}); | |||||
auto result = options.parse(argc, argv); | |||||
if (result.count("help")) | |||||
{ | |||||
std::cout << options.help({"", "Group"}) << std::endl; | |||||
exit(0); | |||||
} | |||||
if (apple) | |||||
{ | |||||
std::cout << "Saw option ‘a’ " << result.count("a") << " times " << | |||||
std::endl; | |||||
} | |||||
if (result.count("b")) | |||||
{ | |||||
std::cout << "Saw option ‘b’" << std::endl; | |||||
} | |||||
if (result.count("char")) | |||||
{ | |||||
std::cout << "Saw a character ‘" << result["char"].as<char>() << "’" << std::endl; | |||||
} | |||||
if (result.count("f")) | |||||
{ | |||||
auto& ff = result["f"].as<std::vector<std::string>>(); | |||||
std::cout << "Files" << std::endl; | |||||
for (const auto& f : ff) | |||||
{ | |||||
std::cout << f << std::endl; | |||||
} | |||||
} | |||||
if (result.count("input")) | |||||
{ | |||||
std::cout << "Input = " << result["input"].as<std::string>() | |||||
<< std::endl; | |||||
} | |||||
if (result.count("output")) | |||||
{ | |||||
std::cout << "Output = " << result["output"].as<std::string>() | |||||
<< std::endl; | |||||
} | |||||
if (result.count("positional")) | |||||
{ | |||||
std::cout << "Positional = {"; | |||||
auto& v = result["positional"].as<std::vector<std::string>>(); | |||||
for (const auto& s : v) { | |||||
std::cout << s << ", "; | |||||
} | |||||
std::cout << "}" << std::endl; | |||||
} | |||||
if (result.count("int")) | |||||
{ | |||||
std::cout << "int = " << result["int"].as<int>() << std::endl; | |||||
} | |||||
if (result.count("float")) | |||||
{ | |||||
std::cout << "float = " << result["float"].as<float>() << std::endl; | |||||
} | |||||
if (result.count("vector")) | |||||
{ | |||||
std::cout << "vector = "; | |||||
const auto values = result["vector"].as<std::vector<double>>(); | |||||
for (const auto& v : values) { | |||||
std::cout << v << ", "; | |||||
} | |||||
std::cout << std::endl; | |||||
} | |||||
std::cout << "Arguments remain = " << argc << std::endl; | |||||
auto arguments = result.arguments(); | |||||
std::cout << "Saw " << arguments.size() << " arguments" << std::endl; | |||||
} | |||||
catch (const cxxopts::OptionException& e) | |||||
{ | |||||
std::cout << "error parsing options: " << e.what() << std::endl; | |||||
exit(1); | |||||
} | |||||
} | |||||
int main(int argc, char* argv[]) | |||||
{ | |||||
parse(argc, argv); | |||||
return 0; | |||||
} |
@ -0,0 +1 @@ | |||||
options_test |
@ -0,0 +1,35 @@ | |||||
if (CXXOPTS_BUILD_TESTS) | |||||
add_executable(options_test main.cpp options.cpp) | |||||
target_link_libraries(options_test cxxopts) | |||||
add_test(options options_test) | |||||
# test if the targets are findable from the build directory | |||||
add_test(find-package-test ${CMAKE_CTEST_COMMAND} | |||||
-C ${CMAKE_BUILD_TYPE} | |||||
--build-and-test | |||||
"${CMAKE_CURRENT_SOURCE_DIR}/find-package-test" | |||||
"${CMAKE_CURRENT_BINARY_DIR}/find-package-test" | |||||
--build-generator ${CMAKE_GENERATOR} | |||||
--build-makeprogram ${CMAKE_MAKE_PROGRAM} | |||||
--build-options | |||||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" | |||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" | |||||
"-Dcxxopts_DIR=${PROJECT_BINARY_DIR}" | |||||
) | |||||
# test if the targets are findable when add_subdirectory is used | |||||
add_test(add-subdirectory-test ${CMAKE_CTEST_COMMAND} | |||||
-C ${CMAKE_BUILD_TYPE} | |||||
--build-and-test | |||||
"${CMAKE_CURRENT_SOURCE_DIR}/add-subdirectory-test" | |||||
"${CMAKE_CURRENT_BINARY_DIR}/add-subdirectory-test" | |||||
--build-generator ${CMAKE_GENERATOR} | |||||
--build-makeprogram ${CMAKE_MAKE_PROGRAM} | |||||
--build-options | |||||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" | |||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" | |||||
) | |||||
add_executable(link_test link_a.cpp link_b.cpp) | |||||
target_link_libraries(link_test cxxopts) | |||||
endif() |
@ -0,0 +1,11 @@ | |||||
cmake_minimum_required(VERSION 3.1) | |||||
project(cxxopts-test) | |||||
set(CMAKE_CXX_STANDARD 11) | |||||
set(CMAKE_CXX_EXTENSIONS OFF) | |||||
add_subdirectory(../.. cxxopts EXCLUDE_FROM_ALL) | |||||
add_executable(library-test "../../src/example.cpp") | |||||
target_link_libraries(library-test cxxopts) |
@ -0,0 +1,11 @@ | |||||
cmake_minimum_required(VERSION 3.1) | |||||
project(cxxopts-test) | |||||
set(CMAKE_CXX_STANDARD 11) | |||||
set(CMAKE_CXX_EXTENSIONS OFF) | |||||
find_package(cxxopts REQUIRED) | |||||
add_executable(library-test "../../src/example.cpp") | |||||
target_link_libraries(library-test cxxopts::cxxopts) |
@ -0,0 +1,6 @@ | |||||
#include "cxxopts.hpp" | |||||
int main(int, char**) | |||||
{ | |||||
return 0; | |||||
} |
@ -0,0 +1 @@ | |||||
#include <cxxopts.hpp> |
@ -0,0 +1,2 @@ | |||||
#define CATCH_CONFIG_MAIN | |||||
#include "catch.hpp" |
@ -0,0 +1,776 @@ | |||||
#include "catch.hpp" | |||||
#include <initializer_list> | |||||
#include "cxxopts.hpp" | |||||
class Argv { | |||||
public: | |||||
Argv(std::initializer_list<const char*> args) | |||||
: m_argv(new char*[args.size()]) | |||||
, m_argc(static_cast<int>(args.size())) | |||||
{ | |||||
int i = 0; | |||||
auto iter = args.begin(); | |||||
while (iter != args.end()) { | |||||
auto len = strlen(*iter) + 1; | |||||
auto ptr = std::unique_ptr<char[]>(new char[len]); | |||||
strcpy(ptr.get(), *iter); | |||||
m_args.push_back(std::move(ptr)); | |||||
m_argv.get()[i] = m_args.back().get(); | |||||
++iter; | |||||
++i; | |||||
} | |||||
} | |||||
char** argv() const { | |||||
return m_argv.get(); | |||||
} | |||||
int argc() const { | |||||
return m_argc; | |||||
} | |||||
private: | |||||
std::vector<std::unique_ptr<char[]>> m_args; | |||||
std::unique_ptr<char*[]> m_argv; | |||||
int m_argc; | |||||
}; | |||||
TEST_CASE("Basic options", "[options]") | |||||
{ | |||||
cxxopts::Options options("tester", " - test basic options"); | |||||
options.add_options() | |||||
("long", "a long option") | |||||
("s,short", "a short option") | |||||
("value", "an option with a value", cxxopts::value<std::string>()) | |||||
("a,av", "a short option with a value", cxxopts::value<std::string>()) | |||||
("6,six", "a short number option") | |||||
("p, space", "an option with space between short and long") | |||||
("nothing", "won't exist", cxxopts::value<std::string>()) | |||||
; | |||||
Argv argv({ | |||||
"tester", | |||||
"--long", | |||||
"-s", | |||||
"--value", | |||||
"value", | |||||
"-a", | |||||
"b", | |||||
"-6", | |||||
"-p", | |||||
"--space", | |||||
}); | |||||
char** actual_argv = argv.argv(); | |||||
auto argc = argv.argc(); | |||||
auto result = options.parse(argc, actual_argv); | |||||
CHECK(result.count("long") == 1); | |||||
CHECK(result.count("s") == 1); | |||||
CHECK(result.count("value") == 1); | |||||
CHECK(result.count("a") == 1); | |||||
CHECK(result["value"].as<std::string>() == "value"); | |||||
CHECK(result["a"].as<std::string>() == "b"); | |||||
CHECK(result.count("6") == 1); | |||||
CHECK(result.count("p") == 2); | |||||
CHECK(result.count("space") == 2); | |||||
auto& arguments = result.arguments(); | |||||
REQUIRE(arguments.size() == 7); | |||||
CHECK(arguments[0].key() == "long"); | |||||
CHECK(arguments[0].value() == "true"); | |||||
CHECK(arguments[0].as<bool>() == true); | |||||
CHECK(arguments[1].key() == "short"); | |||||
CHECK(arguments[2].key() == "value"); | |||||
CHECK(arguments[3].key() == "av"); | |||||
CHECK_THROWS_AS(result["nothing"].as<std::string>(), cxxopts::option_has_no_value_exception&); | |||||
} | |||||
TEST_CASE("Short options", "[options]") | |||||
{ | |||||
cxxopts::Options options("test_short", " - test short options"); | |||||
options.add_options() | |||||
("a", "a short option", cxxopts::value<std::string>()); | |||||
Argv argv({"test_short", "-a", "value"}); | |||||
auto actual_argv = argv.argv(); | |||||
auto argc = argv.argc(); | |||||
auto result = options.parse(argc, actual_argv); | |||||
CHECK(result.count("a") == 1); | |||||
CHECK(result["a"].as<std::string>() == "value"); | |||||
REQUIRE_THROWS_AS(options.add_options()("", "nothing option"), | |||||
cxxopts::invalid_option_format_error&); | |||||
} | |||||
TEST_CASE("No positional", "[positional]") | |||||
{ | |||||
cxxopts::Options options("test_no_positional", | |||||
" - test no positional options"); | |||||
Argv av({"tester", "a", "b", "def"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
auto result = options.parse(argc, argv); | |||||
REQUIRE(argc == 4); | |||||
CHECK(strcmp(argv[1], "a") == 0); | |||||
} | |||||
TEST_CASE("All positional", "[positional]") | |||||
{ | |||||
std::vector<std::string> positional; | |||||
cxxopts::Options options("test_all_positional", " - test all positional"); | |||||
options.add_options() | |||||
("positional", "Positional parameters", | |||||
cxxopts::value<std::vector<std::string>>(positional)) | |||||
; | |||||
Argv av({"tester", "a", "b", "c"}); | |||||
auto argc = av.argc(); | |||||
auto argv = av.argv(); | |||||
std::vector<std::string> pos_names = {"positional"}; | |||||
options.parse_positional(pos_names.begin(), pos_names.end()); | |||||
auto result = options.parse(argc, argv); | |||||
REQUIRE(argc == 1); | |||||
REQUIRE(positional.size() == 3); | |||||
CHECK(positional[0] == "a"); | |||||
CHECK(positional[1] == "b"); | |||||
CHECK(positional[2] == "c"); | |||||
} | |||||
TEST_CASE("Some positional explicit", "[positional]") | |||||
{ | |||||
cxxopts::Options options("positional_explicit", " - test positional"); | |||||
options.add_options() | |||||
("input", "Input file", cxxopts::value<std::string>()) | |||||
("output", "Output file", cxxopts::value<std::string>()) | |||||
("positional", "Positional parameters", | |||||
cxxopts::value<std::vector<std::string>>()) | |||||
; | |||||
options.parse_positional({"input", "output", "positional"}); | |||||
Argv av({"tester", "--output", "a", "b", "c", "d"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
auto result = options.parse(argc, argv); | |||||
CHECK(argc == 1); | |||||
CHECK(result.count("output")); | |||||
CHECK(result["input"].as<std::string>() == "b"); | |||||
CHECK(result["output"].as<std::string>() == "a"); | |||||
auto& positional = result["positional"].as<std::vector<std::string>>(); | |||||
REQUIRE(positional.size() == 2); | |||||
CHECK(positional[0] == "c"); | |||||
CHECK(positional[1] == "d"); | |||||
} | |||||
TEST_CASE("No positional with extras", "[positional]") | |||||
{ | |||||
cxxopts::Options options("posargmaster", "shows incorrect handling"); | |||||
options.add_options() | |||||
("dummy", "oh no", cxxopts::value<std::string>()) | |||||
; | |||||
Argv av({"extras", "--", "a", "b", "c", "d"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
auto old_argv = argv; | |||||
auto old_argc = argc; | |||||
options.parse(argc, argv); | |||||
REQUIRE(argc == old_argc - 1); | |||||
CHECK(argv[0] == std::string("extras")); | |||||
CHECK(argv[1] == std::string("a")); | |||||
} | |||||
TEST_CASE("Positional not valid", "[positional]") { | |||||
cxxopts::Options options("positional_invalid", "invalid positional argument"); | |||||
options.add_options() | |||||
("long", "a long option", cxxopts::value<std::string>()) | |||||
; | |||||
options.parse_positional("something"); | |||||
Argv av({"foobar", "bar", "baz"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_not_exists_exception&); | |||||
} | |||||
TEST_CASE("Empty with implicit value", "[implicit]") | |||||
{ | |||||
cxxopts::Options options("empty_implicit", "doesn't handle empty"); | |||||
options.add_options() | |||||
("implicit", "Has implicit", cxxopts::value<std::string>() | |||||
->implicit_value("foo")); | |||||
Argv av({"implicit", "--implicit="}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
auto result = options.parse(argc, argv); | |||||
REQUIRE(result.count("implicit") == 1); | |||||
REQUIRE(result["implicit"].as<std::string>() == ""); | |||||
} | |||||
TEST_CASE("Boolean without implicit value", "[implicit]") | |||||
{ | |||||
cxxopts::Options options("no_implicit", "bool without an implicit value"); | |||||
options.add_options() | |||||
("bool", "Boolean without implicit", cxxopts::value<bool>() | |||||
->no_implicit_value()); | |||||
SECTION("When no value provided") { | |||||
Argv av({"no_implicit", "--bool"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::missing_argument_exception&); | |||||
} | |||||
SECTION("With equal-separated true") { | |||||
Argv av({"no_implicit", "--bool=true"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
auto result = options.parse(argc, argv); | |||||
CHECK(result.count("bool") == 1); | |||||
CHECK(result["bool"].as<bool>() == true); | |||||
} | |||||
SECTION("With equal-separated false") { | |||||
Argv av({"no_implicit", "--bool=false"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
auto result = options.parse(argc, argv); | |||||
CHECK(result.count("bool") == 1); | |||||
CHECK(result["bool"].as<bool>() == false); | |||||
} | |||||
SECTION("With space-separated true") { | |||||
Argv av({"no_implicit", "--bool", "true"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
auto result = options.parse(argc, argv); | |||||
CHECK(result.count("bool") == 1); | |||||
CHECK(result["bool"].as<bool>() == true); | |||||
} | |||||
SECTION("With space-separated false") { | |||||
Argv av({"no_implicit", "--bool", "false"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
auto result = options.parse(argc, argv); | |||||
CHECK(result.count("bool") == 1); | |||||
CHECK(result["bool"].as<bool>() == false); | |||||
} | |||||
} | |||||
TEST_CASE("Default values", "[default]") | |||||
{ | |||||
cxxopts::Options options("defaults", "has defaults"); | |||||
options.add_options() | |||||
("default", "Has implicit", cxxopts::value<int>()->default_value("42")) | |||||
("v,vector", "Default vector", cxxopts::value<std::vector<int>>() | |||||
->default_value("1,4")) | |||||
; | |||||
SECTION("Sets defaults") { | |||||
Argv av({"implicit"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
auto result = options.parse(argc, argv); | |||||
CHECK(result.count("default") == 0); | |||||
CHECK(result["default"].as<int>() == 42); | |||||
auto& v = result["vector"].as<std::vector<int>>(); | |||||
REQUIRE(v.size() == 2); | |||||
CHECK(v[0] == 1); | |||||
CHECK(v[1] == 4); | |||||
} | |||||
SECTION("When values provided") { | |||||
Argv av({"implicit", "--default", "5"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
auto result = options.parse(argc, argv); | |||||
CHECK(result.count("default") == 1); | |||||
CHECK(result["default"].as<int>() == 5); | |||||
} | |||||
} | |||||
TEST_CASE("Parse into a reference", "[reference]") | |||||
{ | |||||
int value = 0; | |||||
cxxopts::Options options("into_reference", "parses into a reference"); | |||||
options.add_options() | |||||
("ref", "A reference", cxxopts::value(value)); | |||||
Argv av({"into_reference", "--ref", "42"}); | |||||
auto argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
auto result = options.parse(argc, argv); | |||||
CHECK(result.count("ref") == 1); | |||||
CHECK(value == 42); | |||||
} | |||||
TEST_CASE("Integers", "[options]") | |||||
{ | |||||
cxxopts::Options options("parses_integers", "parses integers correctly"); | |||||
options.add_options() | |||||
("positional", "Integers", cxxopts::value<std::vector<int>>()); | |||||
Argv av({"ints", "--", "5", "6", "-6", "0", "0xab", "0xAf", "0x0"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
options.parse_positional("positional"); | |||||
auto result = options.parse(argc, argv); | |||||
REQUIRE(result.count("positional") == 7); | |||||
auto& positional = result["positional"].as<std::vector<int>>(); | |||||
REQUIRE(positional.size() == 7); | |||||
CHECK(positional[0] == 5); | |||||
CHECK(positional[1] == 6); | |||||
CHECK(positional[2] == -6); | |||||
CHECK(positional[3] == 0); | |||||
CHECK(positional[4] == 0xab); | |||||
CHECK(positional[5] == 0xaf); | |||||
CHECK(positional[6] == 0x0); | |||||
} | |||||
TEST_CASE("Leading zero integers", "[options]") | |||||
{ | |||||
cxxopts::Options options("parses_integers", "parses integers correctly"); | |||||
options.add_options() | |||||
("positional", "Integers", cxxopts::value<std::vector<int>>()); | |||||
Argv av({"ints", "--", "05", "06", "0x0ab", "0x0001"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
options.parse_positional("positional"); | |||||
auto result = options.parse(argc, argv); | |||||
REQUIRE(result.count("positional") == 4); | |||||
auto& positional = result["positional"].as<std::vector<int>>(); | |||||
REQUIRE(positional.size() == 4); | |||||
CHECK(positional[0] == 5); | |||||
CHECK(positional[1] == 6); | |||||
CHECK(positional[2] == 0xab); | |||||
CHECK(positional[3] == 0x1); | |||||
} | |||||
TEST_CASE("Unsigned integers", "[options]") | |||||
{ | |||||
cxxopts::Options options("parses_unsigned", "detects unsigned errors"); | |||||
options.add_options() | |||||
("positional", "Integers", cxxopts::value<std::vector<unsigned int>>()); | |||||
Argv av({"ints", "--", "-2"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
options.parse_positional("positional"); | |||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type&); | |||||
} | |||||
TEST_CASE("Integer bounds", "[integer]") | |||||
{ | |||||
cxxopts::Options options("integer_boundaries", "check min/max integer"); | |||||
options.add_options() | |||||
("positional", "Integers", cxxopts::value<std::vector<int8_t>>()); | |||||
SECTION("No overflow") | |||||
{ | |||||
Argv av({"ints", "--", "127", "-128", "0x7f", "-0x80", "0x7e"}); | |||||
auto argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
options.parse_positional("positional"); | |||||
auto result = options.parse(argc, argv); | |||||
REQUIRE(result.count("positional") == 5); | |||||
auto& positional = result["positional"].as<std::vector<int8_t>>(); | |||||
CHECK(positional[0] == 127); | |||||
CHECK(positional[1] == -128); | |||||
CHECK(positional[2] == 0x7f); | |||||
CHECK(positional[3] == -0x80); | |||||
CHECK(positional[4] == 0x7e); | |||||
} | |||||
} | |||||
TEST_CASE("Overflow on boundary", "[integer]") | |||||
{ | |||||
using namespace cxxopts::values; | |||||
int8_t si; | |||||
uint8_t ui; | |||||
CHECK_THROWS_AS((integer_parser("128", si)), cxxopts::argument_incorrect_type&); | |||||
CHECK_THROWS_AS((integer_parser("-129", si)), cxxopts::argument_incorrect_type&); | |||||
CHECK_THROWS_AS((integer_parser("256", ui)), cxxopts::argument_incorrect_type&); | |||||
CHECK_THROWS_AS((integer_parser("-0x81", si)), cxxopts::argument_incorrect_type&); | |||||
CHECK_THROWS_AS((integer_parser("0x80", si)), cxxopts::argument_incorrect_type&); | |||||
CHECK_THROWS_AS((integer_parser("0x100", ui)), cxxopts::argument_incorrect_type&); | |||||
} | |||||
TEST_CASE("Integer overflow", "[options]") | |||||
{ | |||||
using namespace cxxopts::values; | |||||
cxxopts::Options options("reject_overflow", "rejects overflowing integers"); | |||||
options.add_options() | |||||
("positional", "Integers", cxxopts::value<std::vector<int8_t>>()); | |||||
Argv av({"ints", "--", "128"}); | |||||
auto argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
options.parse_positional("positional"); | |||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type&); | |||||
int integer = 0; | |||||
CHECK_THROWS_AS((integer_parser("23423423423", integer)), cxxopts::argument_incorrect_type&); | |||||
CHECK_THROWS_AS((integer_parser("234234234234", integer)), cxxopts::argument_incorrect_type&); | |||||
} | |||||
TEST_CASE("Floats", "[options]") | |||||
{ | |||||
cxxopts::Options options("parses_floats", "parses floats correctly"); | |||||
options.add_options() | |||||
("double", "Double precision", cxxopts::value<double>()) | |||||
("positional", "Floats", cxxopts::value<std::vector<float>>()); | |||||
Argv av({"floats", "--double", "0.5", "--", "4", "-4", "1.5e6", "-1.5e6"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
options.parse_positional("positional"); | |||||
auto result = options.parse(argc, argv); | |||||
REQUIRE(result.count("double") == 1); | |||||
REQUIRE(result.count("positional") == 4); | |||||
CHECK(result["double"].as<double>() == 0.5); | |||||
auto& positional = result["positional"].as<std::vector<float>>(); | |||||
CHECK(positional[0] == 4); | |||||
CHECK(positional[1] == -4); | |||||
CHECK(positional[2] == 1.5e6); | |||||
CHECK(positional[3] == -1.5e6); | |||||
} | |||||
TEST_CASE("Invalid integers", "[integer]") { | |||||
cxxopts::Options options("invalid_integers", "rejects invalid integers"); | |||||
options.add_options() | |||||
("positional", "Integers", cxxopts::value<std::vector<int>>()); | |||||
Argv av({"ints", "--", "Ae"}); | |||||
char **argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
options.parse_positional("positional"); | |||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type&); | |||||
} | |||||
TEST_CASE("Booleans", "[boolean]") { | |||||
cxxopts::Options options("parses_floats", "parses floats correctly"); | |||||
options.add_options() | |||||
("bool", "A Boolean", cxxopts::value<bool>()) | |||||
("debug", "Debugging", cxxopts::value<bool>()) | |||||
("timing", "Timing", cxxopts::value<bool>()) | |||||
("verbose", "Verbose", cxxopts::value<bool>()) | |||||
("dry-run", "Dry Run", cxxopts::value<bool>()) | |||||
("noExplicitDefault", "No Explicit Default", cxxopts::value<bool>()) | |||||
("defaultTrue", "Timing", cxxopts::value<bool>()->default_value("true")) | |||||
("defaultFalse", "Timing", cxxopts::value<bool>()->default_value("false")) | |||||
("others", "Other arguments", cxxopts::value<std::vector<std::string>>()) | |||||
; | |||||
options.parse_positional("others"); | |||||
Argv av({"booleans", "--bool=false", "--debug=true", "--timing", "--verbose=1", "--dry-run=0", "extra"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
auto result = options.parse(argc, argv); | |||||
REQUIRE(result.count("bool") == 1); | |||||
REQUIRE(result.count("debug") == 1); | |||||
REQUIRE(result.count("timing") == 1); | |||||
REQUIRE(result.count("verbose") == 1); | |||||
REQUIRE(result.count("dry-run") == 1); | |||||
REQUIRE(result.count("noExplicitDefault") == 0); | |||||
REQUIRE(result.count("defaultTrue") == 0); | |||||
REQUIRE(result.count("defaultFalse") == 0); | |||||
CHECK(result["bool"].as<bool>() == false); | |||||
CHECK(result["debug"].as<bool>() == true); | |||||
CHECK(result["timing"].as<bool>() == true); | |||||
CHECK(result["verbose"].as<bool>() == true); | |||||
CHECK(result["dry-run"].as<bool>() == false); | |||||
CHECK(result["noExplicitDefault"].as<bool>() == false); | |||||
CHECK(result["defaultTrue"].as<bool>() == true); | |||||
CHECK(result["defaultFalse"].as<bool>() == false); | |||||
REQUIRE(result.count("others") == 1); | |||||
} | |||||
TEST_CASE("std::vector", "[vector]") { | |||||
std::vector<double> vector; | |||||
cxxopts::Options options("vector", " - tests vector"); | |||||
options.add_options() | |||||
("vector", "an vector option", cxxopts::value<std::vector<double>>(vector)); | |||||
Argv av({"vector", "--vector", "1,-2.1,3,4.5"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
options.parse(argc, argv); | |||||
REQUIRE(vector.size() == 4); | |||||
CHECK(vector[0] == 1); | |||||
CHECK(vector[1] == -2.1); | |||||
CHECK(vector[2] == 3); | |||||
CHECK(vector[3] == 4.5); | |||||
} | |||||
#ifdef CXXOPTS_HAS_OPTIONAL | |||||
TEST_CASE("std::optional", "[optional]") { | |||||
std::optional<std::string> optional; | |||||
cxxopts::Options options("optional", " - tests optional"); | |||||
options.add_options() | |||||
("optional", "an optional option", cxxopts::value<std::optional<std::string>>(optional)); | |||||
Argv av({"optional", "--optional", "foo"}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
options.parse(argc, argv); | |||||
REQUIRE(optional.has_value()); | |||||
CHECK(*optional == "foo"); | |||||
} | |||||
#endif | |||||
TEST_CASE("Unrecognised options", "[options]") { | |||||
cxxopts::Options options("unknown_options", " - test unknown options"); | |||||
options.add_options() | |||||
("long", "a long option") | |||||
("s,short", "a short option"); | |||||
Argv av({ | |||||
"unknown_options", | |||||
"--unknown", | |||||
"--long", | |||||
"-su", | |||||
"--another_unknown", | |||||
}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
SECTION("Default behaviour") { | |||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_not_exists_exception&); | |||||
} | |||||
SECTION("After allowing unrecognised options") { | |||||
options.allow_unrecognised_options(); | |||||
CHECK_NOTHROW(options.parse(argc, argv)); | |||||
REQUIRE(argc == 3); | |||||
CHECK_THAT(argv[1], Catch::Equals("--unknown")); | |||||
} | |||||
} | |||||
TEST_CASE("Allow bad short syntax", "[options]") { | |||||
cxxopts::Options options("unknown_options", " - test unknown options"); | |||||
options.add_options() | |||||
("long", "a long option") | |||||
("s,short", "a short option"); | |||||
Argv av({ | |||||
"unknown_options", | |||||
"-some_bad_short", | |||||
}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
SECTION("Default behaviour") { | |||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_syntax_exception&); | |||||
} | |||||
SECTION("After allowing unrecognised options") { | |||||
options.allow_unrecognised_options(); | |||||
CHECK_NOTHROW(options.parse(argc, argv)); | |||||
REQUIRE(argc == 2); | |||||
CHECK_THAT(argv[1], Catch::Equals("-some_bad_short")); | |||||
} | |||||
} | |||||
TEST_CASE("Invalid option syntax", "[options]") { | |||||
cxxopts::Options options("invalid_syntax", " - test invalid syntax"); | |||||
Argv av({ | |||||
"invalid_syntax", | |||||
"--a", | |||||
}); | |||||
char** argv = av.argv(); | |||||
auto argc = av.argc(); | |||||
SECTION("Default behaviour") { | |||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_syntax_exception&); | |||||
} | |||||
} | |||||
TEST_CASE("Options empty", "[options]") { | |||||
cxxopts::Options options("Options list empty", " - test empty option list"); | |||||
options.add_options(); | |||||
options.add_options(""); | |||||
options.add_options("", {}); | |||||
options.add_options("test"); | |||||
Argv argv_({ | |||||
"test", | |||||
"--unknown" | |||||
}); | |||||
auto argc = argv_.argc(); | |||||
char** argv = argv_.argv(); | |||||
CHECK(options.groups().empty()); | |||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_not_exists_exception&); | |||||
} | |||||
TEST_CASE("Initializer list with group", "[options]") { | |||||
cxxopts::Options options("Initializer list group", " - test initializer list with group"); | |||||
options.add_options("", { | |||||
{"a, address", "server address", cxxopts::value<std::string>()->default_value("127.0.0.1")}, | |||||
{"p, port", "server port", cxxopts::value<std::string>()->default_value("7110"), "PORT"}, | |||||
}); | |||||
cxxopts::Option help{"h,help", "Help"}; | |||||
options.add_options("TEST_GROUP", { | |||||
{"t, test", "test option"}, | |||||
help | |||||
}); | |||||
Argv argv({ | |||||
"test", | |||||
"--address", | |||||
"10.0.0.1", | |||||
"-p", | |||||
"8000", | |||||
"-t", | |||||
}); | |||||
char** actual_argv = argv.argv(); | |||||
auto argc = argv.argc(); | |||||
auto result = options.parse(argc, actual_argv); | |||||
CHECK(options.groups().size() == 2); | |||||
CHECK(result.count("address") == 1); | |||||
CHECK(result.count("port") == 1); | |||||
CHECK(result.count("test") == 1); | |||||
CHECK(result.count("help") == 0); | |||||
CHECK(result["address"].as<std::string>() == "10.0.0.1"); | |||||
CHECK(result["port"].as<std::string>() == "8000"); | |||||
CHECK(result["test"].as<bool>() == true); | |||||
} | |||||
TEST_CASE("Option add with add_option(string, Option)", "[options]") { | |||||
cxxopts::Options options("Option add with add_option", " - test Option add with add_option(string, Option)"); | |||||
cxxopts::Option option_1("t,test", "test option", cxxopts::value<int>()->default_value("7"), "TEST"); | |||||
options.add_option("", option_1); | |||||
options.add_option("TEST", {"a,aggregate", "test option 2", cxxopts::value<int>(), "AGGREGATE"}); | |||||
Argv argv_({ | |||||
"test", | |||||
"--test", | |||||
"5", | |||||
"-a", | |||||
"4" | |||||
}); | |||||
auto argc = argv_.argc(); | |||||
char** argv = argv_.argv(); | |||||
auto result = options.parse(argc, argv); | |||||
CHECK(result.arguments().size()==2); | |||||
CHECK(options.groups().size() == 2); | |||||
CHECK(result.count("address") == 0); | |||||
CHECK(result.count("aggregate") == 1); | |||||
CHECK(result.count("test") == 1); | |||||
CHECK(result["aggregate"].as<int>() == 4); | |||||
CHECK(result["test"].as<int>() == 5); | |||||
} |