str2num.h 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. // String to number conversion utilities
  2. //
  3. // Platform: ISO C++ 98/11
  4. // $Id$
  5. //
  6. // (c) __vic 2010
  7. #ifndef __VIC_STR2NUM_H
  8. #define __VIC_STR2NUM_H
  9. #include<__vic/defs.h>
  10. #include<__vic/ascii.h>
  11. #include<__vic/tchar.h>
  12. #include<string>
  13. #include<limits>
  14. #include<cerrno>
  15. namespace __vic {
  16. namespace impl {
  17. //////////////////////////////////////////////////////////////////////////////
  18. template<class UInt>
  19. class unsigned_decimal_parser
  20. {
  21. #if __cpp_constexpr
  22. static constexpr UInt
  23. decs = std::numeric_limits<UInt>::max() / UInt(10), // count of full decades in maximum value
  24. ones = std::numeric_limits<UInt>::max() % UInt(10); // count of the rest ones in maximum value
  25. #endif
  26. UInt res;
  27. public:
  28. template<class InputIterator>
  29. int parse(InputIterator begin, InputIterator end)
  30. {
  31. if(begin == end) return EDOM;
  32. #if !__cpp_constexpr
  33. const UInt decs = std::numeric_limits<UInt>::max() / UInt(10),
  34. ones = std::numeric_limits<UInt>::max() % UInt(10);
  35. #endif
  36. UInt res = 0;
  37. do {
  38. if(!ascii::isdigit(*begin)) return EDOM;
  39. UInt dig = *begin++ - '0';
  40. // check if the next increment will cause overflow
  41. if(res > decs || (res == decs && dig > ones)) return ERANGE;
  42. res *= UInt(10); // decimal left shift (one digit)
  43. res += dig;
  44. } while(begin != end);
  45. this->res = res;
  46. return 0;
  47. }
  48. int parse(const std::string &s) { return parse(&*s.begin(), &*s.end()); }
  49. int parse(const char *s) { return s ? parse(s, tchar::end(s)) : EDOM; }
  50. UInt result() const { return res; }
  51. };
  52. //////////////////////////////////////////////////////////////////////////////
  53. template<class Int>
  54. class signed_decimal_parser
  55. {
  56. #if __cpp_constexpr
  57. static constexpr Int
  58. decs = std::numeric_limits<Int>::max() / Int(10), // count of full decades in maximum value
  59. ones = std::numeric_limits<Int>::max() % Int(10); // count of the rest ones in maximum value
  60. #endif
  61. Int res;
  62. public:
  63. template<class InputIterator>
  64. int parse(InputIterator begin, InputIterator end)
  65. {
  66. if(begin == end) return EDOM;
  67. #if !__cpp_constexpr
  68. const Int decs = std::numeric_limits<Int>::max() / Int(10),
  69. ones = std::numeric_limits<Int>::max() % Int(10);
  70. #endif
  71. bool negative = false;
  72. switch(*begin)
  73. {
  74. case '-':
  75. negative = true;
  76. // no break, fall through
  77. case '+':
  78. ++begin;
  79. }
  80. Int res = 0;
  81. do {
  82. if(!ascii::isdigit(*begin)) return EDOM;
  83. Int dig = *begin++ - '0';
  84. // check if the next increment will cause overflow
  85. if(res > decs) return ERANGE;
  86. else if(res == decs && dig > ones)
  87. {
  88. // cut off the minimal negative case
  89. if(negative && dig == ones + 1 && begin == end)
  90. return (this->res = std::numeric_limits<Int>::min(), 0);
  91. return ERANGE;
  92. }
  93. res *= Int(10);
  94. res += dig;
  95. } while(begin != end);
  96. this->res = negative ? -res : res;
  97. return 0;
  98. }
  99. int parse(const std::string &s) { return parse(&*s.begin(), &*s.end()); }
  100. int parse(const char *s) { return s ? parse(s, tchar::end(s)) : EDOM; }
  101. Int result() const { return res; }
  102. };
  103. //////////////////////////////////////////////////////////////////////////////
  104. //----------------------------------------------------------------------------
  105. __VIC_NORETURN void throw_empty_integer();
  106. __VIC_NORETURN void throw_null_integer();
  107. __VIC_NORETURN void throw_non_digit_char();
  108. __VIC_NORETURN void throw_integer_too_long();
  109. //----------------------------------------------------------------------------
  110. } // namespace
  111. //////////////////////////////////////////////////////////////////////////////
  112. template<class > struct decimal_parser; // not defined
  113. template<> struct decimal_parser<signed char> : impl::signed_decimal_parser<signed char> {};
  114. template<> struct decimal_parser<short> : impl::signed_decimal_parser<short> {};
  115. template<> struct decimal_parser<int> : impl::signed_decimal_parser<int> {};
  116. template<> struct decimal_parser<long> : impl::signed_decimal_parser<long> {};
  117. template<> struct decimal_parser<__VIC_LONGLONG> : impl::signed_decimal_parser<__VIC_LONGLONG> {};
  118. template<> struct decimal_parser<unsigned char> : impl::unsigned_decimal_parser<unsigned char> {};
  119. template<> struct decimal_parser<unsigned short> : impl::unsigned_decimal_parser<unsigned short> {};
  120. template<> struct decimal_parser<unsigned> : impl::unsigned_decimal_parser<unsigned> {};
  121. template<> struct decimal_parser<unsigned long> : impl::unsigned_decimal_parser<unsigned long> {};
  122. template<> struct decimal_parser<unsigned __VIC_LONGLONG> : impl::unsigned_decimal_parser<unsigned __VIC_LONGLONG> {};
  123. //////////////////////////////////////////////////////////////////////////////
  124. //----------------------------------------------------------------------------
  125. // String to number conversion with strict format control
  126. //----------------------------------------------------------------------------
  127. template<class T>
  128. T decimal_to_number(const std::string &s)
  129. {
  130. decimal_parser<T> p;
  131. switch(p.parse(s))
  132. {
  133. case EDOM:
  134. if(s.empty()) impl::throw_empty_integer();
  135. else impl::throw_non_digit_char();
  136. case ERANGE:
  137. impl::throw_integer_too_long();
  138. }
  139. return p.result();
  140. }
  141. //----------------------------------------------------------------------------
  142. template<class T>
  143. T decimal_to_number(const char *s)
  144. {
  145. if(!s) impl::throw_null_integer();
  146. decimal_parser<T> p;
  147. switch(p.parse(s))
  148. {
  149. case EDOM:
  150. if(!*s) impl::throw_empty_integer();
  151. else impl::throw_non_digit_char();
  152. case ERANGE:
  153. impl::throw_integer_too_long();
  154. }
  155. return p.result();
  156. }
  157. //----------------------------------------------------------------------------
  158. template<class T, class InputIterator>
  159. T decimal_to_number_range(InputIterator begin, InputIterator end)
  160. {
  161. decimal_parser<T> p;
  162. switch(p.parse(begin, end))
  163. {
  164. case EDOM:
  165. if(begin == end) impl::throw_empty_integer();
  166. else impl::throw_non_digit_char();
  167. case ERANGE:
  168. impl::throw_integer_too_long();
  169. }
  170. return p.result();
  171. }
  172. //----------------------------------------------------------------------------
  173. //----------------------------------------------------------------------------
  174. // The same functions with the result as 2nd out argument
  175. //----------------------------------------------------------------------------
  176. template<class T>
  177. inline void decimal_to_number(const std::string &s, T &n)
  178. {
  179. n = decimal_to_number<T>(s);
  180. }
  181. //----------------------------------------------------------------------------
  182. template<class T>
  183. inline void decimal_to_number(const char *s, T &n)
  184. {
  185. n = decimal_to_number<T>(s);
  186. }
  187. //----------------------------------------------------------------------------
  188. template<class T, class InputIterator>
  189. inline void decimal_to_number_range(
  190. InputIterator begin, InputIterator end, T &n)
  191. {
  192. n = decimal_to_number_range<T>(begin, end);
  193. }
  194. //----------------------------------------------------------------------------
  195. } // namespace
  196. #endif // header guard