changeset 626:64884a4de16a

to_int: initial import
author David Demelier <markand@malikania.fr>
date Thu, 26 Oct 2017 19:45:00 +0200
parents dccaf66c8b9f
children b1bfc23d33fe
files cpp/CMakeLists.txt cpp/to_int/CMakeLists.txt cpp/to_int/test/main.cpp cpp/to_int/to_int.hpp
diffstat 4 files changed, 227 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/cpp/CMakeLists.txt	Mon Oct 23 15:21:54 2017 +0200
+++ b/cpp/CMakeLists.txt	Thu Oct 26 19:45:00 2017 +0200
@@ -20,3 +20,4 @@
 add_subdirectory(is_number)
 add_subdirectory(join)
 add_subdirectory(options)
+add_subdirectory(to_int)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cpp/to_int/CMakeLists.txt	Thu Oct 26 19:45:00 2017 +0200
@@ -0,0 +1,22 @@
+#
+# CMakeLists.txt -- code building for common code
+#
+# Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+code_define_module(
+    NAME to-int
+    SOURCES to_int.hpp
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cpp/to_int/test/main.cpp	Thu Oct 26 19:45:00 2017 +0200
@@ -0,0 +1,74 @@
+/*
+ * main.cpp -- test to_int functions
+ *
+ * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define BOOST_TEST_MODULE "to-int"
+#include <boost/test/unit_test.hpp>
+
+#include "to_int.hpp"
+
+BOOST_AUTO_TEST_SUITE(valid)
+
+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_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_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_AUTO_TEST_CASE(unsigned_to_uint64)
+{
+    BOOST_TEST(to_uint<std::uint64_t>("18446744073709551615") == 18446744073709551615ULL);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE(errors)
+
+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_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_AUTO_TEST_SUITE_END()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cpp/to_int/to_int.hpp	Thu Oct 26 19:45:00 2017 +0200
@@ -0,0 +1,130 @@
+/*
+ * to_int.hpp -- safely convert string to integers
+ *
+ * Copyright (c) 2016-2017 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef TO_INT_HPP
+#define TO_INT_HPP
+
+/**
+ * \file to_int.hpp
+ * \brief Safely convert string to integers.
+ */
+
+#include <cassert>
+#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
+ */
+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())
+{
+    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);
+
+    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.
+ *
+ * Note, for this you need to have a trimmed string which contains no leading
+ * whitespaces.
+ *
+ * \pre string must be trimmed
+ * \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
+ */
+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())
+{
+    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);
+
+    return v;
+}
+
+#endif // !TO_INT_HPP