The Battle for Wesnoth  1.19.17+dev
charconv.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2024 - 2025
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #pragma once
16 
17 #include <boost/version.hpp>
18 
19 #include <array>
20 #include <assert.h>
21 #include <cctype>
22 #include <charconv>
23 #include "utils/optional_fwd.hpp"
24 #include <string_view>
25 #include <string>
26 #include <stdexcept>
27 // The gcc implemenetation of from_chars is in some versions just a temporaty solution that calls
28 // strtod that's why we prefer the boost version if available.
29 #if BOOST_VERSION >= 108500 && __has_include(<boost/charconv.hpp>)
30  #define USE_BOOST_CHARCONV
31 #elif defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE >= 11
32  #define USE_STD_CHARCONV
33 #elif defined(_MSC_VER) && _MSC_VER >= 1924
34  #define USE_STD_CHARCONV
35 #elif defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 14000
36  #define USE_FALLBACK_CHARCONV
37 #else
38  #error No charconv implementation found.
39 #endif
40 
41 #ifdef USE_BOOST_CHARCONV
42 #include <boost/charconv.hpp>
43 namespace charconv_impl = boost::charconv;
44 #else
45 namespace charconv_impl = std;
46 #endif
47 
48 
49 namespace utils::charconv
50 {
54 
55  template<typename... T>
56  to_chars_result to_chars(char* first, char* last, T&&... value )
57  {
58  return charconv_impl::to_chars(first, last, value...);
59  }
60 
61  template<typename T>
62  std::enable_if_t<std::is_integral_v<T>, std::from_chars_result> from_chars(const char* first, const char* last, T& value, int base = 10 )
63  {
64  return std::from_chars(first, last, value, base);
65  }
66 
67 #ifndef USE_FALLBACK_CHARCONV
68  template<typename T>
69  std::enable_if_t<std::is_floating_point_v<T>, from_chars_result> from_chars(const char* first, const char* last, T& value, chars_format fmt = chars_format::general )
70  {
71  return charconv_impl::from_chars(first, last, value, fmt);
72  }
73 #else
74  template<typename T>
75  std::enable_if_t<std::is_same_v<T, double> || std::is_same_v<T, float> || std::is_same_v<T, long double>, from_chars_result> from_chars(const char* first, const char* last, T& value, chars_format fmt = chars_format::general );
76 #endif
77 
78  // the maximum size of a string that to_chars produces for type T, with the default chars_format
79  template<class T>
80  constexpr std::size_t buffer_size = 50;
81 }
82 
83 namespace utils
84 {
85  // from_chars doesnt support leading whitespaces, call this before from_chars if you want to accept leading whitespaces.
86  inline void trim_for_from_chars(std::string_view& v)
87  {
88  while(!v.empty() && std::isspace(v.front())) {
89  v.remove_prefix(1);
90  }
91  if(v.size() >= 2 && v[0] == '+' && v[1] != '-' ) {
92  v.remove_prefix(1);
93  }
94  }
95 
96 
97  // converts a number to a char buffer without allocations.
98  template<typename TNum>
100  {
101  std::array<char, utils::charconv::buffer_size<TNum>> buffer;
102  std::size_t size;
103 
105  : size(0)
106  {
107  }
108 
109  charconv_buffer(TNum num)
110  : size(0)
111  {
112  set_value(num);
113  }
114 
115  void set_value(TNum num)
116  {
117  auto [ptr, ec] = utils::charconv::to_chars(buffer.data(), buffer.data() + buffer.size(), num);
118  if(ec != std::errc()) {
119  // Shouldnt happen by definition of utils::charconv::buffer_size<TNum>
120  assert(!"Error in charconv_buffer, buffer not large enough");
121  size = 0;
122  } else {
123  size = ptr - buffer.data();
124  }
125  //TODO: should we make this null-terminated?
126  }
127 
128  std::string_view get_view() const
129  {
130  return std::string_view(buffer.data(), size);
131  }
132 
133  std::string to_string() const
134  {
135  return std::string(buffer.data(), size);
136  }
137  };
138 
139  /// Same interface as std::stod and meant as a drop in replacement, except:
140  /// - It takes a std::string_view
141  /// - It is locale independent
142  inline double stod(std::string_view str) {
143  trim_for_from_chars(str);
144  double res;
145  auto [ptr, ec] = utils::charconv::from_chars(str.data(), str.data() + str.size(), res);
146  if(ec == std::errc::invalid_argument) {
147  throw std::invalid_argument("Failed to convert string to double: input contains invalid characters or is not a valid number.");
148  } else if(ec == std::errc::result_out_of_range) {
149  throw std::out_of_range("Failed to convert string to double: input value is out of the representable range for a double.");
150  }
151  return res;
152  }
153 
154  /// Same interface as std::stoi and meant as a drop in replacement, except:
155  /// - It takes a std::string_view
156  inline int stoi(std::string_view str) {
157  trim_for_from_chars(str);
158  int res;
159  auto [ptr, ec] = utils::charconv::from_chars(str.data(), str.data() + str.size(), res);
160  if(ec == std::errc::invalid_argument) {
161  throw std::invalid_argument("Failed to convert string to int: input contains invalid characters or is not a valid number.");
162  } else if(ec == std::errc::result_out_of_range) {
163  throw std::out_of_range("Failed to convert string to int: input value is out of the representable range for an int.");
164  }
165  return res;
166  }
167 
168 #ifdef __cpp_concepts
169  template<std::integral T>
170  inline auto from_chars(std::string_view str, int base = 10) -> utils::optional<T>
171 #else
172  template<typename T>
173  inline auto from_chars(std::string_view str, int base = 10) -> std::enable_if_t<std::is_integral_v<T>, utils::optional<T>>
174 #endif
175  {
176  trim_for_from_chars(str);
177  T result{};
178  const auto [_, ec] = utils::charconv::from_chars(str.data(), str.data() + str.size(), result, base);
179  return ec == std::errc{} ? utils::make_optional(result) : utils::nullopt;
180  }
181 
182 #ifdef __cpp_concepts
183  template<std::floating_point T>
184  inline auto from_chars(std::string_view str,
186 #else
187  template<typename T>
188  inline auto from_chars(std::string_view str,
189  utils::charconv::chars_format fmt = utils::charconv::chars_format::general) -> std::enable_if_t<std::is_floating_point_v<T>, utils::optional<T>>
190 #endif
191  {
192  trim_for_from_chars(str);
193  T result{};
194  const auto [_, ec] = utils::charconv::from_chars(str.data(), str.data() + str.size(), result, fmt);
195  return ec == std::errc{} ? utils::make_optional(result) : utils::nullopt;
196  }
197 }
static std::string _(const char *str)
Definition: gettext.hpp:97
log_domain & general()
Definition: log.cpp:365
std::enable_if_t< std::is_integral_v< T >, std::from_chars_result > from_chars(const char *first, const char *last, T &value, int base=10)
Definition: charconv.hpp:62
charconv_impl::from_chars_result from_chars_result
Definition: charconv.hpp:52
charconv_impl::chars_format chars_format
Definition: charconv.hpp:51
to_chars_result to_chars(char *first, char *last, T &&... value)
Definition: charconv.hpp:56
charconv_impl::to_chars_result to_chars_result
Definition: charconv.hpp:53
constexpr std::size_t buffer_size
Definition: charconv.hpp:80
auto from_chars(std::string_view str, int base=10) -> std::enable_if_t< std::is_integral_v< T >, utils::optional< T >>
Definition: charconv.hpp:173
int stoi(std::string_view str)
Same interface as std::stoi and meant as a drop in replacement, except:
Definition: charconv.hpp:156
void trim_for_from_chars(std::string_view &v)
Definition: charconv.hpp:86
double stod(std::string_view str)
Same interface as std::stod and meant as a drop in replacement, except:
Definition: charconv.hpp:142
auto from_chars(std::string_view str, utils::charconv::chars_format fmt=utils::charconv::chars_format::general) -> std::enable_if_t< std::is_floating_point_v< T >, utils::optional< T >>
Definition: charconv.hpp:188
void set_value(TNum num)
Definition: charconv.hpp:115
std::string_view get_view() const
Definition: charconv.hpp:128
std::string to_string() const
Definition: charconv.hpp:133
std::array< char, utils::charconv::buffer_size< TNum > > buffer
Definition: charconv.hpp:101
charconv_buffer(TNum num)
Definition: charconv.hpp:109