mirror of https://github.com/jarro2783/cxxopts.git
fix: parse special floating point values
This commit is contained in:
parent
a3a21b31ef
commit
35286c2af3
|
|
@ -27,6 +27,7 @@ THE SOFTWARE.
|
|||
#ifndef CXXOPTS_HPP_INCLUDED
|
||||
#define CXXOPTS_HPP_INCLUDED
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
|
@ -999,6 +1000,27 @@ check_signed_range(bool negative, U value, const std::string& text)
|
|||
SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
floating_point_parser(const std::string& text, T& value, T (*parser)(const char*, char**))
|
||||
{
|
||||
if (text.empty())
|
||||
{
|
||||
throw_or_mimic<exceptions::incorrect_argument_type>(text);
|
||||
}
|
||||
|
||||
char* end = nullptr;
|
||||
errno = 0;
|
||||
const auto result = parser(text.c_str(), &end);
|
||||
|
||||
if (end == text.c_str() || errno == ERANGE)
|
||||
{
|
||||
throw_or_mimic<exceptions::incorrect_argument_type>(text);
|
||||
}
|
||||
|
||||
value = result;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename R, typename T>
|
||||
|
|
@ -1132,6 +1154,27 @@ parse_value(const std::string& text, std::string& value)
|
|||
value = text;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
parse_value(const std::string& text, float& value)
|
||||
{
|
||||
detail::floating_point_parser(text, value, std::strtof);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
parse_value(const std::string& text, double& value)
|
||||
{
|
||||
detail::floating_point_parser(text, value, std::strtod);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
parse_value(const std::string& text, long double& value)
|
||||
{
|
||||
detail::floating_point_parser(text, value, std::strtold);
|
||||
}
|
||||
|
||||
// The fallback parser. It uses the stringstream parser to parse all types
|
||||
// that have not been overloaded explicitly. It has to be placed in the
|
||||
// source code before all other more specialized templates.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "catch.hpp"
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
#include <initializer_list>
|
||||
|
|
@ -977,6 +978,52 @@ TEST_CASE("Floats", "[options]")
|
|||
CHECK(positional[3] == -1.5e6);
|
||||
}
|
||||
|
||||
TEST_CASE("Special floating point values", "[options]")
|
||||
{
|
||||
cxxopts::Options options("parses_special_floats", "parses special floating point values");
|
||||
options.add_options()
|
||||
("double", "Double precision", cxxopts::value<double>())
|
||||
("long-double", "Extended precision", cxxopts::value<long double>())
|
||||
("positional", "Floats", cxxopts::value<std::vector<float>>());
|
||||
|
||||
Argv av({"special_floats", "--double", "inf", "--long-double", "infinity", "--", "-inf", "nan"});
|
||||
|
||||
auto** argv = av.argv();
|
||||
auto argc = av.argc();
|
||||
|
||||
options.parse_positional("positional");
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
auto parsed_double = result["double"].as<double>();
|
||||
CHECK(std::isinf(parsed_double));
|
||||
CHECK(!std::signbit(parsed_double));
|
||||
|
||||
auto parsed_long_double = result["long-double"].as<long double>();
|
||||
CHECK(std::isinf(parsed_long_double));
|
||||
CHECK(!std::signbit(parsed_long_double));
|
||||
|
||||
auto& positional = result["positional"].as<std::vector<float>>();
|
||||
REQUIRE(positional.size() == 2);
|
||||
CHECK(std::isinf(positional[0]));
|
||||
CHECK(std::signbit(positional[0]));
|
||||
CHECK(std::isnan(positional[1]));
|
||||
}
|
||||
|
||||
TEST_CASE("Invalid floats", "[options]")
|
||||
{
|
||||
cxxopts::Options options("invalid_floats", "rejects invalid floats");
|
||||
options.add_options()
|
||||
("positional", "Floats", cxxopts::value<std::vector<float>>());
|
||||
|
||||
Argv av({"floats", "--", "abc"});
|
||||
|
||||
auto** argv = av.argv();
|
||||
auto argc = av.argc();
|
||||
|
||||
options.parse_positional("positional");
|
||||
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type);
|
||||
}
|
||||
|
||||
TEST_CASE("Invalid integers", "[integer]") {
|
||||
cxxopts::Options options("invalid_integers", "rejects invalid integers");
|
||||
options.add_options()
|
||||
|
|
|
|||
Loading…
Reference in New Issue