The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
lexical_cast.hpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2009 - 2025
3 by Mark de Wever <koraq@xs4all.nl>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
16/**
17 * @file
18 * New lexcical_cast header.
19 *
20 * For debugging you can include this header _in_ a namespace (to honor ODR)
21 * and have a set of functions that throws exceptions instead of doing the
22 * real job. This is done for the unit tests but should normally not be done.
23 */
24
25#ifdef LEXICAL_CAST_DEBUG
26#undef LEXICAL_CAST_HPP_INCLUDED
27#endif
28
29#ifndef LEXICAL_CAST_HPP_INCLUDED
30#define LEXICAL_CAST_HPP_INCLUDED
31
32#ifdef LEXICAL_CAST_DEBUG
33
34#undef DEBUG_THROW
35/**
36 * Throws an exception for debugging.
37 *
38 * @param id The unique name to identify the function.
39 * @note this name is a user defined string and
40 * should not be modified once used!
41 */
42#define DEBUG_THROW(id) throw id;
43#else
44
45#include "utils/charconv.hpp"
47
48#include <limits>
49#include <string>
50#include <sstream>
51#include <type_traits>
52
53#define DEBUG_THROW(id)
54#endif
55
56/**
57 * @namespace implementation
58 * Contains the implementation details for lexical_cast and shouldn't be used
59 * directly.
60 */
61namespace implementation {
62
63 template<
64 typename To
65 , typename From
66 , typename ToEnable = void
67 , typename FromEnable = void
68 >
69 struct lexical_caster;
70
71} // namespace implementation
72
73/**
74 * Lexical cast converts one type to another.
75 *
76 * @tparam To The type to convert to.
77 * @tparam From The type to convert from.
78 *
79 * @param value The value to convert.
80 *
81 * @returns The converted value.
82 *
83 * @throw bad_lexical_cast if the cast was unsuccessful.
84 */
85template<typename To, typename From>
86inline To lexical_cast(From value)
87{
88 return implementation::lexical_caster<To, From>().operator()(value, utils::nullopt);
89}
90
91/**
92 * Lexical cast converts one type to another with a fallback.
93 *
94 * @tparam To The type to convert to.
95 * @tparam From The type to convert from.
96 *
97 * @param value The value to convert.
98 * @param fallback The fallback value to return if the cast fails.
99 *
100 * @returns The converted value.
101 */
102template<typename To, typename From>
103inline To lexical_cast_default(From value, To fallback = To())
104{
105 return implementation::lexical_caster<To, From>().operator()(value, fallback);
106}
107
108/** Thrown when a lexical_cast fails. */
109struct bad_lexical_cast : std::exception
110{
111 const char* what() const noexcept
112 {
113 return "bad_lexical_cast";
114 }
115};
116
117namespace implementation {
118
119/**
120 * Base class for the conversion.
121 *
122 * Since functions can't be partially specialized we use a class, which can be
123 * partially specialized for the conversion.
124 *
125 * @tparam To The type to convert to.
126 * @tparam From The type to convert from.
127 * @tparam ToEnable Filter to enable the To type.
128 * @tparam FromEnable Filter to enable the From type.
129 */
130template<
131 typename To
132 , typename From
133 , typename ToEnable
134 , typename FromEnable
135>
137{
138 To operator()(From value, utils::optional<To> fallback) const
139 {
140 DEBUG_THROW("generic");
141
142 To result = To();
143 std::stringstream sstr;
144
145 if(!(sstr << value && sstr >> result)) {
146 if(fallback) { return *fallback; }
147
148 throw bad_lexical_cast();
149 } else {
150 return result;
151 }
152 }
153};
154
155/**
156 * Specialized conversion class.
157 *
158 * Specialized for returning strings from an integral type or a pointer to an
159 * integral type.
160 */
161template <typename From>
163 std::string
164 , From
165 , void
166 , std::enable_if_t<std::is_arithmetic_v<From>>
167>
168{
169 std::string operator()(From value, utils::optional<std::string>) const
170 {
171 DEBUG_THROW("specialized - To std::string - From arithmetic");
172 if constexpr (std::is_same_v<bool, From>) {
173 return value ? "1" : "0";
174 } else {
176 }
177 }
178};
179
180
181
182/**
183 * Specialized conversion class.
184 *
185 * @note is specialized to silence C4804 from MSVC.
186 */
187template <>
189 bool
190 , std::string_view
191 , void
192 , void
193>
194{
195 bool operator()(std::string_view str, utils::optional<bool> fallback) const
196 {
197 DEBUG_THROW("specialized - To bool - From string");
199 if(str == "1") {
200 return true;
201 } else if(str == "0") {
202 return false;
203 } else if (fallback) {
204 return *fallback;
205 } else {
206 throw bad_lexical_cast();
207 }
208 }
209};
210
211/**
212 * Specialized conversion class.
213 *
214 * Specialized for returning arithmetic from a string_view, also used by std::string and (const) char*
215 */
216template <typename To>
218 To
219 , std::string_view
220 , std::enable_if_t<std::is_arithmetic_v<To>>
221 , void
222>
223{
224 To operator()(std::string_view str, utils::optional<To> fallback) const
225 {
226 DEBUG_THROW("specialized - To arithmetic - From string");
227
228 To res = To();
229
231
232 auto [ptr, ec] = utils::charconv::from_chars(str.data(), str.data() + str.size(), res);
233 if(ec == std::errc()) {
234 return res;
235 } else if(fallback){
236 return *fallback;
237 } else {
238 throw bad_lexical_cast();
239 }
240 }
241};
242
243/**
244 * Specialized conversion class.
245 *
246 * Specialized for returning arithmetic from a std::string
247 */
248template <typename To>
250 To
251 , std::string
252 , std::enable_if_t<std::is_arithmetic_v<To>>
253 , void
254>
255{
256 To operator()(const std::string& value, utils::optional<To> fallback) const
257 {
258 // Dont DEBUG_THROW. the test shodul test what actual implementaiton is used in the end, not which specialazion that jsut forwards to another
259 if(fallback) {
260 return lexical_cast_default<To>(std::string_view(value), *fallback);
261 } else {
262 return lexical_cast<To>(std::string_view(value));
263 }
264 }
265};
266
267
268/**
269 * Specialized conversion class.
270 *
271 * Specialized for returning arithmetic from a (const) char*.
272 */
273template <class To, class From>
275 To
276 , From
277 , std::enable_if_t<std::is_arithmetic_v<To> >
278 , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*> >
279>
280{
281 To operator()(const std::string& value, utils::optional<To> fallback) const
282 {
283 // Dont DEBUG_THROW. the test shodul test what actual implementaiton is used in the end, not which specialazion that jsut forwards to another
284 if(fallback) {
285 return lexical_cast_default<To>(std::string_view(value), *fallback);
286 } else {
287 return lexical_cast<To>(std::string_view(value));
288 }
289 }
290};
291
292} // namespace implementation
293
294#endif
To lexical_cast_default(From value, To fallback=To())
Lexical cast converts one type to another with a fallback.
#define DEBUG_THROW(id)
To lexical_cast(From value)
Lexical cast converts one type to another.
Contains the implementation details for lexical_cast and shouldn't be used directly.
STL namespace.
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
void trim_for_from_chars(std::string_view &v)
Definition charconv.hpp:86
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
Thrown when a lexical_cast fails.
const char * what() const noexcept
bool operator()(std::string_view str, utils::optional< bool > fallback) const
Base class for the conversion.
To operator()(From value, utils::optional< To > fallback) const
std::string to_string() const
Definition charconv.hpp:133