Compare commits

..

No commits in common. "master" and "fix_iter_access" have entirely different histories.

11 changed files with 8753 additions and 16636 deletions

View File

@ -1,24 +0,0 @@
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

View File

@ -20,9 +20,11 @@ jobs:
build-ubuntu:
strategy:
matrix:
os: [ ubuntu-20.04, ubuntu-22.04 ]
os: [ ubuntu-18.04, ubuntu-20.04, ubuntu-22.04 ]
compiler: [ g++-9, g++-10, clang++ ]
include:
- os: ubuntu-18.04
compiler: g++-7
name: Build and Test on Ubuntu
runs-on: ${{matrix.os}}
steps:

View File

@ -6,11 +6,3 @@ cc_library(
strip_include_prefix = "include",
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"],
)

View File

@ -3,51 +3,17 @@
This is the changelog for `cxxopts`, a C++11 library for parsing command line
options. The project adheres to semantic versioning.
## 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
## Unreleased
### Added
* Support for multiple long names for the same option (= multiple long aliases)
* Add a `program()` function to retrieve the program name.
* Added a .clang-format file.
* Added iterator and printing for a ParseResult.
### Changed
* Cleanup exception code, add cxxopts::exceptions namespace.
* Renamed several exceptions to be more descriptive, and added to a nested namespace.
### Bug Fixes

View File

@ -85,6 +85,9 @@ 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.
## Unrecognised arguments
You can allow unrecognised arguments to be skipped. This applies to both
@ -193,8 +196,8 @@ therefore, `-o false` does not work.
## `std::vector<T>` values
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 define symbol `CXXOPTS_VECTOR_DELIMITER`
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>`:
@ -275,3 +278,7 @@ 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.

View File

@ -1,16 +0,0 @@
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()

View File

@ -27,7 +27,6 @@ THE SOFTWARE.
#ifndef CXXOPTS_HPP_INCLUDED
#define CXXOPTS_HPP_INCLUDED
#include <cstdlib>
#include <cstring>
#include <exception>
#include <limits>
@ -41,7 +40,6 @@ THE SOFTWARE.
#include <utility>
#include <vector>
#include <algorithm>
#include <locale>
#ifdef CXXOPTS_NO_EXCEPTIONS
#include <iostream>
@ -56,8 +54,8 @@ THE SOFTWARE.
#define CXXOPTS_LINKONCE_CONST __declspec(selectany) extern
#define CXXOPTS_LINKONCE __declspec(selectany) extern
#else
#define CXXOPTS_LINKONCE_CONST
#define CXXOPTS_LINKONCE
#define CXXOPTS_LINKONCE_CONST
#define CXXOPTS_LINKONCE
#endif
#ifndef CXXOPTS_NO_REGEX
@ -74,14 +72,6 @@ THE SOFTWARE.
# 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
#define CXXOPTS_NODISCARD [[nodiscard]]
#else
@ -93,8 +83,8 @@ THE SOFTWARE.
#endif
#define CXXOPTS__VERSION_MAJOR 3
#define CXXOPTS__VERSION_MINOR 2
#define CXXOPTS__VERSION_PATCH 1
#define CXXOPTS__VERSION_MINOR 0
#define CXXOPTS__VERSION_PATCH 0
#if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6
#define CXXOPTS_NULL_DEREF_IGNORE
@ -239,10 +229,10 @@ stringAppend(String& s, Iterator begin, Iterator end)
}
inline
size_t
std::size_t
stringLength(const String& s)
{
return static_cast<size_t>(s.length());
return s.length();
}
inline
@ -346,8 +336,13 @@ empty(const std::string& s)
namespace cxxopts {
namespace {
#ifdef _WIN32
CXXOPTS_LINKONCE_CONST std::string LQUOTE("\'");
CXXOPTS_LINKONCE_CONST std::string RQUOTE("\'");
#else
CXXOPTS_LINKONCE_CONST std::string LQUOTE("");
CXXOPTS_LINKONCE_CONST std::string RQUOTE("");
#endif
} // namespace
// GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we
@ -369,9 +364,6 @@ class Value : public std::enable_shared_from_this<Value>
std::shared_ptr<Value>
clone() const = 0;
virtual void
add(const std::string& text) const = 0;
virtual void
parse(const std::string& text) const = 0;
@ -765,31 +757,29 @@ inline ArguDesc ParseArgument(const char *arg, bool &matched)
namespace {
CXXOPTS_LINKONCE
const char* const integer_pattern =
"(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)";
std::basic_regex<char> integer_pattern
("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
CXXOPTS_LINKONCE
const char* const truthy_pattern =
"(t|T)(rue)?|1";
std::basic_regex<char> truthy_pattern
("(t|T)(rue)?|1");
CXXOPTS_LINKONCE
const char* const falsy_pattern =
"(f|F)(alse)?|0";
std::basic_regex<char> falsy_pattern
("(f|F)(alse)?|0");
CXXOPTS_LINKONCE
const char* const option_pattern =
"--([[:alnum:]][-_[:alnum:]\\.]+)(=(.*))?|-([[:alnum:]].*)";
std::basic_regex<char> option_matcher
("--([[:alnum:]][-_[:alnum:]\\.]+)(=(.*))?|-([[:alnum:]].*)");
CXXOPTS_LINKONCE
const char* const option_specifier_pattern =
"([[:alnum:]][-_[:alnum:]\\.]*)(,[ ]*[[:alnum:]][-_[:alnum:]]*)*";
std::basic_regex<char> option_specifier
("([[:alnum:]][-_[:alnum:]\\.]*)(,[ ]*[[:alnum:]][-_[:alnum:]]*)*");
CXXOPTS_LINKONCE
const char* const option_specifier_separator_pattern = ", *";
std::basic_regex<char> option_specifier_separator(", *");
} // namespace
inline IntegerDesc SplitInteger(const std::string &text)
{
static const std::basic_regex<char> integer_matcher(integer_pattern);
std::smatch match;
std::regex_match(text, match, integer_matcher);
std::regex_match(text, match, integer_pattern);
if (match.length() == 0)
{
@ -813,17 +803,15 @@ inline IntegerDesc SplitInteger(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::regex_match(text, result, truthy_matcher);
std::regex_match(text, result, truthy_pattern);
return !result.empty();
}
inline bool IsFalseText(const std::string &text)
{
static const std::basic_regex<char> falsy_matcher(falsy_pattern);
std::smatch result;
std::regex_match(text, result, falsy_matcher);
std::regex_match(text, result, falsy_pattern);
return !result.empty();
}
@ -832,25 +820,22 @@ inline bool IsFalseText(const std::string &text)
// (without considering which or how many are single-character)
inline OptionNames split_option_names(const std::string &text)
{
static const std::basic_regex<char> option_specifier_matcher(option_specifier_pattern);
if (!std::regex_match(text.c_str(), option_specifier_matcher))
if (!std::regex_match(text.c_str(), option_specifier))
{
throw_or_mimic<exceptions::invalid_option_format>(text);
}
OptionNames split_names;
static const std::basic_regex<char> option_specifier_separator_matcher(option_specifier_separator_pattern);
constexpr int use_non_matches { -1 };
auto token_iterator = std::sregex_token_iterator(
text.begin(), text.end(), option_specifier_separator_matcher, use_non_matches);
text.begin(), text.end(), option_specifier_separator, use_non_matches);
std::copy(token_iterator, std::sregex_token_iterator(), std::back_inserter(split_names));
return split_names;
}
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::regex_match(arg, result, option_matcher);
matched = !result.empty();
@ -973,26 +958,13 @@ integer_parser(const std::string& text, T& value)
throw_or_mimic<exceptions::incorrect_argument_type>(text);
}
US limit = 0;
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)
const US next = static_cast<US>(result * base + digit);
if (result > next)
{
throw_or_mimic<exceptions::incorrect_argument_type>(text);
}
result = static_cast<US>(result * base + digit);
result = next;
}
detail::check_signed_range<T>(negative, result, text);
@ -1062,6 +1034,25 @@ parse_value(const std::string& text, T& 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
template <typename T>
void
@ -1084,41 +1075,6 @@ void parse_value(const std::string& text, char& c)
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>
struct type_is_container
{
@ -1170,12 +1126,6 @@ class abstract_value : public Value
m_implicit_value = rhs.m_implicit_value;
}
void
add(const std::string& text) const override
{
add_value(text, *m_store);
}
void
parse(const std::string& text) const override
{
@ -1461,19 +1411,6 @@ struct HelpGroupDetails
class OptionValue
{
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
parse
(
@ -1538,15 +1475,6 @@ CXXOPTS_DIAGNOSTIC_POP
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:
void
ensure_value(const std::shared_ptr<const OptionDetails>& details)
@ -1569,7 +1497,7 @@ CXXOPTS_DIAGNOSTIC_POP
class KeyValue
{
public:
KeyValue(std::string key_, std::string value_) noexcept
KeyValue(std::string key_, std::string value_)
: m_key(std::move(key_))
, m_value(std::move(value_))
{
@ -1622,8 +1550,7 @@ class ParseResult
Iterator(const Iterator&) = default;
// GCC complains about m_iter not being initialised in the member
// initializer list
CXXOPTS_DIAGNOSTIC_PUSH
// initializer list
CXXOPTS_IGNORE_WARNING("-Weffc++")
Iterator(const ParseResult *pr, bool end=false)
: m_pr(pr)
@ -1759,24 +1686,6 @@ CXXOPTS_DIAGNOSTIC_POP
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>&
arguments() const
{
@ -1871,7 +1780,7 @@ class OptionParser
);
void
add_to_option(const std::shared_ptr<OptionDetails>& value, const std::string& arg);
add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg);
void
parse_option
@ -2074,7 +1983,6 @@ class Options
std::unordered_set<std::string> m_positional_set{};
//mapping from groups to help options
std::vector<std::string> m_group{};
std::map<std::string, HelpGroupDetails> m_help{};
};
@ -2327,7 +2235,6 @@ OptionAdder::operator()
case 1:
short_name = *first_short_name_iter;
option_names.erase(first_short_name_iter);
CXXOPTS_FALLTHROUGH;
case 0:
break;
default:
@ -2419,13 +2326,9 @@ OptionParser::checked_parse_arg
inline
void
OptionParser::add_to_option(const std::shared_ptr<OptionDetails>& value, const std::string& arg)
OptionParser::add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg)
{
auto hash = value->hash();
auto& result = m_parsed[hash];
result.add(value, arg);
m_sequential.emplace_back(value->essential_name(), arg);
parse_option(iter->second, option, arg);
}
inline
@ -2442,14 +2345,14 @@ OptionParser::consume_positional(const std::string& a, PositionalListIterator& n
auto& result = m_parsed[iter->second->hash()];
if (result.count() == 0)
{
add_to_option(iter->second, a);
add_to_option(iter, *next, a);
++next;
return true;
}
++next;
continue;
}
add_to_option(iter->second, a);
add_to_option(iter, *next, a);
return true;
}
throw_or_mimic<exceptions::no_such_option>(*next);
@ -2713,12 +2616,6 @@ Options::add_option
}
//add the help details
if (m_help.find(group) == m_help.end())
{
m_group.push_back(group);
}
auto& options = m_help[group];
options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
@ -2850,7 +2747,19 @@ inline
void
Options::generate_all_groups_help(String& result) const
{
generate_group_help(result, m_group);
std::vector<std::string> all_groups;
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
@ -2890,7 +2799,19 @@ inline
std::vector<std::string>
Options::groups() const
{
return m_group;
std::vector<std::string> g;
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

View File

@ -51,10 +51,3 @@ add_test(add-subdirectory-test ${CMAKE_CTEST_COMMAND}
add_executable(link_test link_a.cpp link_b.cpp)
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()

File diff suppressed because it is too large Load Diff

View File

@ -1,107 +0,0 @@
#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;
}

View File

@ -112,7 +112,7 @@ TEST_CASE("Basic options", "[options]")
CHECK(arguments[2].key() == "value");
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");
}
@ -147,7 +147,7 @@ TEST_CASE("Short options", "[options]")
CHECK(result["c"].as<std::string>() == "foo=something");
REQUIRE_THROWS_AS(options.add_options()("", "nothing option"),
cxxopts::exceptions::invalid_option_format);
cxxopts::exceptions::invalid_option_format&);
}
TEST_CASE("No positional", "[positional]")
@ -238,6 +238,9 @@ TEST_CASE("No positional with extras", "[positional]")
auto** argv = av.argv();
auto argc = av.argc();
auto old_argv = argv;
auto old_argc = argc;
auto result = options.parse(argc, argv);
auto& unmatched = result.unmatched();
@ -257,7 +260,7 @@ TEST_CASE("Positional not valid", "[positional]") {
auto** argv = av.argv();
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]") {
@ -285,37 +288,6 @@ TEST_CASE("Positional with empty arguments", "[positional]") {
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]")
{
cxxopts::Options options("empty_implicit", "doesn't handle empty");
@ -347,7 +319,7 @@ TEST_CASE("Boolean without implicit value", "[implicit]")
auto** argv = av.argv();
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") {
@ -517,7 +489,7 @@ TEST_CASE("Unsigned integers", "[options]")
auto argc = av.argc();
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]")
@ -552,32 +524,14 @@ TEST_CASE("Overflow on boundary", "[integer]")
using namespace cxxopts::values;
int8_t si;
int16_t si16;
int64_t si64;
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("-129", si)), 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("0x80", si)), 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);
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("256", ui)), 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("0x100", ui)), cxxopts::exceptions::incorrect_argument_type&);
}
TEST_CASE("Integer overflow", "[options]")
@ -594,11 +548,11 @@ TEST_CASE("Integer overflow", "[options]")
auto argc = av.argc();
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;
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("23423423423", integer)), cxxopts::exceptions::incorrect_argument_type&);
CHECK_THROWS_AS((integer_parser("234234234234", integer)), cxxopts::exceptions::incorrect_argument_type&);
}
TEST_CASE("Floats", "[options]")
@ -639,7 +593,7 @@ TEST_CASE("Invalid integers", "[integer]") {
auto argc = av.argc();
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]") {
@ -709,13 +663,11 @@ TEST_CASE("std::vector", "[vector]") {
#ifdef CXXOPTS_HAS_OPTIONAL
TEST_CASE("std::optional", "[optional]") {
std::optional<std::string> optional;
std::optional<bool> opt_bool;
cxxopts::Options options("optional", " - tests optional");
options.add_options()
("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"));
("optional", "an optional option", cxxopts::value<std::optional<std::string>>(optional));
Argv av({"optional", "--optional", "foo", "--optional_bool", "true"});
Argv av({"optional", "--optional", "foo"});
auto** argv = av.argv();
auto argc = av.argc();
@ -724,8 +676,6 @@ TEST_CASE("std::optional", "[optional]") {
REQUIRE(optional.has_value());
CHECK(*optional == "foo");
CHECK(opt_bool.has_value());
CHECK(*opt_bool);
}
#endif
@ -749,7 +699,7 @@ TEST_CASE("Unrecognised options", "[options]") {
auto argc = av.argc();
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") {
@ -776,7 +726,7 @@ TEST_CASE("Allow bad short syntax", "[options]") {
auto argc = av.argc();
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") {
@ -799,7 +749,7 @@ TEST_CASE("Invalid option syntax", "[options]") {
auto argc = av.argc();
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&);
}
}
@ -818,60 +768,9 @@ TEST_CASE("Options empty", "[options]") {
auto** argv = argv_.argv();
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]") {
cxxopts::Options options("Initializer list group", " - test initializer list with group");