@ -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); | |||
} |