Browse Source

Add cxxopts module

master
C. J. Howard 4 years ago
parent
commit
50e0e2f202
24 changed files with 14295 additions and 2 deletions
  1. +12
    -0
      CMakeLists.txt
  2. +2
    -1
      README.md
  3. +1
    -1
      modules/antkeeper-source
  4. +8
    -0
      modules/cxxopts/.gitignore
  5. +70
    -0
      modules/cxxopts/.travis.yml
  6. +97
    -0
      modules/cxxopts/CHANGELOG.md
  7. +108
    -0
      modules/cxxopts/CMakeLists.txt
  8. +23
    -0
      modules/cxxopts/INSTALL
  9. +19
    -0
      modules/cxxopts/LICENSE
  10. +224
    -0
      modules/cxxopts/README.md
  11. +4
    -0
      modules/cxxopts/cxxopts-config.cmake.in
  12. +2231
    -0
      modules/cxxopts/include/cxxopts.hpp
  13. +1
    -0
      modules/cxxopts/src/.gitignore
  14. +24
    -0
      modules/cxxopts/src/CMakeLists.txt
  15. +168
    -0
      modules/cxxopts/src/example.cpp
  16. +1
    -0
      modules/cxxopts/test/.gitignore
  17. +35
    -0
      modules/cxxopts/test/CMakeLists.txt
  18. +11
    -0
      modules/cxxopts/test/add-subdirectory-test/CMakeLists.txt
  19. +10460
    -0
      modules/cxxopts/test/catch.hpp
  20. +11
    -0
      modules/cxxopts/test/find-package-test/CMakeLists.txt
  21. +6
    -0
      modules/cxxopts/test/link_a.cpp
  22. +1
    -0
      modules/cxxopts/test/link_b.cpp
  23. +2
    -0
      modules/cxxopts/test/main.cpp
  24. +776
    -0
      modules/cxxopts/test/options.cpp

+ 12
- 0
CMakeLists.txt View File

@ -178,6 +178,18 @@ ExternalProject_Add(dirent
"-DDIRENT_BUILD_TESTS=OFF" "-DDIRENT_BUILD_TESTS=OFF"
BUILD_ALWAYS 0) BUILD_ALWAYS 0)
# Build cxxopts module
ExternalProject_Add(cxxopts
SOURCE_DIR ${MODULE_SOURCE_DIR}/cxxopts
BINARY_DIR ${MODULE_BUILD_DIR}/cxxopts
INSTALL_DIR ${MODULE_INSTALL_DIR}
CMAKE_ARGS
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
"-DCMAKE_INSTALL_PREFIX=${MODULE_INSTALL_DIR}"
"-DCXXOPTS_BUILD_EXAMPLES=OFF"
"-DCXXOPTS_BUILD_TESTS=OFF"
BUILD_ALWAYS 0)
# Build PhysicsFS module # Build PhysicsFS module
ExternalProject_Add(physfs ExternalProject_Add(physfs
SOURCE_DIR ${MODULE_SOURCE_DIR}/physfs SOURCE_DIR ${MODULE_SOURCE_DIR}/physfs

+ 2
- 1
README.md View File

@ -65,9 +65,10 @@ Antkeeper superbuild is licensed under the GNU General Public License, version 3
| Name | Author(s) | License | File(s) | | Name | Author(s) | License | File(s) |
| :------------------------------ | :--------------------- | :-------------------------- | :------------------------------------------------------------------ | | :------------------------------ | :--------------------- | :-------------------------- | :------------------------------------------------------------------ |
| cxxopts | Jarryd Beck | MIT License | [cxxopts.hpp](./modules/cxxopts/include/cxxopts.hpp) |
| dirent | Toni Ronkko | MIT License | [dirent.h](./modules/dirent/include/dirent.h) | | dirent | Toni Ronkko | MIT License | [dirent.h](./modules/dirent/include/dirent.h) |
| dr_wav | David Reid | Public Domain (Unlicense) | [dr_wav.h](./modules/dr_wav/dr_wav.h) | | dr_wav | David Reid | Public Domain (Unlicense) | [dr_wav.h](./modules/dr_wav/dr_wav.h) |
| Easing Functions (Equations) | Robert Penner | MIT License | [ease.hpp](./modules/antkeeper-source/src/animation/ease.hpp) |
| Easing Functions (Equations) | Robert Penner | MIT License | [ease.hpp](./modules/antkeeper-source/src/animation/ease.hpp) |
| EnTT | Michele Caini | MIT License | [entt/*](./modules/entt/) | | EnTT | Michele Caini | MIT License | [entt/*](./modules/entt/) |
| khrplatform.h | The Khronos Group Inc. | MIT License | [khrplatform.h](./modules/glad/khrplatform.h) | | khrplatform.h | The Khronos Group Inc. | MIT License | [khrplatform.h](./modules/glad/khrplatform.h) |
| OpenGL loader generated by glad | David Herberth | Public Domain / WTFPL / CC0 | [glad.h](./modules/glad/glad.h), [glad.c](./modules/glad/glad.c) | | OpenGL loader generated by glad | David Herberth | Public Domain / WTFPL / CC0 | [glad.h](./modules/glad/glad.h), [glad.c](./modules/glad/glad.c) |

+ 1
- 1
modules/antkeeper-source

@ -1 +1 @@
Subproject commit f44df4b7cc602875602e9eeecab28b9a58ead097
Subproject commit 03dda20c2bfc481313ba446e34e4917dad0ec0bd

+ 8
- 0
modules/cxxopts/.gitignore View File

@ -0,0 +1,8 @@
*.swp
build*
CMakeCache.txt
Makefile
CMakeFiles/
Testing/
CTestTestfile.cmake
cmake_install.cmake

+ 70
- 0
modules/cxxopts/.travis.yml View File

@ -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

+ 97
- 0
modules/cxxopts/CHANGELOG.md View File

@ -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.

+ 108
- 0
modules/cxxopts/CMakeLists.txt View File

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

+ 23
- 0
modules/cxxopts/INSTALL View File

@ -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

+ 19
- 0
modules/cxxopts/LICENSE View File

@ -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.

+ 224
- 0
modules/cxxopts/README.md View File

@ -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.

+ 4
- 0
modules/cxxopts/cxxopts-config.cmake.in View File

@ -0,0 +1,4 @@
@PACKAGE_INIT@
include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake)
check_required_components(cxxopts)

+ 2231
- 0
modules/cxxopts/include/cxxopts.hpp
File diff suppressed because it is too large
View File


+ 1
- 0
modules/cxxopts/src/.gitignore View File

@ -0,0 +1 @@
example

+ 24
- 0
modules/cxxopts/src/CMakeLists.txt View File

@ -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()

+ 168
- 0
modules/cxxopts/src/example.cpp View File

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

+ 1
- 0
modules/cxxopts/test/.gitignore View File

@ -0,0 +1 @@
options_test

+ 35
- 0
modules/cxxopts/test/CMakeLists.txt View File

@ -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()

+ 11
- 0
modules/cxxopts/test/add-subdirectory-test/CMakeLists.txt View File

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

+ 10460
- 0
modules/cxxopts/test/catch.hpp
File diff suppressed because it is too large
View File


+ 11
- 0
modules/cxxopts/test/find-package-test/CMakeLists.txt View File

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

+ 6
- 0
modules/cxxopts/test/link_a.cpp View File

@ -0,0 +1,6 @@
#include "cxxopts.hpp"
int main(int, char**)
{
return 0;
}

+ 1
- 0
modules/cxxopts/test/link_b.cpp View File

@ -0,0 +1 @@
#include <cxxopts.hpp>

+ 2
- 0
modules/cxxopts/test/main.cpp View File

@ -0,0 +1,2 @@
#define CATCH_CONFIG_MAIN
#include "catch.hpp"

+ 776
- 0
modules/cxxopts/test/options.cpp View File

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

Loading…
Cancel
Save