The Battle for Wesnoth  1.19.7+dev
charconv.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2024
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 <string_view>
23 #include <string>
24 #include <stdexcept>
25 // The gcc implemenetation of from_chars is in some versions just a temporaty solution that calls
26 // strtod that's why we prefer the boost version if available.
27 #if BOOST_VERSION >= 108500 && __has_include(<boost/charconv.hpp>)
28  #define USE_BOOST_CHARCONV
29 #elif defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE >= 11
30  #define USE_STD_CHARCONV
31 #elif defined(_MSC_VER) && _MSC_VER >= 1924
32  #define USE_STD_CHARCONV
33 #elif defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 14000
34  #define USE_FALLBACK_CHARCONV
35 #else
36  #error No charconv implementation found.
37 #endif
38 
39 #ifdef USE_BOOST_CHARCONV
40 #include <boost/charconv.hpp>
41 namespace charconv_impl = boost::charconv;
42 #else
43 #include <charconv>
44 namespace charconv_impl = std;
45 #endif
46 
47 
48 namespace utils::charconv
49 {
53 
54  template<typename... T>
55  to_chars_result to_chars(char* first, char* last, T&&... value )
56  {
57  return charconv_impl::to_chars(first, last, value...);
58  }
59 
60  template<typename T>
61  std::enable_if_t<std::is_integral_v<T>, from_chars_result> from_chars(const char* first, const char* last, T& value, int base = 10 )
62  {
63  return charconv_impl::from_chars(first, last, value, base);
64  }
65 
66 #ifndef USE_FALLBACK_CHARCONV
67  template<typename T>
68  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 )
69  {
70  return charconv_impl::from_chars(first, last, value, fmt);
71  }
72 #else
73  template<typename T>
74  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 );
75 #endif
76 
77  // the maximum size of a string that to_chars produces for type T, with the default chars_format
78  template<class T>
79  constexpr size_t buffer_size = 50;
80 }
81 
82 namespace utils
83 {
84  // from_chars doesnt support leading whitespaces, call this before from_chars if you want to accept leading whitespaces.
85  inline void trim_for_from_chars(std::string_view& v)
86  {
87  while(!v.empty() && std::isspace(v.front())) {
88  v.remove_prefix(1);
89  }
90  if(v.size() >= 2 && v[0] == '+' && v[1] != '-' ) {
91  v.remove_prefix(1);
92  }
93  }
94 
95 
96  // converts a number to a char buffer without allocations.
97  template<typename TNum>
99  {
100  std::array<char, utils::charconv::buffer_size<TNum>> buffer;
101  size_t size;
102 
104  : size(0)
105  {
106  }
107 
108  charconv_buffer(TNum num)
109  : size(0)
110  {
111  set_value(num);
112  }
113 
114  void set_value(TNum num)
115  {
116  auto [ptr, ec] = utils::charconv::to_chars(buffer.data(), buffer.data() + buffer.size(), num);
117  if(ec != std::errc()) {
118  // Shouldnt happen by definition of utils::charconv::buffer_size<TNum>
119  assert(!"Error in charconv_buffer, buffer not large enough");
120  size = 0;
121  } else {
122  size = ptr - buffer.data();
123  }
124  //TODO: should we make this null-terminated?
125  }
126 
127  std::string_view get_view() const
128  {
129  return std::string_view(buffer.data(), size);
130  }
131 
132  std::string to_string() const
133  {
134  return std::string(buffer.data(), size);
135  }
136  };
137 
138  /// Same interface as std::stod and meant as a drop in replacement, except:
139  /// - It takes a std::string_view
140  /// - It is locale independent
141  inline double stod(std::string_view str) {
142  trim_for_from_chars(str);
143  double res;
144  auto [ptr, ec] = utils::charconv::from_chars(str.data(), str.data() + str.size(), res);
145  if(ec == std::errc::invalid_argument) {
146  throw std::invalid_argument("");
147  } else if(ec == std::errc::result_out_of_range) {
148  throw std::out_of_range("");
149  }
150  return res;
151  }
152  /// Same interface as std::stoi and meant as a drop in replacement, except:
153  /// - It takes a std::string_view
154  inline int stoi(std::string_view str) {
155  trim_for_from_chars(str);
156  int res;
157  auto [ptr, ec] = utils::charconv::from_chars(str.data(), str.data() + str.size(), res);
158  if(ec == std::errc::invalid_argument) {
159  throw std::invalid_argument("");
160  } else if(ec == std::errc::result_out_of_range) {
161  throw std::out_of_range("");
162  }
163  return res;
164  }
165 }
log_domain & general()
Definition: log.cpp:333
std::enable_if_t< std::is_integral_v< T >, from_chars_result > from_chars(const char *first, const char *last, T &value, int base=10)
Definition: charconv.hpp:61
charconv_impl::from_chars_result from_chars_result
Definition: charconv.hpp:51
charconv_impl::chars_format chars_format
Definition: charconv.hpp:50
to_chars_result to_chars(char *first, char *last, T &&... value)
Definition: charconv.hpp:55
constexpr size_t buffer_size
Definition: charconv.hpp:79
charconv_impl::to_chars_result to_chars_result
Definition: charconv.hpp:52
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)
Definition: charconv.hpp:68
int stoi(std::string_view str)
Same interface as std::stoi and meant as a drop in replacement, except:
Definition: charconv.hpp:154
void trim_for_from_chars(std::string_view &v)
Definition: charconv.hpp:85
double stod(std::string_view str)
Same interface as std::stod and meant as a drop in replacement, except:
Definition: charconv.hpp:141
void set_value(TNum num)
Definition: charconv.hpp:114
std::string_view get_view() const
Definition: charconv.hpp:127
std::string to_string() const
Definition: charconv.hpp:132
std::array< char, utils::charconv::buffer_size< TNum > > buffer
Definition: charconv.hpp:100
charconv_buffer(TNum num)
Definition: charconv.hpp:108