mirror of https://github.com/jarro2783/cxxopts.git
feat: Allow parsing Inf,NaN in float, double and long double
This commit is contained in:
parent
25f319275e
commit
c834684f53
|
|
@ -580,6 +580,16 @@ class incorrect_argument_type : public parsing
|
|||
)
|
||||
{
|
||||
}
|
||||
explicit incorrect_argument_type
|
||||
(
|
||||
const std::string& arg,
|
||||
const std::string& message
|
||||
)
|
||||
: parsing(
|
||||
"Argument " + LQUOTE + arg + RQUOTE + " failed to parse becuause " + message
|
||||
)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace exceptions
|
||||
|
|
@ -604,6 +614,25 @@ void throw_or_mimic(const std::string& text)
|
|||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void throw_or_mimic(const std::string& text, const std::string& msg)
|
||||
{
|
||||
static_assert(std::is_base_of<std::exception, T>::value,
|
||||
"throw_or_mimic only works on std::exception and "
|
||||
"deriving classes");
|
||||
|
||||
#ifndef CXXOPTS_NO_EXCEPTIONS
|
||||
// If CXXOPTS_NO_EXCEPTIONS is not defined, just throw
|
||||
throw T{text, msg};
|
||||
#else
|
||||
// Otherwise manually instantiate the exception, print what() to stderr,
|
||||
// and exit
|
||||
T exception{text, msg};
|
||||
std::cerr << exception.what() << std::endl;
|
||||
std::exit(EXIT_FAILURE);
|
||||
#endif
|
||||
}
|
||||
|
||||
using OptionNames = std::vector<std::string>;
|
||||
|
||||
namespace values {
|
||||
|
|
@ -1120,6 +1149,60 @@ parse_value(const std::string& text, bool& value)
|
|||
throw_or_mimic<exceptions::incorrect_argument_type>(text);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
fparse(const std::string& text, float& value){
|
||||
value = std::stof(text);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
fparse(const std::string& text, double& value){
|
||||
value = std::stod(text);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
fparse(const std::string& text, long double& value){
|
||||
value = std::stold(text);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline
|
||||
void
|
||||
fparse_with_exception(const std::string& text, T& value)
|
||||
{
|
||||
try{
|
||||
fparse(text, value);
|
||||
} catch(const std::out_of_range&) {
|
||||
throw_or_mimic<exceptions::incorrect_argument_type>(text, "out of range");
|
||||
} catch(const std::invalid_argument&) {
|
||||
throw_or_mimic<exceptions::incorrect_argument_type>(text, "invalid arguement");
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
parse_value(const std::string& text, float& value)
|
||||
{
|
||||
fparse_with_exception<float>(text, value);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
parse_value(const std::string& text, double& value)
|
||||
{
|
||||
fparse_with_exception<double>(text, value);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
parse_value(const std::string& text, long double& value)
|
||||
{
|
||||
fparse_with_exception<long double>(text, value);
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
void
|
||||
parse_value(const std::string& text, std::string& value)
|
||||
|
|
|
|||
101
test/options.cpp
101
test/options.cpp
|
|
@ -1,5 +1,6 @@
|
|||
#include "catch.hpp"
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
|
|
@ -934,6 +935,106 @@ TEST_CASE("Floats", "[options]")
|
|||
CHECK(positional[3] == -1.5e6);
|
||||
}
|
||||
|
||||
TEST_CASE("Floating special values and parse failures", "[options]")
|
||||
{
|
||||
cxxopts::Options options("floating_special_values", "floating special values and parse failures");
|
||||
options.add_options()
|
||||
("f,float", "Float", cxxopts::value<float>())
|
||||
("d,double", "Double", cxxopts::value<double>())
|
||||
("l,long-double", "Long double", cxxopts::value<long double>());
|
||||
|
||||
enum class expected_result {
|
||||
pos_inf,
|
||||
neg_inf,
|
||||
nan,
|
||||
parse_error
|
||||
};
|
||||
|
||||
struct testcase {
|
||||
std::string name;
|
||||
Argv argv;
|
||||
expected_result expected;
|
||||
} tests[] = {
|
||||
{
|
||||
"parses infinity",
|
||||
Argv{"floating_special_values", "--float", "infinity", "--double", "infinity", "--long-double", "infinity"},
|
||||
expected_result::pos_inf,
|
||||
},
|
||||
{
|
||||
"parses negative inf",
|
||||
Argv{"floating_special_values", "--float", "-INF", "--double", "-inf", "--long-double", "-Inf"},
|
||||
expected_result::neg_inf,
|
||||
},
|
||||
{
|
||||
"parses nan",
|
||||
Argv{"floating_special_values", "--float", "nan", "--double", "nan", "--long-double", "nan"},
|
||||
expected_result::nan,
|
||||
},
|
||||
{
|
||||
"rejects invalid token for float",
|
||||
Argv{"floating_special_values", "--float", "adsfas"},
|
||||
expected_result::parse_error,
|
||||
},
|
||||
{
|
||||
"rejects invalid token for double",
|
||||
Argv{"floating_special_values", "--double", "adsfas"},
|
||||
expected_result::parse_error,
|
||||
},
|
||||
{
|
||||
"rejects invalid token for long double",
|
||||
Argv{"floating_special_values", "--long-double", "adsfas"},
|
||||
expected_result::parse_error,
|
||||
},
|
||||
{
|
||||
"rejects out of range token for float",
|
||||
Argv{"floating_special_values", "--float", "1000000000000000000000000000000000000000"},
|
||||
expected_result::parse_error,
|
||||
},
|
||||
};
|
||||
|
||||
for (const auto& tc : tests) {
|
||||
SECTION(tc.name) {
|
||||
if (tc.expected == expected_result::parse_error) {
|
||||
CHECK_THROWS_AS(options.parse(tc.argv.argc(), tc.argv.argv()), cxxopts::exceptions::incorrect_argument_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto result = options.parse(tc.argv.argc(), tc.argv.argv());
|
||||
|
||||
auto check_value = [&](long double value) {
|
||||
switch (tc.expected) {
|
||||
case expected_result::pos_inf:
|
||||
CHECK(std::isinf(value));
|
||||
CHECK(value > 0.0L);
|
||||
break;
|
||||
case expected_result::neg_inf:
|
||||
CHECK(std::isinf(value));
|
||||
CHECK(value < 0.0L);
|
||||
break;
|
||||
case expected_result::nan:
|
||||
CHECK(std::isnan(value));
|
||||
break;
|
||||
case expected_result::parse_error:
|
||||
// Handled above
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
check_value(result["float"].as<float>());
|
||||
check_value(result["double"].as<double>());
|
||||
check_value(result["long-double"].as<long double>());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("parses finite values") {
|
||||
Argv argv{"floating_special_values", "--float", "1.25", "--double", "-2.5", "--long-double", "3.75"};
|
||||
auto result = options.parse(argv.argc(), argv.argv());
|
||||
CHECK(result["float"].as<float>() == 1.25f);
|
||||
CHECK(result["double"].as<double>() == -2.5);
|
||||
CHECK(result["long-double"].as<long double>() == 3.75L);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Invalid integers", "[integer]") {
|
||||
cxxopts::Options options("invalid_integers", "rejects invalid integers");
|
||||
options.add_options()
|
||||
|
|
|
|||
Loading…
Reference in New Issue