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