ROSE  0.11.98.0
StringToNumber.h
1 #ifndef ROSE_StringUtility_StringToNumber_H
2 #define ROSE_StringUtility_StringToNumber_H
3 
4 #include <Rose/StringUtility/NumberToString.h>
5 
6 #include <Rose/StringUtility/Diagnostics.h>
7 #include <Rose/Exception.h>
8 #include <rosedll.h>
9 
10 #include <limits>
11 #include <string>
12 #include <type_traits>
13 #include <vector>
14 #include <Sawyer/Optional.h>
15 #include <Sawyer/Result.h>
16 
17 namespace Rose {
18 namespace StringUtility {
19 
21 // Number parsing
23 
28 ROSE_UTIL_API unsigned hexadecimalToInt(char);
29 
59 template<class Container, class Stringifier>
60 std::vector<std::string> toStrings(const Container &numbers, const Stringifier &stringifier=numberToString) {
61  return toStrings_range(numbers.begin(), numbers.end(), stringifier);
62 }
63 template<class Iterator, class Stringifier>
64 std::vector<std::string> toStrings_range(Iterator begin, Iterator end, const Stringifier &stringifier=numberToString) {
65  std::vector<std::string> retval;
66  for (/*void*/; begin!=end; ++begin)
67  retval.push_back(stringifier(*begin));
68  return retval;
69 }
76 template<class IntegralType>
78 toDigit(char ch, IntegralType radix = 10) {
79  assert(radix <= 16);
80  assert(!std::numeric_limits<IntegralType>::is_signed || radix >= 0); // trait test is to avoid compiler warning
81  IntegralType digit;
82  if (ch >= '0' && ch <= '9') {
83  digit = ch - '0';
84  } else if (ch >= 'a' && ch <= 'f') {
85  digit = ch - 'a' + 10;
86  } else if (ch >= 'A' && ch <= 'F') {
87  digit = ch - 'A' + 10;
88  } else {
89  return Sawyer::Nothing();
90  }
91 
92  if (digit < radix)
93  return digit;
94  return Sawyer::Nothing();
95 }
96 
110 template<class IntegralType>
111 typename std::enable_if<std::is_integral<IntegralType>::value, Sawyer::Result<IntegralType, std::string>>::type
112 toNumber(const std::string &s) {
113  using UnsignedType = typename std::make_unsigned<IntegralType>::type;
114 
115  // No template parameter deduction in constructors before C++17, so make aliases
116  using Error = Sawyer::Error<std::string>;
117  using Ok = Sawyer::Ok<IntegralType>;
118 
119  const char *sptr = s.c_str();
120 
121  // Optional plus or minus sign when parsing signed values
122  if ('-' == *sptr || '+' == *sptr) {
123  if (std::numeric_limits<IntegralType>::is_signed) {
124  ++sptr;
125  } else {
126  return Error("syntax error: sign not allowed for unsigned types");
127  }
128  }
129  if ('_' == *sptr)
130  return Error("syntax error: separator not allowed before first digit");
131 
132  // Radix specification
133  UnsignedType radix = 10;
134  if (strncmp("0x", sptr, 2) == 0 || strncmp("0X", sptr, 2) == 0) {
135  radix = 16;
136  sptr += 2;
137  } else if (strncmp("0b", sptr, 2) == 0) {
138  radix = 2;
139  sptr += 2;
140  }
141 
142  // Number may have a leading digit separator if it has a radix specifier
143  if (radix != 10 && '_' == *sptr)
144  ++sptr;
145 
146  // Parse the value as unsigned, but be careful of overflows.
147  UnsignedType n = 0;
148  size_t nDigits = 0;
149  for (size_t i = 0; sptr[i]; ++i) {
150  if ('_' == sptr[i]) {
151  if (0 == i || '_' == sptr[i-1] || !sptr[i+1])
152  return Error("syntax error: invalid use of digit separator");
153 
154  } else if (Sawyer::Optional<UnsignedType> digit = toDigit(sptr[i], radix)) {
155  ++nDigits;
156 
157  // Check for overflow
158  const UnsignedType shifted = n * radix;
159  if ((n != 0 && shifted / n != radix) || *digit > std::numeric_limits<UnsignedType>::max() - shifted) {
160  if ('-' == s[0]) {
161  return Error("overflow error: less than minimum value for type");
162  } else {
163  return Error("overflow error: greater than maximum value for type");
164  }
165  }
166 
167  n = shifted + *digit;
168  } else {
169  return Error("syntax error: invalid digit after parsing " + plural(nDigits, "digits"));
170  }
171  }
172  if (0 == nDigits)
173  return Error("syntax error: digits expected");
174 
175  // Convert the unsigned (positive) value to a signed negative if necessary, checking overflow.
176  if (std::numeric_limits<IntegralType>::is_signed) {
177  const UnsignedType signBit = (UnsignedType)1 << (8*sizeof(IntegralType) - 1);
178  if ('-' == s[0]) {
179  if (n & signBit && n != signBit)
180  return Error("overflow error: less than minimum value for type");
181  return Ok((IntegralType)(~n + 1));
182  } else if (n & signBit) {
183  return Error("overflow error: greater than maximum value for type");
184  } else {
185  return Ok((IntegralType)n);
186  }
187  } else {
188  return Ok((IntegralType)n);
189  }
190 }
191 
196 template<class IntegralType>
197 typename std::enable_if<std::is_integral<IntegralType>::value, IntegralType>::type
198 toNumberOrThrow(const std::string &s) {
199  return toNumber<IntegralType>(s).template orThrow<Exception>();
200 }
201 
202 } // namespace
203 } // namespace
204 
205 #endif
Success value.
Definition: Result.h:38
Error value.
Definition: Result.h:132
ROSE_UTIL_API std::string numberToString(long long)
Convert an integer to a string.
std::string plural(T n, const std::string &plural_phrase, const std::string &singular_phrase="")
Helpful way to print singular or plural words.
std::enable_if< std::is_integral< IntegralType >::value, IntegralType >::type toNumberOrThrow(const std::string &s)
Safely convert a string to a number using C++ style syntax.
std::vector< std::string > toStrings_range(Iterator begin, Iterator end, const Stringifier &stringifier=numberToString)
Converts a bunch of numbers to strings.
std::enable_if< std::is_integral< IntegralType >::value, Sawyer::Result< IntegralType, std::string > >::type toNumber(const std::string &s)
Safely convert a string to a number using C++ style syntax.
Holds a value or nothing.
Definition: Optional.h:49
Result containing a value or an error.
Definition: Result.h:236
Main namespace for the ROSE library.
ROSE_UTIL_API unsigned hexadecimalToInt(char)
Convert an ASCII hexadecimal character to an integer.
std::vector< std::string > toStrings(const Container &numbers, const Stringifier &stringifier=numberToString)
Converts a bunch of numbers to strings.
Sawyer::Optional< typename std::enable_if< std::is_integral< IntegralType >::value, IntegralType >::type > toDigit(char ch, IntegralType radix=10)
Convert a character to a numeric digit.
Shortens names of int64_t stringifiers.
Definition: stringify.h:20
Represents no value.
Definition: Optional.h:32