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:
|
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
|
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
|
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.
|
||||||
@ -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"`,
|
writing it on the command line as `--option` would give the value `"implicit"`,
|
||||||
and writing `--option=another` would give it the value `"another"`.
|
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
|
# Linking
|
||||||
|
|
||||||
This is a header only library.
|
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
|
The only build requirement is a C++ compiler that supports C++11 regular
|
||||||
expressions. For example GCC >= 4.9 or clang with libc++.
|
expressions. For example GCC >= 4.9 or clang with libc++.
|
||||||
|
|
||||||
|
|
||||||
# TODO list
|
# TODO list
|
||||||
|
|
||||||
* Allow unrecognised options.
|
* Allow unrecognised options.
|
||||||
* Various help strings.
|
* 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
|
try
|
||||||
{
|
{
|
||||||
cxxopts::Options options(argv[0], " - example command line options");
|
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;
|
bool apple = false;
|
||||||
|
|
||||||
@ -63,9 +65,9 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
options.parse_positional({"input", "output", "positional"});
|
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;
|
std::cout << options.help({"", "Group"}) << std::endl;
|
||||||
exit(0);
|
exit(0);
|
||||||
@ -73,18 +75,18 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
if (apple)
|
if (apple)
|
||||||
{
|
{
|
||||||
std::cout << "Saw option ‘a’ " << options.count("a") << " times " <<
|
std::cout << "Saw option ‘a’ " << result.count("a") << " times " <<
|
||||||
std::endl;
|
std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.count("b"))
|
if (result.count("b"))
|
||||||
{
|
{
|
||||||
std::cout << "Saw option ‘b’" << std::endl;
|
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;
|
std::cout << "Files" << std::endl;
|
||||||
for (const auto& f : ff)
|
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;
|
<< 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;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.count("positional"))
|
if (result.count("positional"))
|
||||||
{
|
{
|
||||||
std::cout << "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) {
|
for (const auto& s : v) {
|
||||||
std::cout << s << ", ";
|
std::cout << s << ", ";
|
||||||
}
|
}
|
||||||
std::cout << "}" << std::endl;
|
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;
|
std::cout << "Arguments remain = " << argc << std::endl;
|
||||||
|
|||||||
@ -29,4 +29,7 @@ if (CXXOPTS_BUILD_TESTS)
|
|||||||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
||||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
|
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_executable(link_test link_a.cpp link_b.cpp)
|
||||||
|
target_link_libraries(link_test cxxopts)
|
||||||
endif()
|
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();
|
char** actual_argv = argv.argv();
|
||||||
auto argc = argv.argc();
|
auto argc = argv.argc();
|
||||||
|
|
||||||
options.parse(argc, actual_argv);
|
auto result = options.parse(argc, actual_argv);
|
||||||
|
|
||||||
CHECK(options.count("long") == 1);
|
CHECK(result.count("long") == 1);
|
||||||
CHECK(options.count("s") == 1);
|
CHECK(result.count("s") == 1);
|
||||||
CHECK(options.count("value") == 1);
|
CHECK(result.count("value") == 1);
|
||||||
CHECK(options.count("a") == 1);
|
CHECK(result.count("a") == 1);
|
||||||
CHECK(options["value"].as<std::string>() == "value");
|
CHECK(result["value"].as<std::string>() == "value");
|
||||||
CHECK(options["a"].as<std::string>() == "b");
|
CHECK(result["a"].as<std::string>() == "b");
|
||||||
CHECK(options.count("6") == 1);
|
CHECK(result.count("6") == 1);
|
||||||
CHECK(options.count("p") == 2);
|
CHECK(result.count("p") == 2);
|
||||||
CHECK(options.count("space") == 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]")
|
TEST_CASE("Short options", "[options]")
|
||||||
@ -96,36 +106,15 @@ TEST_CASE("Short options", "[options]")
|
|||||||
auto actual_argv = argv.argv();
|
auto actual_argv = argv.argv();
|
||||||
auto argc = argv.argc();
|
auto argc = argv.argc();
|
||||||
|
|
||||||
options.parse(argc, actual_argv);
|
auto result = options.parse(argc, actual_argv);
|
||||||
|
|
||||||
CHECK(options.count("a") == 1);
|
CHECK(result.count("a") == 1);
|
||||||
CHECK(options["a"].as<std::string>() == "value");
|
CHECK(result["a"].as<std::string>() == "value");
|
||||||
|
|
||||||
REQUIRE_THROWS_AS(options.add_options()("", "nothing option"),
|
REQUIRE_THROWS_AS(options.add_options()("", "nothing option"),
|
||||||
cxxopts::invalid_option_format_error);
|
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]")
|
TEST_CASE("No positional", "[positional]")
|
||||||
{
|
{
|
||||||
cxxopts::Options options("test_no_positional",
|
cxxopts::Options options("test_no_positional",
|
||||||
@ -135,7 +124,7 @@ TEST_CASE("No positional", "[positional]")
|
|||||||
|
|
||||||
char** argv = av.argv();
|
char** argv = av.argv();
|
||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
options.parse(argc, argv);
|
auto result = options.parse(argc, argv);
|
||||||
|
|
||||||
REQUIRE(argc == 4);
|
REQUIRE(argc == 4);
|
||||||
CHECK(strcmp(argv[1], "a") == 0);
|
CHECK(strcmp(argv[1], "a") == 0);
|
||||||
@ -158,7 +147,7 @@ TEST_CASE("All positional", "[positional]")
|
|||||||
|
|
||||||
options.parse_positional("positional");
|
options.parse_positional("positional");
|
||||||
|
|
||||||
options.parse(argc, argv);
|
auto result = options.parse(argc, argv);
|
||||||
|
|
||||||
REQUIRE(argc == 1);
|
REQUIRE(argc == 1);
|
||||||
REQUIRE(positional.size() == 3);
|
REQUIRE(positional.size() == 3);
|
||||||
@ -186,14 +175,14 @@ TEST_CASE("Some positional explicit", "[positional]")
|
|||||||
char** argv = av.argv();
|
char** argv = av.argv();
|
||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
|
|
||||||
options.parse(argc, argv);
|
auto result = options.parse(argc, argv);
|
||||||
|
|
||||||
CHECK(argc == 1);
|
CHECK(argc == 1);
|
||||||
CHECK(options.count("output"));
|
CHECK(result.count("output"));
|
||||||
CHECK(options["input"].as<std::string>() == "b");
|
CHECK(result["input"].as<std::string>() == "b");
|
||||||
CHECK(options["output"].as<std::string>() == "a");
|
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);
|
REQUIRE(positional.size() == 2);
|
||||||
CHECK(positional[0] == "c");
|
CHECK(positional[0] == "c");
|
||||||
@ -234,10 +223,58 @@ TEST_CASE("Empty with implicit value", "[implicit]")
|
|||||||
char** argv = av.argv();
|
char** argv = av.argv();
|
||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
|
|
||||||
options.parse(argc, argv);
|
auto result = options.parse(argc, argv);
|
||||||
|
|
||||||
REQUIRE(options.count("implicit") == 1);
|
REQUIRE(result.count("implicit") == 1);
|
||||||
REQUIRE(options["implicit"].as<std::string>() == "");
|
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]")
|
TEST_CASE("Integers", "[options]")
|
||||||
@ -252,11 +289,12 @@ TEST_CASE("Integers", "[options]")
|
|||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
|
|
||||||
options.parse_positional("positional");
|
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[0] == 5);
|
||||||
CHECK(positional[1] == 6);
|
CHECK(positional[1] == 6);
|
||||||
CHECK(positional[2] == -6);
|
CHECK(positional[2] == -6);
|
||||||
@ -295,11 +333,11 @@ TEST_CASE("Integer bounds", "[integer]")
|
|||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
|
|
||||||
options.parse_positional("positional");
|
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[0] == 127);
|
||||||
CHECK(positional[1] == -128);
|
CHECK(positional[1] == -128);
|
||||||
CHECK(positional[2] == 0x7f);
|
CHECK(positional[2] == 0x7f);
|
||||||
@ -351,14 +389,14 @@ TEST_CASE("Floats", "[options]")
|
|||||||
auto argc = av.argc();
|
auto argc = av.argc();
|
||||||
|
|
||||||
options.parse_positional("positional");
|
options.parse_positional("positional");
|
||||||
options.parse(argc, argv);
|
auto result = options.parse(argc, argv);
|
||||||
|
|
||||||
REQUIRE(options.count("double") == 1);
|
REQUIRE(result.count("double") == 1);
|
||||||
REQUIRE(options.count("positional") == 4);
|
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[0] == 4);
|
||||||
CHECK(positional[1] == -4);
|
CHECK(positional[1] == -4);
|
||||||
CHECK(positional[2] == 1.5e6);
|
CHECK(positional[2] == 1.5e6);
|
||||||
@ -378,3 +416,27 @@ TEST_CASE("Invalid integers", "[integer]") {
|
|||||||
options.parse_positional("positional");
|
options.parse_positional("positional");
|
||||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type);
|
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