@@ -12,6 +12,7 @@ Author: Michael Tautschnig, michael.tautschnig@cs.ox.ac.uk
1212
1313#include " narrow.h"
1414
15+ #include < charconv>
1516#include < optional>
1617#include < string>
1718#include < type_traits>
@@ -53,66 +54,38 @@ string2optional_unsigned(const std::string &, int base = 10);
5354std::optional<std::size_t >
5455string2optional_size_t (const std::string &, int base = 10 );
5556
56- // / convert string to signed long long if T is signed
57+ // / Convert a string to an integer, given the base of the representation,
58+ // / works with signed and unsigned integer types,
59+ // / rejects negative inputs when the result type is unsigned,
60+ // / rejects the empty string,
61+ // / rejects leading spaces,
62+ // / rejects any trailing non-numerical suffix.
63+ // / A prefix such as 0, 0x, 0X to change base is _not_ supported.
5764template <typename T>
58- auto string2optional_base (const std::string &str, int base) ->
59- typename std::enable_if<std::is_signed<T>::value, long long>::type
65+ std::optional<T> string2optional (const std::string &str, int base = 10 )
6066{
61- static_assert (
62- sizeof (T) <= sizeof (long long ),
63- " this works under the assumption that long long is the largest type we try "
64- " to convert" );
65- return std::stoll (str, nullptr , base);
66- }
67+ PRECONDITION (base == 2 || base == 8 || base == 10 || base == 16 );
6768
68- // / convert string to unsigned long long if T is unsigned
69- template <typename T>
70- auto string2optional_base (const std::string &str, int base) ->
71- typename std::enable_if<std::is_unsigned<T>::value, unsigned long long>::type
72- {
7369 static_assert (
74- sizeof (T) <= sizeof (unsigned long long ),
75- " this works under the assumption that long long is the largest type we try "
76- " to convert" );
77- if (str.find (' -' ) != std::string::npos)
78- {
79- throw std::out_of_range{
80- " unsigned conversion behaves a bit strangely with negative values, "
81- " therefore we disable it" };
82- }
83- return std::stoull (str, nullptr , base);
84- }
70+ std::is_integral<T>::value, " string2optional requires an integral type" );
8571
86- // / attempt a given conversion, return nullopt if the conversion fails
87- // / with out_of_range or invalid_argument
88- template <typename do_conversiont>
89- auto wrap_string_conversion (do_conversiont do_conversion)
90- -> std::optional<decltype(do_conversion())>
91- {
92- try
93- {
94- return do_conversion ();
95- }
96- catch (const std::invalid_argument &)
97- {
72+ if (str.empty ())
73+ return std::nullopt ;
74+
75+ // reject negative inputs for unsigned types
76+ if (std::is_unsigned<T>::value && str.front () == ' -' )
9877 return std::nullopt ;
99- }
100- catch (const std::out_of_range &)
101- {
78+
79+ const char *first = str.data ();
80+ const char *last = str.data () + str.size ();
81+
82+ T value{};
83+ auto [ptr, ec] = std::from_chars (first, last, value, base);
84+
85+ if (ec != std::errc{} || ptr != last)
10286 return std::nullopt ;
103- }
104- }
10587
106- // / convert a string to an integer, given the base of the representation
107- // / works with signed and unsigned integer types smaller than
108- // / (unsigned) long long
109- // / does not accept negative inputs when the result type is unsigned
110- template <typename T>
111- std::optional<T> string2optional (const std::string &str, int base = 10 )
112- {
113- return wrap_string_conversion ([&]() {
114- return narrow_or_throw_out_of_range<T>(string2optional_base<T>(str, base));
115- });
88+ return value;
11689}
11790
11891#endif // CPROVER_UTIL_STRING2INT_H
0 commit comments