Mercurial > irccd
changeset 530:7cd7b2cdf923
Common: import to_int / to_uint
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 16 Nov 2017 23:51:01 +0100 |
parents | b3a0f61a35fe |
children | d71c59eb04c3 |
files | irccdctl/main.cpp irccdctl/rule_add_cli.cpp irccdctl/rule_edit_cli.cpp irccdctl/rule_move_cli.cpp irccdctl/server_connect_cli.cpp libcommon/irccd/string_util.hpp libirccd/irccd/config.cpp tests/util/main.cpp |
diffstat | 8 files changed, 145 insertions(+), 82 deletions(-) [+] |
line wrap: on
line diff
--- a/irccdctl/main.cpp Thu Nov 16 23:31:28 2017 +0100 +++ b/irccdctl/main.cpp Thu Nov 16 23:51:01 2017 +0100 @@ -134,7 +134,7 @@ if ((it = sc.find("port")) == sc.end()) throw std::invalid_argument("missing port parameter"); - port = string_util::to_number<std::uint16_t>(it->value()); + port = string_util::to_uint<std::uint16_t>(it->value()); if ((it = sc.find("ssl")) != sc.end() && string_util::is_boolean(it->value())) #if defined(HAVE_SSL) @@ -310,8 +310,7 @@ if ((it = options.find("-p")) == options.end() && (it = options.find("--port")) == options.end()) throw std::invalid_argument("missing port argument (-p or --port)"); - auto port = string_util::to_number<std::uint16_t>(it->second); - + auto port = string_util::to_uint<std::uint16_t>(it->second); return std::make_unique<ip_connection>(service, host, port); }
--- a/irccdctl/rule_add_cli.cpp Thu Nov 16 23:31:28 2017 +0100 +++ b/irccdctl/rule_add_cli.cpp Thu Nov 16 23:51:01 2017 +0100 @@ -73,9 +73,9 @@ // Index. if (result.count("-i") > 0) - json["index"] = string_util::to_number<unsigned>(result.find("-i")->second); + json["index"] = string_util::to_uint<unsigned>(result.find("-i")->second); if (result.count("--index") > 0) - json["index"] = string_util::to_number<unsigned>(result.find("--index")->second); + json["index"] = string_util::to_uint<unsigned>(result.find("--index")->second); // And action. if (copy[0] != "accept" && copy[0] != "drop")
--- a/irccdctl/rule_edit_cli.cpp Thu Nov 16 23:31:28 2017 +0100 +++ b/irccdctl/rule_edit_cli.cpp Thu Nov 16 23:51:01 2017 +0100 @@ -94,7 +94,7 @@ } // Index. - json["index"] = string_util::to_number<unsigned>(copy[0]); + json["index"] = string_util::to_uint<unsigned>(copy[0]); request(ctl, json); }
--- a/irccdctl/rule_move_cli.cpp Thu Nov 16 23:31:28 2017 +0100 +++ b/irccdctl/rule_move_cli.cpp Thu Nov 16 23:51:01 2017 +0100 @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <irccd/string_util.hpp> + #include "rule_move_cli.hpp" namespace irccd { @@ -32,15 +34,8 @@ if (args.size() < 2) throw std::invalid_argument("rule-move requires 2 arguments"); - int from = 0; - int to = 0; - - try { - from = std::stoi(args[0]); - to = std::stoi(args[1]); - } catch (...) { - throw std::invalid_argument("invalid number"); - } + int from = string_util::to_int<int>(args[0]); + int to = string_util::to_int<int>(args[1]); request(ctl, { { "command", "rule-move" },
--- a/irccdctl/server_connect_cli.cpp Thu Nov 16 23:31:28 2017 +0100 +++ b/irccdctl/server_connect_cli.cpp Thu Nov 16 23:51:01 2017 +0100 @@ -69,12 +69,8 @@ { "host", copy[1] } }); - if (copy.size() == 3) { - if (!string_util::is_int(copy[2])) - throw std::invalid_argument("invalid port number"); - - object["port"] = std::stoi(copy[2]); - } + if (copy.size() == 3) + object["port"] = string_util::to_int(copy[2]); if (result.count("-S") > 0 || result.count("--ssl-verify") > 0) object["sslVerify"] = true;
--- a/libcommon/irccd/string_util.hpp Thu Nov 16 23:31:28 2017 +0100 +++ b/libcommon/irccd/string_util.hpp Thu Nov 16 23:51:01 2017 +0100 @@ -26,12 +26,15 @@ #include "sysconfig.hpp" +#include <cassert> #include <ctime> #include <initializer_list> #include <limits> #include <regex> #include <sstream> +#include <stdexcept> #include <string> +#include <type_traits> #include <unordered_map> #include <boost/format.hpp> @@ -366,40 +369,99 @@ } /** - * 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 + * \cond HIDDEN_SYMBOLS */ + +namespace detail { + +inline 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> -inline T to_number(const std::string& number, - T min = std::numeric_limits<T>::min(), - T max = std::numeric_limits<T>::max()) +inline std::out_of_range make_out_of_range(const std::string& str, T min, T max) { - static_assert(std::is_integral<T>::value, "T must be integer type"); + std::ostringstream oss; + + oss << "number '" << str << "' is out of range "; + oss << min << ".." << max; + + return std::out_of_range(oss.str()); +} + +} // !detail + +/** + * \endcond + */ - std::conditional_t<std::is_unsigned<T>::value, unsigned long long, long long> value; +/** + * 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); - if (std::is_unsigned<T>::value) - value = std::stoull(number); - else - value = std::stoll(number); + return static_cast<T>(v); +} - if (value < min || value > max) - throw std::out_of_range("out of range"); +/** + * 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"); - return static_cast<T>(value); + 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; } } // !string_util
--- a/libirccd/irccd/config.cpp Thu Nov 16 23:31:28 2017 +0100 +++ b/libirccd/irccd/config.cpp Thu Nov 16 23:51:01 2017 +0100 @@ -133,7 +133,7 @@ throw std::invalid_argument("transport: missing 'port' parameter"); try { - port = string_util::to_number<std::uint16_t>(it->value()); + port = string_util::to_uint<std::uint16_t>(it->value()); } catch (const std::exception&) { throw std::invalid_argument(string_util::sprintf("transport: invalid port number: %s", it->value())); } @@ -343,13 +343,13 @@ // Reconnect and ping timeout try { if ((it = sc.find("port")) != sc.end()) - sv->set_port(string_util::to_number<std::uint16_t>(it->value())); + sv->set_port(string_util::to_uint<std::uint16_t>(it->value())); if ((it = sc.find("reconnect-tries")) != sc.end()) - sv->set_reconnect_tries(string_util::to_number<std::int8_t>(it->value())); + sv->set_reconnect_tries(string_util::to_int<std::int8_t>(it->value())); if ((it = sc.find("reconnect-timeout")) != sc.end()) - sv->set_reconnect_delay(string_util::to_number<std::uint16_t>(it->value())); + sv->set_reconnect_delay(string_util::to_uint<std::uint16_t>(it->value())); if ((it = sc.find("ping-timeout")) != sc.end()) - sv->set_ping_timeout(string_util::to_number<std::uint16_t>(it->value())); + sv->set_ping_timeout(string_util::to_uint<std::uint16_t>(it->value())); } catch (const std::exception&) { log::warning(string_util::sprintf("server %s: invalid number for %s: %s", sv->name(), it->key(), it->value()));
--- a/tests/util/main.cpp Thu Nov 16 23:31:28 2017 +0100 +++ b/tests/util/main.cpp Thu Nov 16 23:51:01 2017 +0100 @@ -417,47 +417,58 @@ BOOST_AUTO_TEST_SUITE_END() /* - * string_util::to_number function + * string_util::to_int function * ------------------------------------------------------------------ */ -BOOST_AUTO_TEST_SUITE(to_number) +BOOST_AUTO_TEST_SUITE(to_int) -BOOST_AUTO_TEST_CASE(correct) +BOOST_AUTO_TEST_CASE(signed_to_int) { - /* unsigned */ - BOOST_REQUIRE_EQUAL(50u, string_util::to_number<std::uint8_t>("50")); - BOOST_REQUIRE_EQUAL(5000u, string_util::to_number<std::uint16_t>("5000")); - BOOST_REQUIRE_EQUAL(50000u, string_util::to_number<std::uint32_t>("50000")); - BOOST_REQUIRE_EQUAL(500000u, string_util::to_number<std::uint64_t>("500000")); + 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); +} - /* signed */ - BOOST_REQUIRE_EQUAL(-50, string_util::to_number<std::int8_t>("-50")); - BOOST_REQUIRE_EQUAL(-500, string_util::to_number<std::int16_t>("-500")); - BOOST_REQUIRE_EQUAL(-5000, string_util::to_number<std::int32_t>("-5000")); - BOOST_REQUIRE_EQUAL(-50000, string_util::to_number<std::int64_t>("-50000")); +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(incorrect) +BOOST_AUTO_TEST_CASE(unsigned_to_uint) { - /* unsigned */ - BOOST_REQUIRE_THROW(string_util::to_number<std::uint8_t>("300"), std::out_of_range); - BOOST_REQUIRE_THROW(string_util::to_number<std::uint16_t>("80000"), std::out_of_range); - BOOST_REQUIRE_THROW(string_util::to_number<std::uint8_t>("-125"), std::out_of_range); - BOOST_REQUIRE_THROW(string_util::to_number<std::uint16_t>("-25000"), std::out_of_range); + 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() - /* signed */ - BOOST_REQUIRE_THROW(string_util::to_number<std::int8_t>("300"), std::out_of_range); - BOOST_REQUIRE_THROW(string_util::to_number<std::int16_t>("80000"), std::out_of_range); - BOOST_REQUIRE_THROW(string_util::to_number<std::int8_t>("-300"), std::out_of_range); - BOOST_REQUIRE_THROW(string_util::to_number<std::int16_t>("-80000"), std::out_of_range); +BOOST_AUTO_TEST_SUITE(errors) - /* not numbers */ - BOOST_REQUIRE_THROW(string_util::to_number<std::uint8_t>("nonono"), std::invalid_argument); +BOOST_AUTO_TEST_CASE(invalid_argument) +{ + BOOST_REQUIRE_THROW(to_int("plopation"), std::invalid_argument); + BOOST_REQUIRE_THROW(to_uint("plopation"), std::invalid_argument); +} - /* custom ranges */ - BOOST_REQUIRE_THROW(string_util::to_number<std::uint8_t>("50", 0, 10), std::out_of_range); - BOOST_REQUIRE_THROW(string_util::to_number<std::int8_t>("-50", -10, 10), std::out_of_range); +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()