Mercurial > code
changeset 631:5dd3347df00d
to_int: return boost::optional instead of throwing, closes #777 @10m
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 20 Mar 2018 13:27:22 +0100 |
parents | 04dffcdb8e1a |
children | 337a805b9ce2 |
files | cpp/to_int/test/main.cpp cpp/to_int/to_int.hpp |
diffstat | 2 files changed, 38 insertions(+), 85 deletions(-) [+] |
line wrap: on
line diff
--- a/cpp/to_int/test/main.cpp Tue Mar 20 13:18:44 2018 +0100 +++ b/cpp/to_int/test/main.cpp Tue Mar 20 13:27:22 2018 +0100 @@ -25,32 +25,32 @@ BOOST_AUTO_TEST_CASE(signed_to_int) { - BOOST_TEST(to_int("10") == 10); - BOOST_TEST(to_int<std::int8_t>("-10") == -10); - BOOST_TEST(to_int<std::int8_t>("10") == 10); - BOOST_TEST(to_int<std::int16_t>("-1000") == -1000); - BOOST_TEST(to_int<std::int16_t>("1000") == 1000); - BOOST_TEST(to_int<std::int32_t>("-1000") == -1000); - BOOST_TEST(to_int<std::int32_t>("1000") == 1000); + BOOST_TEST(*to_int("10") == 10); + BOOST_TEST(*to_int<std::int8_t>("-10") == -10); + BOOST_TEST(*to_int<std::int8_t>("10") == 10); + BOOST_TEST(*to_int<std::int16_t>("-1000") == -1000); + BOOST_TEST(*to_int<std::int16_t>("1000") == 1000); + BOOST_TEST(*to_int<std::int32_t>("-1000") == -1000); + BOOST_TEST(*to_int<std::int32_t>("1000") == 1000); } BOOST_AUTO_TEST_CASE(signed_to_int64) { - BOOST_TEST(to_int<std::int64_t>("-9223372036854775807") == -9223372036854775807LL); - BOOST_TEST(to_int<std::int64_t>("9223372036854775807") == 9223372036854775807LL); + BOOST_TEST(*to_int<std::int64_t>("-9223372036854775807") == -9223372036854775807LL); + BOOST_TEST(*to_int<std::int64_t>("9223372036854775807") == 9223372036854775807LL); } BOOST_AUTO_TEST_CASE(unsigned_to_uint) { - BOOST_TEST(to_uint("10") == 10U); - BOOST_TEST(to_uint<std::uint8_t>("10") == 10U); - BOOST_TEST(to_uint<std::uint16_t>("1000") == 1000U); - BOOST_TEST(to_uint<std::uint32_t>("1000") == 1000U); + BOOST_TEST(*to_uint("10") == 10U); + BOOST_TEST(*to_uint<std::uint8_t>("10") == 10U); + BOOST_TEST(*to_uint<std::uint16_t>("1000") == 1000U); + BOOST_TEST(*to_uint<std::uint32_t>("1000") == 1000U); } BOOST_AUTO_TEST_CASE(unsigned_to_uint64) { - BOOST_TEST(to_uint<std::uint64_t>("18446744073709551615") == 18446744073709551615ULL); + BOOST_TEST(*to_uint<std::uint64_t>("18446744073709551615") == 18446744073709551615ULL); } BOOST_AUTO_TEST_SUITE_END() @@ -59,16 +59,16 @@ BOOST_AUTO_TEST_CASE(invalid_argument) { - BOOST_REQUIRE_THROW(to_int("plopation"), std::invalid_argument); - BOOST_REQUIRE_THROW(to_uint("plopation"), std::invalid_argument); + BOOST_TEST(!to_int("plopation")); + BOOST_TEST(!to_uint("plopation")); } BOOST_AUTO_TEST_CASE(out_of_range) { - BOOST_REQUIRE_THROW(to_int<std::int8_t>("1000"), std::out_of_range); - BOOST_REQUIRE_THROW(to_int<std::int8_t>("-1000"), std::out_of_range); - BOOST_REQUIRE_THROW(to_uint<std::uint8_t>("1000"), std::out_of_range); - BOOST_REQUIRE_THROW(to_uint<std::uint8_t>("-1000"), std::out_of_range); + BOOST_TEST(!to_int<std::int8_t>("1000")); + BOOST_TEST(!to_int<std::int8_t>("-1000")); + BOOST_TEST(!to_uint<std::uint8_t>("1000")); + BOOST_TEST(!to_uint<std::uint8_t>("-1000")); } BOOST_AUTO_TEST_SUITE_END()
--- a/cpp/to_int/to_int.hpp Tue Mar 20 13:18:44 2018 +0100 +++ b/cpp/to_int/to_int.hpp Tue Mar 20 13:27:22 2018 +0100 @@ -24,107 +24,60 @@ * \brief Safely convert string to integers. */ -#include <cassert> +#include <boost/optional.hpp> + +#include <cstdlib> #include <limits> -#include <sstream> -#include <stdexcept> #include <string> #include <type_traits> /** - * \cond HIDDEN_SYMBOLS - */ - -namespace detail { - -std::invalid_argument make_invalid_argument(const std::string& str) -{ - std::ostringstream oss; - - oss << "invalid number '" << str << "'"; - - return std::invalid_argument(oss.str()); -} - -template <typename T> -std::out_of_range make_out_of_range(const std::string& str, T min, T max) -{ - std::ostringstream oss; - - oss << "number '" << str << "' is out of range "; - oss << min << ".." << max; - - return std::out_of_range(oss.str()); -} - -} // !detail - -/** - * \endcond - */ - -/** * Convert the given string into a signed integer. * * \param str the string to convert * \param min the minimum value allowed * \param max the maximum value allowed - * \throw std::invalid_argument if the number was not parsed - * \throw std::out_or_range if the argument is out of the specified range + * \return the value or boost::none if not convertible */ template <typename T = int> -T to_int(const std::string& str, T min = std::numeric_limits<T>::min(), T max = std::numeric_limits<T>::max()) +boost::optional<T> to_int(const std::string& str, + T min = std::numeric_limits<T>::min(), + T max = std::numeric_limits<T>::max()) noexcept { static_assert(std::is_signed<T>::value, "must be signed"); char* end; auto v = std::strtoll(str.c_str(), &end, 10); - if (*end != '\0') - throw detail::make_invalid_argument(str); - if (v < min || v > max) - throw detail::make_out_of_range(str, min, max); + if (*end != '\0' || v < min || v > max) + return boost::none; return static_cast<T>(v); } /** - * Convert the given string into an unsigned integer. - * - * In contrast to the [std::strtoull][strtoull] function, this functions - * verifies if the string starts with minus sign and throws an exception if any. + * Convert the given string into a unsigned integer. * - * Note, for this you need to have a trimmed string which contains no leading - * whitespaces. - * - * \pre string must be trimmed + * \note invalid numbers are valid as well * \param str the string to convert * \param min the minimum value allowed * \param max the maximum value allowed - * \throw std::invalid_argument if the number was not parsed - * \throw std::out_or_range if the argument is out of the specified range - * - * [strtoull]: http://en.cppreference.com/w/cpp/string/byte/strtoul + * \return the value or boost::none if not convertible */ template <typename T = unsigned> -T to_uint(const std::string& str, T min = std::numeric_limits<T>::min(), T max = std::numeric_limits<T>::max()) +boost::optional<T> to_uint(const std::string& str, + T min = std::numeric_limits<T>::min(), + T max = std::numeric_limits<T>::max()) noexcept { static_assert(std::is_unsigned<T>::value, "must be unsigned"); - assert(str.empty() || !std::isspace(str[0])); - - if (str.size() > 0U && str[0] == '-') - throw detail::make_out_of_range(str, min, max); - char* end; auto v = std::strtoull(str.c_str(), &end, 10); - if (*end != '\0') - throw detail::make_invalid_argument(str); - if (v < min || v > max) - throw detail::make_out_of_range(str, min, max); + if (*end != '\0' || v < min || v > max) + return boost::none; - return v; + return static_cast<T>(v); } #endif // !TO_INT_HPP