25#define STRING_CONV_HAVE_CHAR_CONV
28#if defined __has_include
29#if __has_include(<charconv>)
31#ifdef __cpp_lib_to_chars
32#define STRING_CONV_HAVE_CHAR_CONV
38namespace a_util::strings::conversion {
40enum class float_format { general = 0, scientific = 1, fixed = 2, hex = 3 };
44#ifdef STRING_CONV_HAVE_CHAR_CONV
47inline std::string float_to_string(T value, float_format
format)
49 std::array<char, std::numeric_limits<T>::max_exponent10 + 2> buffer{};
50 std::to_chars_result conversion_result;
52 case float_format::general: {
53 conversion_result = std::to_chars(
54 buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::general, 100);
57 case float_format::fixed: {
58 conversion_result = std::to_chars(
59 buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::fixed);
62 case float_format::scientific: {
63 conversion_result = std::to_chars(
64 buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::scientific);
67 case float_format::hex: {
68 conversion_result = std::to_chars(
69 buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::hex);
73 throw std::invalid_argument(
"unknown float format");
77 if (conversion_result.ec != std::errc()) {
78 throw std::runtime_error(
"cannot convert number to string: " +
79 std::make_error_code(conversion_result.ec).message());
82 return std::string(buffer.data(), conversion_result.ptr);
87inline T string_to_float(std::string_view value)
90 const auto conversion_result =
91 std::from_chars(value.data(), value.data() + value.size(), result);
92 if (conversion_result.ec != std::errc()) {
93 throw std::runtime_error(
"cannot convert string '" + std::string(value) +
"' to number: " +
94 std::make_error_code(conversion_result.ec).message());
96 else if (conversion_result.ptr != value.data() + value.size()) {
97 throw std::runtime_error(
"cannot convert string '" + std::string(value) +
98 "' to number: trailing chars found");
105const std::locale& get_c_locale();
108inline std::string float_to_string(T value, float_format
format)
110 std::ostringstream stream_used;
111 stream_used.imbue(get_c_locale());
114 case float_format::general:
115 stream_used << std::setprecision(100);
117 case float_format::fixed:
118 stream_used << std::fixed;
120 case float_format::scientific:
121 stream_used << std::scientific;
123 case float_format::hex:
124 stream_used << std::hexfloat;
128 stream_used << value;
130 return stream_used.str();
134inline T string_to_float(std::string_view value)
137 std::istringstream stream_used{std::string(value)};
138 stream_used.imbue(get_c_locale());
139 stream_used >> result;
140 if (stream_used.fail() || !stream_used.eof()) {
141 throw std::invalid_argument(
"not a number");
148inline std::pair<int, size_t> get_base(std::string_view value)
150 const size_t nSignLength =
151 (!value.empty() && (value.front() ==
'-' || value.front() ==
'+')) ? 1 : 0;
152 const std::string_view strDigits = value.substr(nSignLength);
154 if (strDigits.size() > 1 && strDigits[0] ==
'0') {
155 if (strDigits[1] ==
'x' || strDigits[1] ==
'X') {
157 return {16, nSignLength + 2};
159 if (strDigits[1] ==
'b' || strDigits[1] ==
'B') {
161 return {2, nSignLength + 2};
163 if (strDigits[1] >=
'0' && strDigits[1] <=
'7') {
165 return {8, nSignLength + 1};
169 return {10, nSignLength};
175inline std::string to_string(T value, float_format = float_format::fixed)
177 return std::to_string(value);
181inline std::string to_string(
float value, float_format
format)
183 return detail::float_to_string(value,
format);
187inline std::string to_string(
double value, float_format
format)
189 return detail::float_to_string(value,
format);
217 bool negative =
false;
219 const auto base_helper = detail::get_base(value);
220 base = base_helper.first;
227 if (base_helper.second > 0) {
228 negative = !value.empty() && value.front() ==
'-';
229 value = value.substr(base_helper.second);
233#ifdef STRING_CONV_HAVE_CHAR_CONV
234 const auto fnConvert = [&](std::string_view value,
auto&
result,
int base) {
235 const auto conversion_result =
236 std::from_chars(value.data(), value.data() + value.size(),
result, base);
237 if (conversion_result.ec != std::errc()) {
238 const auto strMessage =
239 "cannot convert string '" + std::string(value) +
240 "' to number: " + std::make_error_code(conversion_result.ec).message();
242 if (conversion_result.ec == std::errc::result_out_of_range) {
243 throw std::out_of_range(strMessage);
246 throw std::runtime_error(strMessage);
250 const auto fnConvert = [&](std::string_view value,
auto&
result,
int base) {
251 using U = std::decay_t<
decltype(
result)>;
253 if constexpr (std::is_signed_v<U>) {
254 const auto helper = std::stoll(std::string(value), 0, base);
255 if (helper > (std::numeric_limits<U>::max)() ||
256 helper < std::numeric_limits<U>::lowest()) {
257 throw std::out_of_range(
"out of range");
259 result =
static_cast<U
>(helper);
262 const auto helper = std::stoull(std::string(value), 0, base);
263 if (helper > (std::numeric_limits<U>::max)()) {
264 throw std::out_of_range(
"out of range");
266 result =
static_cast<U
>(helper);
269 catch (
const std::out_of_range& oError) {
270 const auto strMessage =
271 "cannot convert string '" + std::string(value) +
"' to number: " + oError.what();
272 throw std::out_of_range(strMessage);
278 if constexpr (std::is_signed_v<T>) {
283 using unsigned_t = std::make_unsigned_t<T>;
284 unsigned_t magnitude = 0;
285 fnConvert(value, magnitude, base);
287 const unsigned_t max_magnitude =
288 static_cast<unsigned_t
>((std::numeric_limits<T>::max)()) + 1;
289 if (magnitude > max_magnitude) {
290 throw std::out_of_range(
291 "cannot convert string '" + std::string(value) +
"' to number: " +
292 std::make_error_code(std::errc::result_out_of_range).message());
295 result =
static_cast<T
>(0 - magnitude);
298 throw std::runtime_error(
299 "cannot convert string '" + std::string(value) +
300 "' to number: negative value is not representable as an unsigned type");
304 fnConvert(value,
result, base);
311inline float to_number(std::string_view value,
int )
313 return detail::string_to_float<float>(value);
317inline double to_number(std::string_view value,
int )
319 return detail::string_to_float<double>(value);
Serves as component for functionality handling error and return types.
Definition result.h:20
std::string format(const char *str_format,...)
T to_number(std::string_view value, int base=0)
Definition strings_conversion.h:213