Merge branch '2_0'
This commit is contained in:
commit
8893afe13c
29
CHANGELOG.md
Normal file
29
CHANGELOG.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Changelog
|
||||
|
||||
This is the changelog for `cxxopts`, a C++11 library for parsing command line
|
||||
options. The project adheres to semantic versioning.
|
||||
|
||||
## 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.
|
||||
19
README.md
19
README.md
@ -39,12 +39,12 @@ Any type can be given as long as it can be parsed, with operator>>.
|
||||
|
||||
To parse the command line do:
|
||||
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
To retrieve an option use `options.count("option")` to get the number of times
|
||||
To retrieve an option use `result.count("option")` to get the number of times
|
||||
it appeared, and
|
||||
|
||||
options["opt"].as<type>()
|
||||
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.
|
||||
@ -84,6 +84,17 @@ 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 either
|
||||
`=value` or the next argument.
|
||||
|
||||
# Linking
|
||||
|
||||
This is a header only library.
|
||||
@ -93,9 +104,7 @@ This is a header only library.
|
||||
The only build requirement is a C++ compiler that supports C++11 regular
|
||||
expressions. For example GCC >= 4.9 or clang with libc++.
|
||||
|
||||
|
||||
# TODO list
|
||||
|
||||
* Allow unrecognised options.
|
||||
* Various help strings.
|
||||
* Unicode aware for help strings.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -31,7 +31,9 @@ int main(int argc, char* argv[])
|
||||
try
|
||||
{
|
||||
cxxopts::Options options(argv[0], " - example command line options");
|
||||
options.positional_help("[optional args]");
|
||||
options
|
||||
.positional_help("[optional args]")
|
||||
.show_positional_help();
|
||||
|
||||
bool apple = false;
|
||||
|
||||
@ -63,9 +65,9 @@ int main(int argc, char* argv[])
|
||||
|
||||
options.parse_positional({"input", "output", "positional"});
|
||||
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
if (options.count("help"))
|
||||
if (result.count("help"))
|
||||
{
|
||||
std::cout << options.help({"", "Group"}) << std::endl;
|
||||
exit(0);
|
||||
@ -73,18 +75,18 @@ int main(int argc, char* argv[])
|
||||
|
||||
if (apple)
|
||||
{
|
||||
std::cout << "Saw option ‘a’ " << options.count("a") << " times " <<
|
||||
std::cout << "Saw option ‘a’ " << result.count("a") << " times " <<
|
||||
std::endl;
|
||||
}
|
||||
|
||||
if (options.count("b"))
|
||||
if (result.count("b"))
|
||||
{
|
||||
std::cout << "Saw option ‘b’" << std::endl;
|
||||
}
|
||||
|
||||
if (options.count("f"))
|
||||
if (result.count("f"))
|
||||
{
|
||||
auto& ff = options["f"].as<std::vector<std::string>>();
|
||||
auto& ff = result["f"].as<std::vector<std::string>>();
|
||||
std::cout << "Files" << std::endl;
|
||||
for (const auto& f : ff)
|
||||
{
|
||||
@ -92,36 +94,36 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
|
||||
if (options.count("input"))
|
||||
if (result.count("input"))
|
||||
{
|
||||
std::cout << "Input = " << options["input"].as<std::string>()
|
||||
std::cout << "Input = " << result["input"].as<std::string>()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
if (options.count("output"))
|
||||
if (result.count("output"))
|
||||
{
|
||||
std::cout << "Output = " << options["output"].as<std::string>()
|
||||
std::cout << "Output = " << result["output"].as<std::string>()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
if (options.count("positional"))
|
||||
if (result.count("positional"))
|
||||
{
|
||||
std::cout << "Positional = {";
|
||||
auto& v = options["positional"].as<std::vector<std::string>>();
|
||||
auto& v = result["positional"].as<std::vector<std::string>>();
|
||||
for (const auto& s : v) {
|
||||
std::cout << s << ", ";
|
||||
}
|
||||
std::cout << "}" << std::endl;
|
||||
}
|
||||
|
||||
if (options.count("int"))
|
||||
if (result.count("int"))
|
||||
{
|
||||
std::cout << "int = " << options["int"].as<int>() << std::endl;
|
||||
std::cout << "int = " << result["int"].as<int>() << std::endl;
|
||||
}
|
||||
|
||||
if (options.count("float"))
|
||||
if (result.count("float"))
|
||||
{
|
||||
std::cout << "float = " << options["float"].as<float>() << std::endl;
|
||||
std::cout << "float = " << result["float"].as<float>() << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "Arguments remain = " << argc << std::endl;
|
||||
|
||||
@ -29,4 +29,7 @@ if (CXXOPTS_BUILD_TESTS)
|
||||
"-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()
|
||||
|
||||
6
test/link_a.cpp
Normal file
6
test/link_a.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "cxxopts.hpp"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
1
test/link_b.cpp
Normal file
1
test/link_b.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include <cxxopts.hpp>
|
||||
172
test/options.cpp
172
test/options.cpp
@ -71,17 +71,27 @@ TEST_CASE("Basic options", "[options]")
|
||||
char** actual_argv = argv.argv();
|
||||
auto argc = argv.argc();
|
||||
|
||||
options.parse(argc, actual_argv);
|
||||
auto result = options.parse(argc, actual_argv);
|
||||
|
||||
CHECK(options.count("long") == 1);
|
||||
CHECK(options.count("s") == 1);
|
||||
CHECK(options.count("value") == 1);
|
||||
CHECK(options.count("a") == 1);
|
||||
CHECK(options["value"].as<std::string>() == "value");
|
||||
CHECK(options["a"].as<std::string>() == "b");
|
||||
CHECK(options.count("6") == 1);
|
||||
CHECK(options.count("p") == 2);
|
||||
CHECK(options.count("space") == 2);
|
||||
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");
|
||||
}
|
||||
|
||||
TEST_CASE("Short options", "[options]")
|
||||
@ -96,36 +106,15 @@ TEST_CASE("Short options", "[options]")
|
||||
auto actual_argv = argv.argv();
|
||||
auto argc = argv.argc();
|
||||
|
||||
options.parse(argc, actual_argv);
|
||||
auto result = options.parse(argc, actual_argv);
|
||||
|
||||
CHECK(options.count("a") == 1);
|
||||
CHECK(options["a"].as<std::string>() == "value");
|
||||
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("Required arguments", "[options]")
|
||||
{
|
||||
cxxopts::Options options("required", " - test required options");
|
||||
options.add_options()
|
||||
("one", "one option")
|
||||
("two", "second option")
|
||||
;
|
||||
|
||||
Argv argv({
|
||||
"required",
|
||||
"--one"
|
||||
});
|
||||
|
||||
auto aargv = argv.argv();
|
||||
auto argc = argv.argc();
|
||||
|
||||
options.parse(argc, aargv);
|
||||
REQUIRE_THROWS_AS(cxxopts::check_required(options, {"two"}),
|
||||
cxxopts::option_required_exception);
|
||||
}
|
||||
|
||||
TEST_CASE("No positional", "[positional]")
|
||||
{
|
||||
cxxopts::Options options("test_no_positional",
|
||||
@ -135,7 +124,7 @@ TEST_CASE("No positional", "[positional]")
|
||||
|
||||
char** argv = av.argv();
|
||||
auto argc = av.argc();
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
REQUIRE(argc == 4);
|
||||
CHECK(strcmp(argv[1], "a") == 0);
|
||||
@ -158,7 +147,7 @@ TEST_CASE("All positional", "[positional]")
|
||||
|
||||
options.parse_positional("positional");
|
||||
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
REQUIRE(argc == 1);
|
||||
REQUIRE(positional.size() == 3);
|
||||
@ -186,14 +175,14 @@ TEST_CASE("Some positional explicit", "[positional]")
|
||||
char** argv = av.argv();
|
||||
auto argc = av.argc();
|
||||
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
CHECK(argc == 1);
|
||||
CHECK(options.count("output"));
|
||||
CHECK(options["input"].as<std::string>() == "b");
|
||||
CHECK(options["output"].as<std::string>() == "a");
|
||||
CHECK(result.count("output"));
|
||||
CHECK(result["input"].as<std::string>() == "b");
|
||||
CHECK(result["output"].as<std::string>() == "a");
|
||||
|
||||
auto& positional = options["positional"].as<std::vector<std::string>>();
|
||||
auto& positional = result["positional"].as<std::vector<std::string>>();
|
||||
|
||||
REQUIRE(positional.size() == 2);
|
||||
CHECK(positional[0] == "c");
|
||||
@ -234,10 +223,58 @@ TEST_CASE("Empty with implicit value", "[implicit]")
|
||||
char** argv = av.argv();
|
||||
auto argc = av.argc();
|
||||
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
REQUIRE(options.count("implicit") == 1);
|
||||
REQUIRE(options["implicit"].as<std::string>() == "");
|
||||
REQUIRE(result.count("implicit") == 1);
|
||||
REQUIRE(result["implicit"].as<std::string>() == "");
|
||||
}
|
||||
|
||||
TEST_CASE("Default values", "[default]")
|
||||
{
|
||||
cxxopts::Options options("defaults", "has defaults");
|
||||
options.add_options()
|
||||
("default", "Has implicit", cxxopts::value<int>()
|
||||
->default_value("42"));
|
||||
|
||||
SECTION("Sets defaults") {
|
||||
Argv av({"implicit"});
|
||||
|
||||
char** argv = av.argv();
|
||||
auto argc = av.argc();
|
||||
|
||||
auto result = options.parse(argc, argv);
|
||||
CHECK(result.count("default") == 1);
|
||||
CHECK(result["default"].as<int>() == 42);
|
||||
}
|
||||
|
||||
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]")
|
||||
@ -252,11 +289,12 @@ TEST_CASE("Integers", "[options]")
|
||||
auto argc = av.argc();
|
||||
|
||||
options.parse_positional("positional");
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
REQUIRE(options.count("positional") == 7);
|
||||
REQUIRE(result.count("positional") == 7);
|
||||
|
||||
auto& positional = options["positional"].as<std::vector<int>>();
|
||||
auto& positional = result["positional"].as<std::vector<int>>();
|
||||
REQUIRE(positional.size() == 7);
|
||||
CHECK(positional[0] == 5);
|
||||
CHECK(positional[1] == 6);
|
||||
CHECK(positional[2] == -6);
|
||||
@ -295,11 +333,11 @@ TEST_CASE("Integer bounds", "[integer]")
|
||||
auto argc = av.argc();
|
||||
|
||||
options.parse_positional("positional");
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
REQUIRE(options.count("positional") == 5);
|
||||
REQUIRE(result.count("positional") == 5);
|
||||
|
||||
auto& positional = options["positional"].as<std::vector<int8_t>>();
|
||||
auto& positional = result["positional"].as<std::vector<int8_t>>();
|
||||
CHECK(positional[0] == 127);
|
||||
CHECK(positional[1] == -128);
|
||||
CHECK(positional[2] == 0x7f);
|
||||
@ -351,14 +389,14 @@ TEST_CASE("Floats", "[options]")
|
||||
auto argc = av.argc();
|
||||
|
||||
options.parse_positional("positional");
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
REQUIRE(options.count("double") == 1);
|
||||
REQUIRE(options.count("positional") == 4);
|
||||
REQUIRE(result.count("double") == 1);
|
||||
REQUIRE(result.count("positional") == 4);
|
||||
|
||||
CHECK(options["double"].as<double>() == 0.5);
|
||||
CHECK(result["double"].as<double>() == 0.5);
|
||||
|
||||
auto& positional = options["positional"].as<std::vector<float>>();
|
||||
auto& positional = result["positional"].as<std::vector<float>>();
|
||||
CHECK(positional[0] == 4);
|
||||
CHECK(positional[1] == -4);
|
||||
CHECK(positional[2] == 1.5e6);
|
||||
@ -378,3 +416,27 @@ TEST_CASE("Invalid integers", "[integer]") {
|
||||
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>())
|
||||
;
|
||||
|
||||
Argv av({"booleans", "--bool=false", "--debug", "true", "--timing"});
|
||||
|
||||
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);
|
||||
|
||||
CHECK(result["bool"].as<bool>() == false);
|
||||
CHECK(result["debug"].as<bool>() == true);
|
||||
CHECK(result["timing"].as<bool>() == true);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user