ADTF
string_conversion.h
Go to the documentation of this file.
1 
7 #pragma once
8 #include <string>
9 #include <string_view>
10 #include <sstream>
11 #include <locale>
12 #include <array>
13 #include <limits>
14 
15 #ifdef _WIN32
16  #define STRING_CONV_HAVE_CHAR_CONV
17  #include <charconv>
18 #else
19  #if defined __has_include
20  #if __has_include (<charconv>)
21  #include <charconv>
22  #ifdef __cpp_lib_to_chars
23  #define STRING_CONV_HAVE_CHAR_CONV
24  #endif
25  #endif
26  #endif
27 #endif
28 
29 namespace adtf::base::string_conversion
30 {
31 
32 enum class float_format
33 {
34  general = 0,
35  scientific = 1,
36  fixed = 2,
37  hex = 3
38 };
39 
40 namespace detail
41 {
42 
43 #ifdef STRING_CONV_HAVE_CHAR_CONV
44 
45 template <typename T>
46 inline std::string float_to_string(T value, float_format format)
47 {
48  std::chars_format target_format = std::chars_format::fixed;
49  switch (format)
50  {
51  case float_format::general: target_format = std::chars_format::general; break;
52  case float_format::fixed: target_format = std::chars_format::fixed; break;
53  case float_format::scientific: target_format = std::chars_format::scientific; break;
54  case float_format::hex: target_format = std::chars_format::hex; break;
55  }
56 
57  std::array<char, std::numeric_limits<T>::max_exponent10 + 2> buffer {};
58  const auto conversion_result = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, target_format);
59  if (conversion_result.ec != std::errc())
60  {
61  throw std::runtime_error("cannot convert number to string: " + std::make_error_code(conversion_result.ec).message());
62  }
63  else
64  {
65  return std::string(buffer.data(), conversion_result.ptr);
66  }
67 }
68 
69 template <typename T>
70 inline T string_to_float(std::string_view value)
71 {
72  T result = 0;
73  const auto conversion_result = std::from_chars(value.data(), value.data() + value.size(), result);
74  if (conversion_result.ec != std::errc())
75  {
76  throw std::runtime_error("cannot convert string '" + std::string(value) + "' to number: " +
77  std::make_error_code(conversion_result.ec).message());
78  }
79  return result;
80 }
81 
82 #else
83 
84 const std::locale& get_c_locale();
85 
86 template <typename T>
87 inline std::string float_to_string(T value, float_format format)
88 {
89  std::ostringstream oStream;
90  oStream.imbue(get_c_locale());
91 
92  switch (format)
93  {
94  case float_format::general: break;
95  case float_format::fixed: oStream << std::fixed ; break;
96  case float_format::scientific: oStream << std::scientific; break;
97  case float_format::hex: oStream << std::hexfloat; break;
98  }
99 
100  oStream << value;
101 
102  return oStream.str();
103 }
104 
105 template <typename T>
106 inline T string_to_float(std::string_view value)
107 {
108  T result = {};
109  std::istringstream oStream {std::string(value)};
110  oStream.imbue(get_c_locale());
111  oStream >> result;
112  if (oStream.fail())
113  {
114  throw std::invalid_argument("not a number");
115  }
116  return result;
117 }
118 
119 #endif
120 
121 }
122 
123 template <typename T>
124 inline std::string to_string(T value, float_format /* format */ = float_format::fixed)
125 {
126  return std::to_string(value);
127 }
128 
129 template <>
130 inline std::string to_string(float value, float_format format)
131 {
132  return detail::float_to_string(value, format);
133 }
134 
135 template <>
136 inline std::string to_string(double value, float_format format)
137 {
138  return detail::float_to_string(value, format);
139 }
140 
141 std::pair<int, size_t> get_base(std::string_view value);
142 
143 template <typename T>
144 inline T to_number(std::string_view value, int base = 0)
145 {
146  T result = 0;
147 
148 #ifdef STRING_CONV_HAVE_CHAR_CONV
149  if (base == 0)
150  {
151  const auto base_helper = get_base(value);
152  value = value.substr(base_helper.second);
153  base = base_helper.first;
154  }
155  const auto conversion_result = std::from_chars(value.data(), value.data() + value.size(), result, base);
156 
157  if (conversion_result.ec != std::errc())
158  {
159  const auto strMessage = "cannot convert string '" + std::string(value) + "' to number: " +
160  std::make_error_code(conversion_result.ec).message();
161 
162  if (conversion_result.ec == std::errc::result_out_of_range)
163  {
164  throw std::overflow_error(strMessage);
165  }
166 
167  throw std::runtime_error(strMessage);
168  }
169 #else
170  try
171  {
172  if constexpr(std::is_signed_v<T>)
173  {
174  const auto helper = std::stoll(std::string(value), 0, base);
175  if (helper > std::numeric_limits<T>::max() ||
176  helper < std::numeric_limits<T>::lowest())
177  {
178  throw std::out_of_range("out of range");
179  }
180  result = static_cast<T>(helper);
181  }
182  else
183  {
184  const auto helper = std::stoull(std::string(value), 0, base);
185  if (helper > std::numeric_limits<T>::max())
186  {
187  throw std::out_of_range("out of range");
188  }
189  result = static_cast<T>(helper);
190  }
191  }
192  catch (const std::out_of_range& oError)
193  {
194  const auto strMessage = "cannot convert string '" + std::string(value) + "' to number: " + oError.what();
195  throw std::overflow_error(strMessage);
196  }
197 #endif
198 
199  return result;
200 }
201 
202 template <>
203 inline float to_number(std::string_view value, int /*base = 10*/)
204 {
205  return detail::string_to_float<float>(value);
206 }
207 
208 template <>
209 inline double to_number(std::string_view value, int /*base = 10*/)
210 {
211  return detail::string_to_float<double>(value);
212 }
213 
214 }
cString to_string(const tResult &i_oResult, eResultFormatFlags i_eFormatFlags=eResultFormatFlags::RFF_DisableNone, const tChar *i_strFormat=nullptr)
Copy all information of an assigned result object to a (formatted) string.
std::string format(const char *str_format,...)
printf()-like formatting of an input string.