Mercurial > code
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