Fix parsing of std::vector and add test, example and documentation (#182)

Improve parsing into std::vector so that a single argument can take a list.
This commit is contained in:
Christian Lang 2019-06-18 00:14:18 +02:00 committed by jarro2783
parent 1eca210edc
commit 7b14d5f60b
4 changed files with 54 additions and 3 deletions

View File

@ -116,6 +116,18 @@ There is no way to disambiguate positional arguments from the value following
a boolean, so we have chosen that they will be positional arguments, and a boolean, so we have chosen that they will be positional arguments, and
therefore, `-o false` does not work. therefore, `-o false` does not work.
## `std::vector<T>` values
Parsing of list of values in form of an `std::vector<T>` is also supported, as long as `T`
can be parsed. To separate single values in a list the definition `CXXOPTS_VECTOR_DELIMITER`
is used, which is ',' by default. Ensure that you use no whitespaces between values because
those would be interpreted as the next command line option. Example for a command line option
that can be parsed as a `std::vector<double>`:
~~~
--my_list=1,-2.1,3,4.5
~~~
## Custom help ## Custom help
The string after the program name on the first line of the help can be The string after the program name on the first line of the help can be

View File

@ -44,6 +44,10 @@ THE SOFTWARE.
#define CXXOPTS_HAS_OPTIONAL #define CXXOPTS_HAS_OPTIONAL
#endif #endif
#ifndef CXXOPTS_VECTOR_DELIMITER
#define CXXOPTS_VECTOR_DELIMITER ','
#endif
#define CXXOPTS__VERSION_MAJOR 2 #define CXXOPTS__VERSION_MAJOR 2
#define CXXOPTS__VERSION_MINOR 2 #define CXXOPTS__VERSION_MINOR 2
#define CXXOPTS__VERSION_PATCH 0 #define CXXOPTS__VERSION_PATCH 0
@ -715,9 +719,13 @@ namespace cxxopts
void void
parse_value(const std::string& text, std::vector<T>& value) parse_value(const std::string& text, std::vector<T>& value)
{ {
T v; std::stringstream in(text);
parse_value(text, v); std::string token;
value.push_back(v); while(in.eof() == false && 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

View File

@ -56,6 +56,7 @@ parse(int argc, char* argv[])
("help", "Print help") ("help", "Print help")
("int", "An integer", cxxopts::value<int>(), "N") ("int", "An integer", cxxopts::value<int>(), "N")
("float", "A floating point number", cxxopts::value<float>()) ("float", "A floating point number", cxxopts::value<float>())
("vector", "A list of doubles", cxxopts::value<std::vector<double>>())
("option_that_is_too_long_for_the_help", "A very long option") ("option_that_is_too_long_for_the_help", "A very long option")
#ifdef CXXOPTS_USE_UNICODE #ifdef CXXOPTS_USE_UNICODE
("unicode", u8"A help option with non-ascii: à. Here the size of the" ("unicode", u8"A help option with non-ascii: à. Here the size of the"
@ -130,6 +131,16 @@ parse(int argc, char* argv[])
std::cout << "float = " << result["float"].as<float>() << std::endl; std::cout << "float = " << result["float"].as<float>() << std::endl;
} }
if (result.count("vector"))
{
std::cout << "vector = ";
const auto values = result["vector"].as<std::vector<double>>();
for (const auto& v : values) {
std::cout << v << ", ";
}
std::cout << std::endl;
}
std::cout << "Arguments remain = " << argc << std::endl; std::cout << "Arguments remain = " << argc << std::endl;
return result; return result;

View File

@ -512,6 +512,26 @@ TEST_CASE("Booleans", "[boolean]") {
REQUIRE(result.count("others") == 1); REQUIRE(result.count("others") == 1);
} }
TEST_CASE("std::vector", "[vector]") {
std::vector<double> vector;
cxxopts::Options options("vector", " - tests vector");
options.add_options()
("vector", "an vector option", cxxopts::value<std::vector<double>>(vector));
Argv av({"vector", "--vector", "1,-2.1,3,4.5"});
char** argv = av.argv();
auto argc = av.argc();
options.parse(argc, argv);
REQUIRE(vector.size() == 4);
CHECK(vector[0] == 1);
CHECK(vector[1] == -2.1);
CHECK(vector[2] == 3);
CHECK(vector[3] == 4.5);
}
#ifdef CXXOPTS_HAS_OPTIONAL #ifdef CXXOPTS_HAS_OPTIONAL
TEST_CASE("std::optional", "[optional]") { TEST_CASE("std::optional", "[optional]") {
std::optional<std::string> optional; std::optional<std::string> optional;