mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 12:13:52 +00:00
add some more tests of custom parsers and adjustments to the README
Add a test that creates and uses a custom parser to store a value add a check around regex to see if it is working fix warning in AppTest from gcc
This commit is contained in:
parent
7cd04e3b65
commit
b26894458b
@ -401,7 +401,7 @@ Every `add_` option you have seen so far depends on one method that takes a lamb
|
||||
|
||||
Other values can be added as long as they support `operator>>` (and defaults can be printed if they support `operator<<`). To add an enum, for example, provide a custom `operator>>` with an `istream` (inside the CLI namespace is fine if you don't want to interfere with an existing `operator>>`).
|
||||
|
||||
If you wanted to extend this to support a completely new type, just use a lambda. An example of a new parser for `complex<double>` that supports all of the features of a standard `add_options` call is in [one of the tests](./tests/NewParseTest.cpp). A simpler example is shown below:
|
||||
If you wanted to extend this to support a completely new type, use a lambda or add a specialization of the lexical_cast function template in the namespace `CLI::detail` with the type you need to convert to. Some examples of some new parsers for `complex<double>` that support all of the features of a standard `add_options` call are in [one of the tests](./tests/NewParseTest.cpp). A simpler example is shown below:
|
||||
|
||||
#### Example
|
||||
|
||||
|
@ -339,7 +339,7 @@ TEST_F(TApp, doubleVectorFunction) {
|
||||
});
|
||||
args = {"--val", "5", "--val", "6", "--val", "7"};
|
||||
run();
|
||||
EXPECT_EQ(res.size(), 3);
|
||||
EXPECT_EQ(res.size(), (size_t)3);
|
||||
EXPECT_EQ(res[0], 10.0);
|
||||
EXPECT_EQ(res[2], 12.0);
|
||||
}
|
||||
|
@ -97,3 +97,133 @@ TEST_F(TApp, BuiltinComplexFail) {
|
||||
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
// an example of custom converter that can be used to add new parsing options
|
||||
// On MSVC and possibly some other new compilers this can be a free standing function without the template
|
||||
// specialization but this is compiler dependent
|
||||
namespace CLI {
|
||||
namespace detail {
|
||||
|
||||
template <>
|
||||
bool lexical_cast<std::pair<std::string, std::string>>(std::string input, std::pair<std::string, std::string> &output) {
|
||||
|
||||
auto sep = input.find_first_of(':');
|
||||
if((sep == std::string::npos) && (sep > 0)) {
|
||||
return false;
|
||||
}
|
||||
output = {input.substr(0, sep), input.substr(sep + 1)};
|
||||
return true;
|
||||
}
|
||||
} // namespace detail
|
||||
} // namespace CLI
|
||||
|
||||
TEST_F(TApp, custom_string_converter) {
|
||||
std::pair<std::string, std::string> val;
|
||||
app.add_option("-d,--dual_string", val);
|
||||
|
||||
args = {"-d", "string1:string2"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ(val.first, "string1");
|
||||
EXPECT_EQ(val.second, "string2");
|
||||
}
|
||||
|
||||
TEST_F(TApp, custom_string_converterFail) {
|
||||
std::pair<std::string, std::string> val;
|
||||
app.add_option("-d,--dual_string", val);
|
||||
|
||||
args = {"-d", "string2"};
|
||||
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
}
|
||||
|
||||
// an example of custom complex number converter that can be used to add new parsing options
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<regex>)
|
||||
// an example of custom converter that can be used to add new parsing options
|
||||
#define HAS_REGEX_INCLUDE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAS_REGEX_INCLUDE
|
||||
// Gcc 4.8 and older and the corresponding standard libraries have a broken <regex> so this would
|
||||
// fail. And if a clang compiler is using libstd++ then this will generate an error as well so this is just a check to
|
||||
// simplify compilation and prevent a much more complicated #if expression
|
||||
#include <regex>
|
||||
namespace CLI {
|
||||
namespace detail {
|
||||
|
||||
// On MSVC and possibly some other new compilers this can be a free standing function without the template
|
||||
// specialization but this is compiler dependent
|
||||
template <> bool lexical_cast<std::complex<double>>(std::string input, std::complex<double> &output) {
|
||||
// regular expression to handle complex numbers of various formats
|
||||
static const std::regex creg(
|
||||
R"(([+-]?(\d+(\.\d+)?|\.\d+)([eE][+-]?\d+)?)\s*([+-]\s*(\d+(\.\d+)?|\.\d+)([eE][+-]?\d+)?)[ji]*)");
|
||||
|
||||
std::smatch m;
|
||||
double x = 0.0, y = 0.0;
|
||||
bool worked;
|
||||
std::regex_search(input, m, creg);
|
||||
if(m.size() == 9) {
|
||||
worked = CLI::detail::lexical_cast(m[1], x) && CLI::detail::lexical_cast(m[6], y);
|
||||
if(worked) {
|
||||
if(*m[5].first == '-') {
|
||||
y = -y;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if((input.back() == 'j') || (input.back() == 'i')) {
|
||||
auto strval = input.substr(0, input.size() - 1);
|
||||
CLI::detail::trim(strval);
|
||||
worked = CLI::detail::lexical_cast(strval, y);
|
||||
} else {
|
||||
CLI::detail::trim(input);
|
||||
worked = CLI::detail::lexical_cast(input, x);
|
||||
}
|
||||
}
|
||||
if(worked) {
|
||||
output = cx{x, y};
|
||||
}
|
||||
return worked;
|
||||
}
|
||||
} // namespace detail
|
||||
} // namespace CLI
|
||||
|
||||
TEST_F(TApp, AddingComplexParserDetail) {
|
||||
|
||||
bool skip_tests = false;
|
||||
try { // check if the library actually supports regex, it is possible to link against a non working regex in the
|
||||
// standard library
|
||||
std::smatch m;
|
||||
std::string input = "1.5+2.5j";
|
||||
static const std::regex creg(
|
||||
R"(([+-]?(\d+(\.\d+)?|\.\d+)([eE][+-]?\d+)?)\s*([+-]\s*(\d+(\.\d+)?|\.\d+)([eE][+-]?\d+)?)[ji]*)");
|
||||
|
||||
auto rsearch = std::regex_search(input, m, creg);
|
||||
if(!rsearch) {
|
||||
skip_tests = true;
|
||||
} else {
|
||||
EXPECT_EQ(m.size(), (size_t)9);
|
||||
}
|
||||
|
||||
} catch(...) {
|
||||
skip_tests = true;
|
||||
}
|
||||
if(!skip_tests) {
|
||||
cx comp{0, 0};
|
||||
app.add_option("-c,--complex", comp, "add a complex number option");
|
||||
args = {"-c", "1.5+2.5j"};
|
||||
|
||||
run();
|
||||
|
||||
EXPECT_DOUBLE_EQ(1.5, comp.real());
|
||||
EXPECT_DOUBLE_EQ(2.5, comp.imag());
|
||||
args = {"-c", "1.5-2.5j"};
|
||||
|
||||
run();
|
||||
|
||||
EXPECT_DOUBLE_EQ(1.5, comp.real());
|
||||
EXPECT_DOUBLE_EQ(-2.5, comp.imag());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user