Mercurial > irccd
changeset 114:8cbbce7b4327
Irccd: add util::toNumber function, #489
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 28 Apr 2016 19:39:51 +0200 |
parents | 6a99814c2317 |
children | 9a8f321371f7 |
files | lib/irccd/util.hpp tests/util/main.cpp |
diffstat | 2 files changed, 84 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/lib/irccd/util.hpp Thu Apr 28 13:30:30 2016 +0200 +++ b/lib/irccd/util.hpp Thu Apr 28 19:39:51 2016 +0200 @@ -26,9 +26,12 @@ #include <ctime> #include <initializer_list> +#include <limits> #include <regex> #include <sstream> +#include <stdexcept> #include <string> +#include <type_traits> #include <unordered_map> #include <vector> @@ -233,6 +236,43 @@ } /** + * Try to convert the string into number. + * + * This function will try to convert the string to number in the limits of T. + * + * If the string is not a number or if the converted value is out of range than specified boundaries, an exception is + * thrown. + * + * By default, the function will use numeric limits from T. + * + * \param number the string to convert + * \param min the minimum (defaults to T minimum) + * \param max the maximum (defaults to T maximum) + * \return the converted value + * \throw std::invalid_argument if number is not a string + * \throw std::out_of_range if the number is not between min and max + */ +template <typename T> +inline T toNumber(const std::string &number, T min = std::numeric_limits<T>::min(), T max = std::numeric_limits<T>::max()) +{ + static_assert(std::is_integral<T>::value, "T must be integer type"); + + std::conditional_t<std::is_unsigned<T>::value, unsigned long long, long long> value; + + if (std::is_unsigned<T>::value) { + value = std::stoull(number); + } else { + value = std::stoll(number); + } + + if (value < min || value > max) { + throw std::out_of_range("out of range"); + } + + return static_cast<T>(value); +} + +/** * Parse a network message from an input buffer and remove it from it. * * \param input the buffer, will be updated
--- a/tests/util/main.cpp Thu Apr 28 13:30:30 2016 +0200 +++ b/tests/util/main.cpp Thu Apr 28 19:39:51 2016 +0200 @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <cstdint> + #include <gtest/gtest.h> #include <irccd/util.hpp> @@ -317,6 +319,48 @@ ASSERT_FALSE(util::isNumber("this is not a number")); } +/* + * util::toNumber function + * ------------------------------------------------------------------ + */ + +TEST(ToNumber, correct) +{ + /* unsigned */ + ASSERT_EQ(50u, util::toNumber<std::uint8_t>("50")); + ASSERT_EQ(5000u, util::toNumber<std::uint16_t>("5000")); + ASSERT_EQ(50000u, util::toNumber<std::uint32_t>("50000")); + ASSERT_EQ(500000u, util::toNumber<std::uint64_t>("500000")); + + /* signed */ + ASSERT_EQ(-50, util::toNumber<std::int8_t>("-50")); + ASSERT_EQ(-500, util::toNumber<std::int16_t>("-500")); + ASSERT_EQ(-5000, util::toNumber<std::int32_t>("-5000")); + ASSERT_EQ(-50000, util::toNumber<std::int64_t>("-50000")); +} + +TEST(ToNumber, incorrect) +{ + /* unsigned */ + ASSERT_THROW(util::toNumber<std::uint8_t>("300"), std::out_of_range); + ASSERT_THROW(util::toNumber<std::uint16_t>("80000"), std::out_of_range); + ASSERT_THROW(util::toNumber<std::uint8_t>("-125"), std::out_of_range); + ASSERT_THROW(util::toNumber<std::uint16_t>("-25000"), std::out_of_range); + + /* signed */ + ASSERT_THROW(util::toNumber<std::int8_t>("300"), std::out_of_range); + ASSERT_THROW(util::toNumber<std::int16_t>("80000"), std::out_of_range); + ASSERT_THROW(util::toNumber<std::int8_t>("-300"), std::out_of_range); + ASSERT_THROW(util::toNumber<std::int16_t>("-80000"), std::out_of_range); + + /* not numbers */ + ASSERT_THROW(util::toNumber<std::uint8_t>("nonono"), std::invalid_argument); + + /* custom ranges */ + ASSERT_THROW(util::toNumber<std::uint8_t>("50", 0, 10), std::out_of_range); + ASSERT_THROW(util::toNumber<std::int8_t>("-50", -10, 10), std::out_of_range); +} + } // !irccd int main(int argc, char **argv)