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)