626
|
1 /* |
|
2 * to_int.hpp -- safely convert string to integers |
|
3 * |
|
4 * Copyright (c) 2016-2017 David Demelier <markand@malikania.fr> |
|
5 * |
|
6 * Permission to use, copy, modify, and/or distribute this software for any |
|
7 * purpose with or without fee is hereby granted, provided that the above |
|
8 * copyright notice and this permission notice appear in all copies. |
|
9 * |
|
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
17 */ |
|
18 |
|
19 #ifndef TO_INT_HPP |
|
20 #define TO_INT_HPP |
|
21 |
|
22 /** |
|
23 * \file to_int.hpp |
|
24 * \brief Safely convert string to integers. |
|
25 */ |
|
26 |
|
27 #include <cassert> |
|
28 #include <limits> |
|
29 #include <sstream> |
|
30 #include <stdexcept> |
|
31 #include <string> |
|
32 #include <type_traits> |
|
33 |
|
34 /** |
|
35 * \cond HIDDEN_SYMBOLS |
|
36 */ |
|
37 |
|
38 namespace detail { |
|
39 |
|
40 std::invalid_argument make_invalid_argument(const std::string& str) |
|
41 { |
|
42 std::ostringstream oss; |
|
43 |
|
44 oss << "invalid number '" << str << "'"; |
|
45 |
|
46 return std::invalid_argument(oss.str()); |
|
47 } |
|
48 |
|
49 template <typename T> |
|
50 std::out_of_range make_out_of_range(const std::string& str, T min, T max) |
|
51 { |
|
52 std::ostringstream oss; |
|
53 |
|
54 oss << "number '" << str << "' is out of range "; |
|
55 oss << min << ".." << max; |
|
56 |
|
57 return std::out_of_range(oss.str()); |
|
58 } |
|
59 |
|
60 } // !detail |
|
61 |
|
62 /** |
|
63 * \endcond |
|
64 */ |
|
65 |
|
66 /** |
|
67 * Convert the given string into a signed integer. |
|
68 * |
|
69 * \param str the string to convert |
|
70 * \param min the minimum value allowed |
|
71 * \param max the maximum value allowed |
|
72 * \throw std::invalid_argument if the number was not parsed |
|
73 * \throw std::out_or_range if the argument is out of the specified range |
|
74 */ |
|
75 template <typename T = int> |
|
76 T to_int(const std::string& str, T min = std::numeric_limits<T>::min(), T max = std::numeric_limits<T>::max()) |
|
77 { |
|
78 static_assert(std::is_signed<T>::value, "must be signed"); |
|
79 |
|
80 char* end; |
|
81 auto v = std::strtoll(str.c_str(), &end, 10); |
|
82 |
|
83 if (*end != '\0') |
|
84 throw detail::make_invalid_argument(str); |
|
85 if (v < min || v > max) |
|
86 throw detail::make_out_of_range(str, min, max); |
|
87 |
|
88 return static_cast<T>(v); |
|
89 } |
|
90 |
|
91 /** |
|
92 * Convert the given string into an unsigned integer. |
|
93 * |
|
94 * In contrast to the [std::strtoull][strtoull] function, this functions |
|
95 * verifies if the string starts with minus sign and throws an exception if any. |
|
96 * |
|
97 * Note, for this you need to have a trimmed string which contains no leading |
|
98 * whitespaces. |
|
99 * |
|
100 * \pre string must be trimmed |
|
101 * \param str the string to convert |
|
102 * \param min the minimum value allowed |
|
103 * \param max the maximum value allowed |
|
104 * \throw std::invalid_argument if the number was not parsed |
|
105 * \throw std::out_or_range if the argument is out of the specified range |
|
106 * |
|
107 * [strtoull]: http://en.cppreference.com/w/cpp/string/byte/strtoul |
|
108 */ |
|
109 template <typename T = unsigned> |
|
110 T to_uint(const std::string& str, T min = std::numeric_limits<T>::min(), T max = std::numeric_limits<T>::max()) |
|
111 { |
|
112 static_assert(std::is_unsigned<T>::value, "must be unsigned"); |
|
113 |
|
114 assert(str.empty() || !std::isspace(str[0])); |
|
115 |
|
116 if (str.size() > 0U && str[0] == '-') |
|
117 throw detail::make_out_of_range(str, min, max); |
|
118 |
|
119 char* end; |
|
120 auto v = std::strtoull(str.c_str(), &end, 10); |
|
121 |
|
122 if (*end != '\0') |
|
123 throw detail::make_invalid_argument(str); |
|
124 if (v < min || v > max) |
|
125 throw detail::make_out_of_range(str, min, max); |
|
126 |
|
127 return v; |
|
128 } |
|
129 |
|
130 #endif // !TO_INT_HPP |