Compare commits
30 Commits
fix_iter_a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70fb4e4814 | ||
|
|
4bf61f0869 | ||
|
|
1e175bc1db | ||
|
|
3bf268481d | ||
|
|
f7194e9c59 | ||
|
|
71d5bf7265 | ||
|
|
d3de64f26e | ||
|
|
e84ab5f67c | ||
|
|
7bf29108d5 | ||
|
|
cd61c685eb | ||
|
|
554396be3b | ||
|
|
78b90d8f0c | ||
|
|
c8c932f891 | ||
|
|
ddc695ebac | ||
|
|
bf1b5a96e0 | ||
|
|
90b318105f | ||
|
|
a526762eb8 | ||
|
|
beda973ec6 | ||
|
|
714a105fe6 | ||
|
|
16cc254919 | ||
|
|
120205ac5a | ||
|
|
89a9d334f2 | ||
|
|
44739d3023 | ||
|
|
134f60f973 | ||
|
|
eb787304d6 | ||
|
|
c12ce65503 | ||
|
|
b80bdfddf0 | ||
|
|
799ee0e3e8 | ||
|
|
58daccc945 | ||
|
|
e1f8c16702 |
24
.github/workflows/cifuzz.yml
vendored
Normal file
24
.github/workflows/cifuzz.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
name: CIFuzz
|
||||||
|
on: [pull_request]
|
||||||
|
jobs:
|
||||||
|
Fuzzing:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Build Fuzzers
|
||||||
|
id: build
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'cxxopts'
|
||||||
|
language: c++
|
||||||
|
- name: Run Fuzzers
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'cxxopts'
|
||||||
|
language: c++
|
||||||
|
fuzz-seconds: 300
|
||||||
|
- name: Upload Crash
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
if: failure() && steps.build.outcome == 'success'
|
||||||
|
with:
|
||||||
|
name: artifacts
|
||||||
|
path: ./out/artifacts
|
||||||
6
.github/workflows/cmake.yml
vendored
6
.github/workflows/cmake.yml
vendored
@ -20,11 +20,9 @@ jobs:
|
|||||||
build-ubuntu:
|
build-ubuntu:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-18.04, ubuntu-20.04, ubuntu-22.04 ]
|
os: [ ubuntu-20.04, ubuntu-22.04 ]
|
||||||
compiler: [ g++-9, g++-10, clang++ ]
|
compiler: [ g++-9, g++-10, clang++ ]
|
||||||
include:
|
|
||||||
- os: ubuntu-18.04
|
|
||||||
compiler: g++-7
|
|
||||||
name: Build and Test on Ubuntu
|
name: Build and Test on Ubuntu
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@ -6,3 +6,11 @@ cc_library(
|
|||||||
strip_include_prefix = "include",
|
strip_include_prefix = "include",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test")
|
||||||
|
|
||||||
|
cc_fuzz_test(
|
||||||
|
name = "cxxopts_fuzz_test",
|
||||||
|
srcs = ["test/fuzz.cpp"],
|
||||||
|
deps = [":cxxopts"],
|
||||||
|
)
|
||||||
36
CHANGELOG.md
36
CHANGELOG.md
@ -3,17 +3,51 @@
|
|||||||
This is the changelog for `cxxopts`, a C++11 library for parsing command line
|
This is the changelog for `cxxopts`, a C++11 library for parsing command line
|
||||||
options. The project adheres to semantic versioning.
|
options. The project adheres to semantic versioning.
|
||||||
|
|
||||||
## Unreleased
|
## 3.2.1
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* Fix compilation with optional on C++20.
|
||||||
|
|
||||||
|
## 3.2
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* Fix unannotated fallthrough.
|
||||||
|
* Fix sign conversion with Unicode output.
|
||||||
|
* Don't initialize regex in static initialiser.
|
||||||
|
* Fix incorrect integer overflow checks.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add fuzzing to CI
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Change quote output to '' to match Windows.
|
||||||
|
* Don't split positional arguments by the list delimiter.
|
||||||
|
* Order help groups by the order they were added.
|
||||||
|
|
||||||
|
## 3.1.1
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Fixed version number in header.
|
||||||
|
* Fixed cast warning in Unicode function.
|
||||||
|
|
||||||
|
## 3.1
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
* Support for multiple long names for the same option (= multiple long aliases)
|
* Support for multiple long names for the same option (= multiple long aliases)
|
||||||
* Add a `program()` function to retrieve the program name.
|
* Add a `program()` function to retrieve the program name.
|
||||||
* Added a .clang-format file.
|
* Added a .clang-format file.
|
||||||
|
* Added iterator and printing for a ParseResult.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
* Cleanup exception code, add cxxopts::exceptions namespace.
|
* Cleanup exception code, add cxxopts::exceptions namespace.
|
||||||
|
* Renamed several exceptions to be more descriptive, and added to a nested namespace.
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
|||||||
11
README.md
11
README.md
@ -85,9 +85,6 @@ result["opt"].as<type>()
|
|||||||
to get its value. If "opt" doesn't exist, or isn't of the right type, then an
|
to get its value. If "opt" doesn't exist, or isn't of the right type, then an
|
||||||
exception will be thrown.
|
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.
|
|
||||||
|
|
||||||
## Unrecognised arguments
|
## Unrecognised arguments
|
||||||
|
|
||||||
You can allow unrecognised arguments to be skipped. This applies to both
|
You can allow unrecognised arguments to be skipped. This applies to both
|
||||||
@ -196,8 +193,8 @@ therefore, `-o false` does not work.
|
|||||||
|
|
||||||
## `std::vector<T>` values
|
## `std::vector<T>` values
|
||||||
|
|
||||||
Parsing of list of values in form of an `std::vector<T>` is also supported, as long as `T`
|
Parsing a list of values into a `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`
|
can be parsed. To separate single values in a list the define symbol `CXXOPTS_VECTOR_DELIMITER`
|
||||||
is used, which is ',' by default. Ensure that you use no whitespaces between values because
|
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
|
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>`:
|
that can be parsed as a `std::vector<double>`:
|
||||||
@ -278,7 +275,3 @@ GCC >= 4.9 or clang >= 3.1 with libc++ are known to work.
|
|||||||
The following compilers are known not to work:
|
The following compilers are known not to work:
|
||||||
|
|
||||||
* MSVC 2013
|
* MSVC 2013
|
||||||
|
|
||||||
# TODO list
|
|
||||||
|
|
||||||
* Allow unrecognised options.
|
|
||||||
|
|||||||
16
WORKSPACE
16
WORKSPACE
@ -0,0 +1,16 @@
|
|||||||
|
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "rules_fuzzing",
|
||||||
|
sha256 = "d9002dd3cd6437017f08593124fdd1b13b3473c7b929ceb0e60d317cb9346118",
|
||||||
|
strip_prefix = "rules_fuzzing-0.3.2",
|
||||||
|
urls = ["https://github.com/bazelbuild/rules_fuzzing/archive/v0.3.2.zip"],
|
||||||
|
)
|
||||||
|
|
||||||
|
load("@rules_fuzzing//fuzzing:repositories.bzl", "rules_fuzzing_dependencies")
|
||||||
|
|
||||||
|
rules_fuzzing_dependencies()
|
||||||
|
|
||||||
|
load("@rules_fuzzing//fuzzing:init.bzl", "rules_fuzzing_init")
|
||||||
|
|
||||||
|
rules_fuzzing_init()
|
||||||
@ -27,6 +27,7 @@ THE SOFTWARE.
|
|||||||
#ifndef CXXOPTS_HPP_INCLUDED
|
#ifndef CXXOPTS_HPP_INCLUDED
|
||||||
#define CXXOPTS_HPP_INCLUDED
|
#define CXXOPTS_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
@ -40,6 +41,7 @@ THE SOFTWARE.
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
#ifdef CXXOPTS_NO_EXCEPTIONS
|
#ifdef CXXOPTS_NO_EXCEPTIONS
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -72,6 +74,14 @@ THE SOFTWARE.
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define CXXOPTS_FALLTHROUGH
|
||||||
|
#ifdef __has_cpp_attribute
|
||||||
|
#if __has_cpp_attribute(fallthrough)
|
||||||
|
#undef CXXOPTS_FALLTHROUGH
|
||||||
|
#define CXXOPTS_FALLTHROUGH [[fallthrough]]
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#if __cplusplus >= 201603L
|
#if __cplusplus >= 201603L
|
||||||
#define CXXOPTS_NODISCARD [[nodiscard]]
|
#define CXXOPTS_NODISCARD [[nodiscard]]
|
||||||
#else
|
#else
|
||||||
@ -83,8 +93,8 @@ THE SOFTWARE.
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define CXXOPTS__VERSION_MAJOR 3
|
#define CXXOPTS__VERSION_MAJOR 3
|
||||||
#define CXXOPTS__VERSION_MINOR 0
|
#define CXXOPTS__VERSION_MINOR 2
|
||||||
#define CXXOPTS__VERSION_PATCH 0
|
#define CXXOPTS__VERSION_PATCH 1
|
||||||
|
|
||||||
#if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6
|
#if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6
|
||||||
#define CXXOPTS_NULL_DEREF_IGNORE
|
#define CXXOPTS_NULL_DEREF_IGNORE
|
||||||
@ -229,10 +239,10 @@ stringAppend(String& s, Iterator begin, Iterator end)
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
std::size_t
|
size_t
|
||||||
stringLength(const String& s)
|
stringLength(const String& s)
|
||||||
{
|
{
|
||||||
return s.length();
|
return static_cast<size_t>(s.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
@ -336,13 +346,8 @@ empty(const std::string& s)
|
|||||||
namespace cxxopts {
|
namespace cxxopts {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
#ifdef _WIN32
|
|
||||||
CXXOPTS_LINKONCE_CONST std::string LQUOTE("\'");
|
CXXOPTS_LINKONCE_CONST std::string LQUOTE("\'");
|
||||||
CXXOPTS_LINKONCE_CONST std::string RQUOTE("\'");
|
CXXOPTS_LINKONCE_CONST std::string RQUOTE("\'");
|
||||||
#else
|
|
||||||
CXXOPTS_LINKONCE_CONST std::string LQUOTE("‘");
|
|
||||||
CXXOPTS_LINKONCE_CONST std::string RQUOTE("’");
|
|
||||||
#endif
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we
|
// GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we
|
||||||
@ -364,6 +369,9 @@ class Value : public std::enable_shared_from_this<Value>
|
|||||||
std::shared_ptr<Value>
|
std::shared_ptr<Value>
|
||||||
clone() const = 0;
|
clone() const = 0;
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
add(const std::string& text) const = 0;
|
||||||
|
|
||||||
virtual void
|
virtual void
|
||||||
parse(const std::string& text) const = 0;
|
parse(const std::string& text) const = 0;
|
||||||
|
|
||||||
@ -757,29 +765,31 @@ inline ArguDesc ParseArgument(const char *arg, bool &matched)
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
CXXOPTS_LINKONCE
|
CXXOPTS_LINKONCE
|
||||||
std::basic_regex<char> integer_pattern
|
const char* const integer_pattern =
|
||||||
("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
|
"(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)";
|
||||||
CXXOPTS_LINKONCE
|
CXXOPTS_LINKONCE
|
||||||
std::basic_regex<char> truthy_pattern
|
const char* const truthy_pattern =
|
||||||
("(t|T)(rue)?|1");
|
"(t|T)(rue)?|1";
|
||||||
CXXOPTS_LINKONCE
|
CXXOPTS_LINKONCE
|
||||||
std::basic_regex<char> falsy_pattern
|
const char* const falsy_pattern =
|
||||||
("(f|F)(alse)?|0");
|
"(f|F)(alse)?|0";
|
||||||
CXXOPTS_LINKONCE
|
CXXOPTS_LINKONCE
|
||||||
std::basic_regex<char> option_matcher
|
const char* const option_pattern =
|
||||||
("--([[:alnum:]][-_[:alnum:]\\.]+)(=(.*))?|-([[:alnum:]].*)");
|
"--([[:alnum:]][-_[:alnum:]\\.]+)(=(.*))?|-([[:alnum:]].*)";
|
||||||
CXXOPTS_LINKONCE
|
CXXOPTS_LINKONCE
|
||||||
std::basic_regex<char> option_specifier
|
const char* const option_specifier_pattern =
|
||||||
("([[:alnum:]][-_[:alnum:]\\.]*)(,[ ]*[[:alnum:]][-_[:alnum:]]*)*");
|
"([[:alnum:]][-_[:alnum:]\\.]*)(,[ ]*[[:alnum:]][-_[:alnum:]]*)*";
|
||||||
CXXOPTS_LINKONCE
|
CXXOPTS_LINKONCE
|
||||||
std::basic_regex<char> option_specifier_separator(", *");
|
const char* const option_specifier_separator_pattern = ", *";
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
inline IntegerDesc SplitInteger(const std::string &text)
|
inline IntegerDesc SplitInteger(const std::string &text)
|
||||||
{
|
{
|
||||||
|
static const std::basic_regex<char> integer_matcher(integer_pattern);
|
||||||
|
|
||||||
std::smatch match;
|
std::smatch match;
|
||||||
std::regex_match(text, match, integer_pattern);
|
std::regex_match(text, match, integer_matcher);
|
||||||
|
|
||||||
if (match.length() == 0)
|
if (match.length() == 0)
|
||||||
{
|
{
|
||||||
@ -803,15 +813,17 @@ inline IntegerDesc SplitInteger(const std::string &text)
|
|||||||
|
|
||||||
inline bool IsTrueText(const std::string &text)
|
inline bool IsTrueText(const std::string &text)
|
||||||
{
|
{
|
||||||
|
static const std::basic_regex<char> truthy_matcher(truthy_pattern);
|
||||||
std::smatch result;
|
std::smatch result;
|
||||||
std::regex_match(text, result, truthy_pattern);
|
std::regex_match(text, result, truthy_matcher);
|
||||||
return !result.empty();
|
return !result.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool IsFalseText(const std::string &text)
|
inline bool IsFalseText(const std::string &text)
|
||||||
{
|
{
|
||||||
|
static const std::basic_regex<char> falsy_matcher(falsy_pattern);
|
||||||
std::smatch result;
|
std::smatch result;
|
||||||
std::regex_match(text, result, falsy_pattern);
|
std::regex_match(text, result, falsy_matcher);
|
||||||
return !result.empty();
|
return !result.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -820,22 +832,25 @@ inline bool IsFalseText(const std::string &text)
|
|||||||
// (without considering which or how many are single-character)
|
// (without considering which or how many are single-character)
|
||||||
inline OptionNames split_option_names(const std::string &text)
|
inline OptionNames split_option_names(const std::string &text)
|
||||||
{
|
{
|
||||||
if (!std::regex_match(text.c_str(), option_specifier))
|
static const std::basic_regex<char> option_specifier_matcher(option_specifier_pattern);
|
||||||
|
if (!std::regex_match(text.c_str(), option_specifier_matcher))
|
||||||
{
|
{
|
||||||
throw_or_mimic<exceptions::invalid_option_format>(text);
|
throw_or_mimic<exceptions::invalid_option_format>(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
OptionNames split_names;
|
OptionNames split_names;
|
||||||
|
|
||||||
|
static const std::basic_regex<char> option_specifier_separator_matcher(option_specifier_separator_pattern);
|
||||||
constexpr int use_non_matches { -1 };
|
constexpr int use_non_matches { -1 };
|
||||||
auto token_iterator = std::sregex_token_iterator(
|
auto token_iterator = std::sregex_token_iterator(
|
||||||
text.begin(), text.end(), option_specifier_separator, use_non_matches);
|
text.begin(), text.end(), option_specifier_separator_matcher, use_non_matches);
|
||||||
std::copy(token_iterator, std::sregex_token_iterator(), std::back_inserter(split_names));
|
std::copy(token_iterator, std::sregex_token_iterator(), std::back_inserter(split_names));
|
||||||
return split_names;
|
return split_names;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ArguDesc ParseArgument(const char *arg, bool &matched)
|
inline ArguDesc ParseArgument(const char *arg, bool &matched)
|
||||||
{
|
{
|
||||||
|
static const std::basic_regex<char> option_matcher(option_pattern);
|
||||||
std::match_results<const char*> result;
|
std::match_results<const char*> result;
|
||||||
std::regex_match(arg, result, option_matcher);
|
std::regex_match(arg, result, option_matcher);
|
||||||
matched = !result.empty();
|
matched = !result.empty();
|
||||||
@ -958,13 +973,26 @@ integer_parser(const std::string& text, T& value)
|
|||||||
throw_or_mimic<exceptions::incorrect_argument_type>(text);
|
throw_or_mimic<exceptions::incorrect_argument_type>(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
const US next = static_cast<US>(result * base + digit);
|
US limit = 0;
|
||||||
if (result > next)
|
if (negative)
|
||||||
|
{
|
||||||
|
limit = static_cast<US>(std::abs(static_cast<intmax_t>(std::numeric_limits<T>::min())));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
limit = std::numeric_limits<T>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (base != 0 && result > limit / base)
|
||||||
|
{
|
||||||
|
throw_or_mimic<exceptions::incorrect_argument_type>(text);
|
||||||
|
}
|
||||||
|
if (result * base > limit - digit)
|
||||||
{
|
{
|
||||||
throw_or_mimic<exceptions::incorrect_argument_type>(text);
|
throw_or_mimic<exceptions::incorrect_argument_type>(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = next;
|
result = static_cast<US>(result * base + digit);
|
||||||
}
|
}
|
||||||
|
|
||||||
detail::check_signed_range<T>(negative, result, text);
|
detail::check_signed_range<T>(negative, result, text);
|
||||||
@ -1034,25 +1062,6 @@ parse_value(const std::string& text, T& value) {
|
|||||||
stringstream_parser(text, value);
|
stringstream_parser(text, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void
|
|
||||||
parse_value(const std::string& text, std::vector<T>& value)
|
|
||||||
{
|
|
||||||
if (text.empty()) {
|
|
||||||
T v;
|
|
||||||
parse_value(text, v);
|
|
||||||
value.emplace_back(std::move(v));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::stringstream in(text);
|
|
||||||
std::string token;
|
|
||||||
while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
|
|
||||||
T v;
|
|
||||||
parse_value(token, v);
|
|
||||||
value.emplace_back(std::move(v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CXXOPTS_HAS_OPTIONAL
|
#ifdef CXXOPTS_HAS_OPTIONAL
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void
|
void
|
||||||
@ -1075,6 +1084,41 @@ void parse_value(const std::string& text, char& c)
|
|||||||
c = text[0];
|
c = text[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void
|
||||||
|
parse_value(const std::string& text, std::vector<T>& value)
|
||||||
|
{
|
||||||
|
if (text.empty()) {
|
||||||
|
T v;
|
||||||
|
parse_value(text, v);
|
||||||
|
value.emplace_back(std::move(v));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::stringstream in(text);
|
||||||
|
std::string token;
|
||||||
|
while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
|
||||||
|
T v;
|
||||||
|
parse_value(token, v);
|
||||||
|
value.emplace_back(std::move(v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void
|
||||||
|
add_value(const std::string& text, T& value)
|
||||||
|
{
|
||||||
|
parse_value(text, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void
|
||||||
|
add_value(const std::string& text, std::vector<T>& value)
|
||||||
|
{
|
||||||
|
T v;
|
||||||
|
add_value(text, v);
|
||||||
|
value.emplace_back(std::move(v));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct type_is_container
|
struct type_is_container
|
||||||
{
|
{
|
||||||
@ -1126,6 +1170,12 @@ class abstract_value : public Value
|
|||||||
m_implicit_value = rhs.m_implicit_value;
|
m_implicit_value = rhs.m_implicit_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
add(const std::string& text) const override
|
||||||
|
{
|
||||||
|
add_value(text, *m_store);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
parse(const std::string& text) const override
|
parse(const std::string& text) const override
|
||||||
{
|
{
|
||||||
@ -1411,6 +1461,19 @@ struct HelpGroupDetails
|
|||||||
class OptionValue
|
class OptionValue
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
void
|
||||||
|
add
|
||||||
|
(
|
||||||
|
const std::shared_ptr<const OptionDetails>& details,
|
||||||
|
const std::string& text
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ensure_value(details);
|
||||||
|
++m_count;
|
||||||
|
m_value->add(text);
|
||||||
|
m_long_names = &details->long_names();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
parse
|
parse
|
||||||
(
|
(
|
||||||
@ -1475,6 +1538,15 @@ CXXOPTS_DIAGNOSTIC_POP
|
|||||||
return CXXOPTS_RTTI_CAST<const values::standard_value<T>&>(*m_value).get();
|
return CXXOPTS_RTTI_CAST<const values::standard_value<T>&>(*m_value).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CXXOPTS_HAS_OPTIONAL
|
||||||
|
template <typename T>
|
||||||
|
std::optional<T>
|
||||||
|
as_optional() const
|
||||||
|
{
|
||||||
|
return as<T>();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void
|
void
|
||||||
ensure_value(const std::shared_ptr<const OptionDetails>& details)
|
ensure_value(const std::shared_ptr<const OptionDetails>& details)
|
||||||
@ -1497,7 +1569,7 @@ CXXOPTS_DIAGNOSTIC_POP
|
|||||||
class KeyValue
|
class KeyValue
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KeyValue(std::string key_, std::string value_)
|
KeyValue(std::string key_, std::string value_) noexcept
|
||||||
: m_key(std::move(key_))
|
: m_key(std::move(key_))
|
||||||
, m_value(std::move(value_))
|
, m_value(std::move(value_))
|
||||||
{
|
{
|
||||||
@ -1551,6 +1623,7 @@ class ParseResult
|
|||||||
|
|
||||||
// GCC complains about m_iter not being initialised in the member
|
// GCC complains about m_iter not being initialised in the member
|
||||||
// initializer list
|
// initializer list
|
||||||
|
CXXOPTS_DIAGNOSTIC_PUSH
|
||||||
CXXOPTS_IGNORE_WARNING("-Weffc++")
|
CXXOPTS_IGNORE_WARNING("-Weffc++")
|
||||||
Iterator(const ParseResult *pr, bool end=false)
|
Iterator(const ParseResult *pr, bool end=false)
|
||||||
: m_pr(pr)
|
: m_pr(pr)
|
||||||
@ -1686,6 +1759,24 @@ CXXOPTS_DIAGNOSTIC_POP
|
|||||||
return viter->second;
|
return viter->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CXXOPTS_HAS_OPTIONAL
|
||||||
|
template <typename T>
|
||||||
|
std::optional<T>
|
||||||
|
as_optional(const std::string& option) const
|
||||||
|
{
|
||||||
|
auto iter = m_keys.find(option);
|
||||||
|
if (iter != m_keys.end())
|
||||||
|
{
|
||||||
|
auto viter = m_values.find(iter->second);
|
||||||
|
if (viter != m_values.end())
|
||||||
|
{
|
||||||
|
return viter->second.as_optional<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
const std::vector<KeyValue>&
|
const std::vector<KeyValue>&
|
||||||
arguments() const
|
arguments() const
|
||||||
{
|
{
|
||||||
@ -1780,7 +1871,7 @@ class OptionParser
|
|||||||
);
|
);
|
||||||
|
|
||||||
void
|
void
|
||||||
add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg);
|
add_to_option(const std::shared_ptr<OptionDetails>& value, const std::string& arg);
|
||||||
|
|
||||||
void
|
void
|
||||||
parse_option
|
parse_option
|
||||||
@ -1983,6 +2074,7 @@ class Options
|
|||||||
std::unordered_set<std::string> m_positional_set{};
|
std::unordered_set<std::string> m_positional_set{};
|
||||||
|
|
||||||
//mapping from groups to help options
|
//mapping from groups to help options
|
||||||
|
std::vector<std::string> m_group{};
|
||||||
std::map<std::string, HelpGroupDetails> m_help{};
|
std::map<std::string, HelpGroupDetails> m_help{};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2235,6 +2327,7 @@ OptionAdder::operator()
|
|||||||
case 1:
|
case 1:
|
||||||
short_name = *first_short_name_iter;
|
short_name = *first_short_name_iter;
|
||||||
option_names.erase(first_short_name_iter);
|
option_names.erase(first_short_name_iter);
|
||||||
|
CXXOPTS_FALLTHROUGH;
|
||||||
case 0:
|
case 0:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -2326,9 +2419,13 @@ OptionParser::checked_parse_arg
|
|||||||
|
|
||||||
inline
|
inline
|
||||||
void
|
void
|
||||||
OptionParser::add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg)
|
OptionParser::add_to_option(const std::shared_ptr<OptionDetails>& value, const std::string& arg)
|
||||||
{
|
{
|
||||||
parse_option(iter->second, option, arg);
|
auto hash = value->hash();
|
||||||
|
auto& result = m_parsed[hash];
|
||||||
|
result.add(value, arg);
|
||||||
|
|
||||||
|
m_sequential.emplace_back(value->essential_name(), arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
@ -2345,14 +2442,14 @@ OptionParser::consume_positional(const std::string& a, PositionalListIterator& n
|
|||||||
auto& result = m_parsed[iter->second->hash()];
|
auto& result = m_parsed[iter->second->hash()];
|
||||||
if (result.count() == 0)
|
if (result.count() == 0)
|
||||||
{
|
{
|
||||||
add_to_option(iter, *next, a);
|
add_to_option(iter->second, a);
|
||||||
++next;
|
++next;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
++next;
|
++next;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
add_to_option(iter, *next, a);
|
add_to_option(iter->second, a);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
throw_or_mimic<exceptions::no_such_option>(*next);
|
throw_or_mimic<exceptions::no_such_option>(*next);
|
||||||
@ -2616,6 +2713,12 @@ Options::add_option
|
|||||||
}
|
}
|
||||||
|
|
||||||
//add the help details
|
//add the help details
|
||||||
|
|
||||||
|
if (m_help.find(group) == m_help.end())
|
||||||
|
{
|
||||||
|
m_group.push_back(group);
|
||||||
|
}
|
||||||
|
|
||||||
auto& options = m_help[group];
|
auto& options = m_help[group];
|
||||||
|
|
||||||
options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
|
options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
|
||||||
@ -2747,19 +2850,7 @@ inline
|
|||||||
void
|
void
|
||||||
Options::generate_all_groups_help(String& result) const
|
Options::generate_all_groups_help(String& result) const
|
||||||
{
|
{
|
||||||
std::vector<std::string> all_groups;
|
generate_group_help(result, m_group);
|
||||||
|
|
||||||
std::transform(
|
|
||||||
m_help.begin(),
|
|
||||||
m_help.end(),
|
|
||||||
std::back_inserter(all_groups),
|
|
||||||
[] (const std::map<std::string, HelpGroupDetails>::value_type& group)
|
|
||||||
{
|
|
||||||
return group.first;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
generate_group_help(result, all_groups);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
@ -2799,19 +2890,7 @@ inline
|
|||||||
std::vector<std::string>
|
std::vector<std::string>
|
||||||
Options::groups() const
|
Options::groups() const
|
||||||
{
|
{
|
||||||
std::vector<std::string> g;
|
return m_group;
|
||||||
|
|
||||||
std::transform(
|
|
||||||
m_help.begin(),
|
|
||||||
m_help.end(),
|
|
||||||
std::back_inserter(g),
|
|
||||||
[] (const std::map<std::string, HelpGroupDetails>::value_type& pair)
|
|
||||||
{
|
|
||||||
return pair.first;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return g;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
|
|||||||
@ -51,3 +51,10 @@ add_test(add-subdirectory-test ${CMAKE_CTEST_COMMAND}
|
|||||||
|
|
||||||
add_executable(link_test link_a.cpp link_b.cpp)
|
add_executable(link_test link_a.cpp link_b.cpp)
|
||||||
target_link_libraries(link_test cxxopts)
|
target_link_libraries(link_test cxxopts)
|
||||||
|
|
||||||
|
if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") AND ("${CMAKE_SYSTEM}" MATCHES "Linux"))
|
||||||
|
add_executable(fuzzer fuzz.cpp)
|
||||||
|
target_link_libraries(fuzzer PRIVATE cxxopts)
|
||||||
|
target_compile_options(fuzzer PRIVATE -fsanitize=fuzzer)
|
||||||
|
target_link_options(fuzzer PRIVATE -fsanitize=fuzzer)
|
||||||
|
endif()
|
||||||
|
|||||||
24318
test/catch.hpp
24318
test/catch.hpp
File diff suppressed because it is too large
Load Diff
107
test/fuzz.cpp
Normal file
107
test/fuzz.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#include <cassert>
|
||||||
|
#include <cxxopts.hpp>
|
||||||
|
#include <fuzzer/FuzzedDataProvider.h>
|
||||||
|
|
||||||
|
constexpr int kMaxOptions = 1024;
|
||||||
|
constexpr int kMaxArgSize = 1024;
|
||||||
|
|
||||||
|
enum class ParseableTypes
|
||||||
|
{
|
||||||
|
kInt,
|
||||||
|
kString,
|
||||||
|
kVectorString,
|
||||||
|
kFloat,
|
||||||
|
kDouble,
|
||||||
|
|
||||||
|
// Marker for fuzzer.
|
||||||
|
kMaxValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void
|
||||||
|
add_fuzzed_option(cxxopts::Options* options, FuzzedDataProvider* provider)
|
||||||
|
{
|
||||||
|
assert(options);
|
||||||
|
assert(provider);
|
||||||
|
|
||||||
|
options->add_options()(provider->ConsumeRandomLengthString(kMaxArgSize),
|
||||||
|
provider->ConsumeRandomLengthString(kMaxArgSize),
|
||||||
|
cxxopts::value<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" int
|
||||||
|
LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FuzzedDataProvider provider(data, size);
|
||||||
|
|
||||||
|
// Randomly generate a usage string.
|
||||||
|
cxxopts::Options options(provider.ConsumeRandomLengthString(kMaxArgSize),
|
||||||
|
provider.ConsumeRandomLengthString(kMaxArgSize));
|
||||||
|
|
||||||
|
// Randomly generate a set of flags configurations.
|
||||||
|
for (int i = 0; i < provider.ConsumeIntegralInRange<int>(0, kMaxOptions);
|
||||||
|
i++)
|
||||||
|
{
|
||||||
|
switch (provider.ConsumeEnum<ParseableTypes>())
|
||||||
|
{
|
||||||
|
case ParseableTypes::kInt:
|
||||||
|
add_fuzzed_option<int>(&options, &provider);
|
||||||
|
break;
|
||||||
|
case ParseableTypes::kString:
|
||||||
|
add_fuzzed_option<std::string>(&options, &provider);
|
||||||
|
break;
|
||||||
|
case ParseableTypes::kVectorString:
|
||||||
|
add_fuzzed_option<std::vector<std::string>>(&options, &provider);
|
||||||
|
break;
|
||||||
|
case ParseableTypes::kFloat:
|
||||||
|
add_fuzzed_option<float>(&options, &provider);
|
||||||
|
break;
|
||||||
|
case ParseableTypes::kDouble:
|
||||||
|
add_fuzzed_option<double>(&options, &provider);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sometimes allow unrecognised options.
|
||||||
|
if (provider.ConsumeBool())
|
||||||
|
{
|
||||||
|
options.allow_unrecognised_options();
|
||||||
|
}
|
||||||
|
// Sometimes allow trailing positional arguments.
|
||||||
|
if (provider.ConsumeBool())
|
||||||
|
{
|
||||||
|
std::string positional_option_name =
|
||||||
|
provider.ConsumeRandomLengthString(kMaxArgSize);
|
||||||
|
options.add_options()(positional_option_name,
|
||||||
|
provider.ConsumeRandomLengthString(kMaxArgSize),
|
||||||
|
cxxopts::value<std::vector<std::string>>());
|
||||||
|
options.parse_positional({positional_option_name});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build command line input.
|
||||||
|
const int argc = provider.ConsumeIntegralInRange<int>(1, kMaxOptions);
|
||||||
|
|
||||||
|
std::vector<std::string> command_line_container;
|
||||||
|
command_line_container.reserve(argc);
|
||||||
|
|
||||||
|
std::vector<const char*> argv;
|
||||||
|
argv.reserve(argc);
|
||||||
|
|
||||||
|
for (int i = 0; i < argc; i++)
|
||||||
|
{
|
||||||
|
command_line_container.push_back(
|
||||||
|
provider.ConsumeRandomLengthString(kMaxArgSize));
|
||||||
|
argv.push_back(command_line_container[i].c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse command line;
|
||||||
|
auto result = options.parse(argc, argv.data());
|
||||||
|
} catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
149
test/options.cpp
149
test/options.cpp
@ -112,7 +112,7 @@ TEST_CASE("Basic options", "[options]")
|
|||||||
CHECK(arguments[2].key() == "value");
|
CHECK(arguments[2].key() == "value");
|
||||||
CHECK(arguments[3].key() == "av");
|
CHECK(arguments[3].key() == "av");
|
||||||
|
|
||||||
CHECK_THROWS_AS(result["nothing"].as<std::string>(), cxxopts::exceptions::option_has_no_value&);
|
CHECK_THROWS_AS(result["nothing"].as<std::string>(), cxxopts::exceptions::option_has_no_value);
|
||||||
|
|
||||||
CHECK(options.program() == "tester");
|
CHECK(options.program() == "tester");
|
||||||
}
|
}
|
||||||
@ -147,7 +147,7 @@ TEST_CASE("Short options", "[options]")
|
|||||||
CHECK(result["c"].as<std::string>() == "foo=something");
|
CHECK(result["c"].as<std::string>() == "foo=something");
|
||||||
|
|
||||||
REQUIRE_THROWS_AS(options.add_options()("", "nothing option"),
|
REQUIRE_THROWS_AS(options.add_options()("", "nothing option"),
|
||||||
cxxopts::exceptions::invalid_option_format&);
|
cxxopts::exceptions::invalid_option_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("No positional", "[positional]")
|
TEST_CASE("No positional", "[positional]")
|
||||||
@ -238,9 +238,6 @@ TEST_CASE("No positional with extras", "[positional]")
|
|||||||
auto** argv = av.argv();
|
auto** argv = av.argv();
|
||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
|
|
||||||
auto old_argv = argv;
|
|
||||||
auto old_argc = argc;
|
|
||||||
|
|
||||||
auto result = options.parse(argc, argv);
|
auto result = options.parse(argc, argv);
|
||||||
|
|
||||||
auto& unmatched = result.unmatched();
|
auto& unmatched = result.unmatched();
|
||||||
@ -260,7 +257,7 @@ TEST_CASE("Positional not valid", "[positional]") {
|
|||||||
auto** argv = av.argv();
|
auto** argv = av.argv();
|
||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
|
|
||||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::no_such_option&);
|
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::no_such_option);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Positional with empty arguments", "[positional]") {
|
TEST_CASE("Positional with empty arguments", "[positional]") {
|
||||||
@ -288,6 +285,37 @@ TEST_CASE("Positional with empty arguments", "[positional]") {
|
|||||||
REQUIRE(actual == expected);
|
REQUIRE(actual == expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Positional with list delimiter", "[positional]") {
|
||||||
|
std::string single;
|
||||||
|
std::vector<std::string> positional;
|
||||||
|
|
||||||
|
cxxopts::Options options("test_all_positional_list_delimiter", " - test all positional with list delimiters");
|
||||||
|
options.add_options()
|
||||||
|
("single", "Single positional param",
|
||||||
|
cxxopts::value<std::string>(single))
|
||||||
|
("positional", "Positional parameters vector",
|
||||||
|
cxxopts::value<std::vector<std::string>>(positional))
|
||||||
|
;
|
||||||
|
|
||||||
|
Argv av({"tester", "a,b", "c,d", "e"});
|
||||||
|
|
||||||
|
auto argc = av.argc();
|
||||||
|
auto argv = av.argv();
|
||||||
|
|
||||||
|
std::vector<std::string> pos_names = {"single", "positional"};
|
||||||
|
|
||||||
|
options.parse_positional(pos_names.begin(), pos_names.end());
|
||||||
|
|
||||||
|
auto result = options.parse(argc, argv);
|
||||||
|
|
||||||
|
CHECK(result.unmatched().size() == 0);
|
||||||
|
REQUIRE(positional.size() == 2);
|
||||||
|
|
||||||
|
CHECK(single == "a,b");
|
||||||
|
CHECK(positional[0] == "c,d");
|
||||||
|
CHECK(positional[1] == "e");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Empty with implicit value", "[implicit]")
|
TEST_CASE("Empty with implicit value", "[implicit]")
|
||||||
{
|
{
|
||||||
cxxopts::Options options("empty_implicit", "doesn't handle empty");
|
cxxopts::Options options("empty_implicit", "doesn't handle empty");
|
||||||
@ -319,7 +347,7 @@ TEST_CASE("Boolean without implicit value", "[implicit]")
|
|||||||
auto** argv = av.argv();
|
auto** argv = av.argv();
|
||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
|
|
||||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::missing_argument&);
|
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::missing_argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("With equal-separated true") {
|
SECTION("With equal-separated true") {
|
||||||
@ -489,7 +517,7 @@ TEST_CASE("Unsigned integers", "[options]")
|
|||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
|
|
||||||
options.parse_positional("positional");
|
options.parse_positional("positional");
|
||||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type&);
|
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Integer bounds", "[integer]")
|
TEST_CASE("Integer bounds", "[integer]")
|
||||||
@ -524,14 +552,32 @@ TEST_CASE("Overflow on boundary", "[integer]")
|
|||||||
using namespace cxxopts::values;
|
using namespace cxxopts::values;
|
||||||
|
|
||||||
int8_t si;
|
int8_t si;
|
||||||
|
int16_t si16;
|
||||||
|
int64_t si64;
|
||||||
uint8_t ui;
|
uint8_t ui;
|
||||||
|
uint16_t ui16;
|
||||||
|
uint64_t ui64;
|
||||||
|
|
||||||
CHECK_THROWS_AS((integer_parser("128", si)), cxxopts::exceptions::incorrect_argument_type&);
|
CHECK_THROWS_AS((integer_parser("128", si)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
CHECK_THROWS_AS((integer_parser("-129", si)), cxxopts::exceptions::incorrect_argument_type&);
|
CHECK_THROWS_AS((integer_parser("-129", si)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
CHECK_THROWS_AS((integer_parser("256", ui)), cxxopts::exceptions::incorrect_argument_type&);
|
CHECK_THROWS_AS((integer_parser("256", ui)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
CHECK_THROWS_AS((integer_parser("-0x81", si)), cxxopts::exceptions::incorrect_argument_type&);
|
CHECK_THROWS_AS((integer_parser("-0x81", si)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
CHECK_THROWS_AS((integer_parser("0x80", si)), cxxopts::exceptions::incorrect_argument_type&);
|
CHECK_THROWS_AS((integer_parser("0x80", si)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
CHECK_THROWS_AS((integer_parser("0x100", ui)), cxxopts::exceptions::incorrect_argument_type&);
|
CHECK_THROWS_AS((integer_parser("0x100", ui)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
|
|
||||||
|
CHECK_THROWS_AS((integer_parser("65536", ui16)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
|
CHECK_THROWS_AS((integer_parser("75536", ui16)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
|
CHECK_THROWS_AS((integer_parser("32768", si16)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
|
CHECK_THROWS_AS((integer_parser("-32769", si16)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
|
CHECK_THROWS_AS((integer_parser("-42769", si16)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
|
CHECK_THROWS_AS((integer_parser("-75536", si16)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
|
|
||||||
|
CHECK_THROWS_AS((integer_parser("18446744073709551616", ui64)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
|
CHECK_THROWS_AS((integer_parser("28446744073709551616", ui64)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
|
CHECK_THROWS_AS((integer_parser("9223372036854775808", si64)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
|
CHECK_THROWS_AS((integer_parser("-9223372036854775809", si64)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
|
CHECK_THROWS_AS((integer_parser("-10223372036854775809", si64)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
|
CHECK_THROWS_AS((integer_parser("-28446744073709551616", si64)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Integer overflow", "[options]")
|
TEST_CASE("Integer overflow", "[options]")
|
||||||
@ -548,11 +594,11 @@ TEST_CASE("Integer overflow", "[options]")
|
|||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
|
|
||||||
options.parse_positional("positional");
|
options.parse_positional("positional");
|
||||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type&);
|
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type);
|
||||||
|
|
||||||
int integer = 0;
|
int integer = 0;
|
||||||
CHECK_THROWS_AS((integer_parser("23423423423", integer)), cxxopts::exceptions::incorrect_argument_type&);
|
CHECK_THROWS_AS((integer_parser("23423423423", integer)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
CHECK_THROWS_AS((integer_parser("234234234234", integer)), cxxopts::exceptions::incorrect_argument_type&);
|
CHECK_THROWS_AS((integer_parser("234234234234", integer)), cxxopts::exceptions::incorrect_argument_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Floats", "[options]")
|
TEST_CASE("Floats", "[options]")
|
||||||
@ -593,7 +639,7 @@ TEST_CASE("Invalid integers", "[integer]") {
|
|||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
|
|
||||||
options.parse_positional("positional");
|
options.parse_positional("positional");
|
||||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type&);
|
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Booleans", "[boolean]") {
|
TEST_CASE("Booleans", "[boolean]") {
|
||||||
@ -663,11 +709,13 @@ TEST_CASE("std::vector", "[vector]") {
|
|||||||
#ifdef CXXOPTS_HAS_OPTIONAL
|
#ifdef CXXOPTS_HAS_OPTIONAL
|
||||||
TEST_CASE("std::optional", "[optional]") {
|
TEST_CASE("std::optional", "[optional]") {
|
||||||
std::optional<std::string> optional;
|
std::optional<std::string> optional;
|
||||||
|
std::optional<bool> opt_bool;
|
||||||
cxxopts::Options options("optional", " - tests optional");
|
cxxopts::Options options("optional", " - tests optional");
|
||||||
options.add_options()
|
options.add_options()
|
||||||
("optional", "an optional option", cxxopts::value<std::optional<std::string>>(optional));
|
("optional", "an optional option", cxxopts::value<std::optional<std::string>>(optional))
|
||||||
|
("optional_bool", "an boolean optional", cxxopts::value<std::optional<bool>>(opt_bool)->default_value("false"));
|
||||||
|
|
||||||
Argv av({"optional", "--optional", "foo"});
|
Argv av({"optional", "--optional", "foo", "--optional_bool", "true"});
|
||||||
|
|
||||||
auto** argv = av.argv();
|
auto** argv = av.argv();
|
||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
@ -676,6 +724,8 @@ TEST_CASE("std::optional", "[optional]") {
|
|||||||
|
|
||||||
REQUIRE(optional.has_value());
|
REQUIRE(optional.has_value());
|
||||||
CHECK(*optional == "foo");
|
CHECK(*optional == "foo");
|
||||||
|
CHECK(opt_bool.has_value());
|
||||||
|
CHECK(*opt_bool);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -699,7 +749,7 @@ TEST_CASE("Unrecognised options", "[options]") {
|
|||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
|
|
||||||
SECTION("Default behaviour") {
|
SECTION("Default behaviour") {
|
||||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::no_such_option&);
|
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::no_such_option);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("After allowing unrecognised options") {
|
SECTION("After allowing unrecognised options") {
|
||||||
@ -726,7 +776,7 @@ TEST_CASE("Allow bad short syntax", "[options]") {
|
|||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
|
|
||||||
SECTION("Default behaviour") {
|
SECTION("Default behaviour") {
|
||||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::invalid_option_syntax&);
|
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::invalid_option_syntax);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("After allowing unrecognised options") {
|
SECTION("After allowing unrecognised options") {
|
||||||
@ -749,7 +799,7 @@ TEST_CASE("Invalid option syntax", "[options]") {
|
|||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
|
|
||||||
SECTION("Default behaviour") {
|
SECTION("Default behaviour") {
|
||||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::invalid_option_syntax&);
|
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::invalid_option_syntax);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -768,9 +818,60 @@ TEST_CASE("Options empty", "[options]") {
|
|||||||
auto** argv = argv_.argv();
|
auto** argv = argv_.argv();
|
||||||
|
|
||||||
CHECK(options.groups().empty());
|
CHECK(options.groups().empty());
|
||||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::no_such_option&);
|
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::no_such_option);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CXXOPTS_HAS_OPTIONAL
|
||||||
|
TEST_CASE("Optional value", "[optional]")
|
||||||
|
{
|
||||||
|
cxxopts::Options options("options", "query as std::optional");
|
||||||
|
options.add_options()
|
||||||
|
("int", "Integer", cxxopts::value<int>())
|
||||||
|
("float", "Float", cxxopts::value<float>())
|
||||||
|
("string", "String", cxxopts::value<std::string>())
|
||||||
|
;
|
||||||
|
|
||||||
|
SECTION("Available") {
|
||||||
|
Argv av({
|
||||||
|
"--int",
|
||||||
|
"42",
|
||||||
|
"--float",
|
||||||
|
"3.141",
|
||||||
|
"--string",
|
||||||
|
"Hello"
|
||||||
|
});
|
||||||
|
|
||||||
|
auto** argv = av.argv();
|
||||||
|
auto argc = av.argc();
|
||||||
|
|
||||||
|
auto result = options.parse(argc, argv);
|
||||||
|
|
||||||
|
CHECK(result.as_optional<int>("int"));
|
||||||
|
CHECK(result.as_optional<float>("float"));
|
||||||
|
CHECK(result.as_optional<string>("string"));
|
||||||
|
|
||||||
|
CHECK(*result.as_optional<int>("int") == 42);
|
||||||
|
CHECK(*result.as_optional<float>("float") == 3.141);
|
||||||
|
CHECK(*result.as_optional<string>("string") == "Hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Unavailable") {
|
||||||
|
Argv av({
|
||||||
|
});
|
||||||
|
|
||||||
|
auto** argv = av.argv();
|
||||||
|
auto argc = av.argc();
|
||||||
|
|
||||||
|
auto result = options.parse(argc, argv);
|
||||||
|
|
||||||
|
CHECK(!result.as_optional<int>("int"));
|
||||||
|
CHECK(!result.as_optional<float>("float"));
|
||||||
|
CHECK(!result.as_optional<string>("string"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST_CASE("Initializer list with group", "[options]") {
|
TEST_CASE("Initializer list with group", "[options]") {
|
||||||
cxxopts::Options options("Initializer list group", " - test initializer list with group");
|
cxxopts::Options options("Initializer list group", " - test initializer list with group");
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user