The Battle for Wesnoth  1.19.0-dev
lexical_cast.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2024
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 <optional>
46 
47 #include <cstdlib>
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  */
61 namespace 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  */
85 template<typename To, typename From>
86 inline To lexical_cast(From value)
87 {
88  return implementation::lexical_caster<To, From>().operator()(value, std::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  */
102 template<typename To, typename From>
103 inline 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. */
109 struct bad_lexical_cast : std::exception
110 {
111  const char* what() const noexcept
112  {
113  return "bad_lexical_cast";
114  }
115 };
116 
117 namespace 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  */
130 template<
131  typename To
132  , typename From
133  , typename ToEnable
134  , typename FromEnable
135 >
137 {
138  To operator()(From value, std::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  */
161 template <typename From>
163  std::string
164  , From
165  , void
166  , std::enable_if_t<std::is_integral_v<std::remove_pointer_t<From>>>
167 >
168 {
169  std::string operator()(From value, std::optional<std::string>) const
170  {
171  DEBUG_THROW("specialized - To std::string - From integral (pointer)");
172 
173  std::stringstream sstr;
174  sstr << value;
175  return sstr.str();
176  }
177 };
178 
179 /**
180  * Specialized conversion class.
181  *
182  * Specialized for returning a long long from a (const) char*.
183  * @note is separate from the other signed types since a long long has a
184  * performance penalty at 32 bit systems.
185  */
186 template <class From>
188  long long
189  , From
190  , void
191  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
192  >
193 {
194  long long operator()(From value, std::optional<long long> fallback) const
195  {
196  DEBUG_THROW("specialized - To long long - From (const) char*");
197 
198  if(fallback) {
199  return lexical_cast_default<long long>(std::string(value), *fallback);
200  } else {
201  return lexical_cast<long long>(std::string(value));
202  }
203  }
204 };
205 
206 /**
207  * Specialized conversion class.
208  *
209  * Specialized for returning a long long from a std::string.
210  * @note is separate from the other signed types since a long long has a
211  * performance penalty at 32 bit systems.
212  */
213 template <>
215  long long
216  , std::string
217  >
218 {
219  long long operator()(const std::string& value, std::optional<long long> fallback) const
220  {
221  DEBUG_THROW("specialized - To long long - From std::string");
222 
223  if(value.empty()) {
224  if(fallback) {
225  return *fallback;
226  } else {
227  throw bad_lexical_cast();
228  }
229  }
230 
231  try {
232  return std::stoll(value);
233  } catch(const std::invalid_argument&) {
234  } catch(const std::out_of_range&) {
235  }
236 
237  if(fallback) {
238  return *fallback;
239  } else {
240  throw bad_lexical_cast();
241  }
242  }
243 };
244 
245 /**
246  * Specialized conversion class.
247  *
248  * Specialized for returning a signed type from a (const) char*.
249  */
250 template <class To, class From>
252  To
253  , From
254  , std::enable_if_t<std::is_integral_v<To> && std::is_signed_v<To> && !std::is_same_v<To, long long>>
255  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
256  >
257 {
258  To operator()(From value, std::optional<To> fallback) const
259  {
260  DEBUG_THROW("specialized - To signed - From (const) char*");
261 
262  if(fallback) {
263  return lexical_cast_default<To>(std::string(value), *fallback);
264  } else {
265  return lexical_cast<To>(std::string(value));
266  }
267  }
268 };
269 
270 /**
271  * Specialized conversion class.
272  *
273  * Specialized for returning a signed type from a std::string.
274  */
275 template <class To>
277  To
278  , std::string
279  , std::enable_if_t<std::is_integral_v<To> && std::is_signed_v<To> && !std::is_same_v<To, long long>>
280  >
281 {
282  To operator()(const std::string& value, std::optional<To> fallback) const
283  {
284  DEBUG_THROW("specialized - To signed - From std::string");
285 
286  if(value.empty()) {
287  if(fallback) {
288  return *fallback;
289  } else {
290  throw bad_lexical_cast();
291  }
292  }
293 
294  try {
295  long res = std::stol(value);
296  if(std::numeric_limits<To>::lowest() <= res && std::numeric_limits<To>::max() >= res) {
297  return static_cast<To>(res);
298  }
299  } catch(const std::invalid_argument&) {
300  } catch(const std::out_of_range&) {
301  }
302 
303  if(fallback) {
304  return *fallback;
305  } else {
306  throw bad_lexical_cast();
307  }
308  }
309 };
310 
311 /**
312  * Specialized conversion class.
313  *
314  * Specialized for returning a floating point type from a (const) char*.
315  */
316 template <class To, class From>
318  To
319  , From
320  , std::enable_if_t<std::is_floating_point_v<To>>
321  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
322  >
323 {
324  To operator()(From value, std::optional<To> fallback) const
325  {
326  DEBUG_THROW("specialized - To floating point - From (const) char*");
327 
328  if(fallback) {
329  return lexical_cast_default<To>(std::string(value), *fallback);
330  } else {
331  return lexical_cast<To>(std::string(value));
332  }
333  }
334 };
335 
336 /**
337  * Specialized conversion class.
338  *
339  * Specialized for returning a floating point type from a std::string.
340  */
341 template <class To>
343  To
344  , std::string
345  , std::enable_if_t<std::is_floating_point_v<To>>
346  >
347 {
348  To operator()(const std::string& value, std::optional<To> fallback) const
349  {
350  DEBUG_THROW("specialized - To floating point - From std::string");
351 
352  if(value.empty()) {
353  if(fallback) {
354  return *fallback;
355  } else {
356  throw bad_lexical_cast();
357  }
358  }
359 
360  // Explicitly reject hexadecimal values. Unit tests of the config class require that.
361  if(value.find_first_of("Xx") != std::string::npos) {
362  if(fallback) {
363  return *fallback;
364  } else {
365  throw bad_lexical_cast();
366  }
367  }
368 
369  try {
370  long double res = std::stold(value);
371  if((static_cast<long double>(std::numeric_limits<To>::lowest()) <= res) && (static_cast<long double>(std::numeric_limits<To>::max()) >= res)) {
372  return static_cast<To>(res);
373  }
374  } catch(const std::invalid_argument&) {
375  } catch(const std::out_of_range&) {
376  }
377 
378  if(fallback) {
379  return *fallback;
380  } else {
381  throw bad_lexical_cast();
382  }
383  }
384 };
385 
386 /**
387  * Specialized conversion class.
388  *
389  * Specialized for returning a unsigned long long from a (const) char*.
390  * @note is separate from the other unsigned types since a unsigned long long
391  * has a performance penalty at 32 bit systems.
392  */
393 template <class From>
395  unsigned long long
396  , From
397  , void
398  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
399  >
400 {
401  unsigned long long operator()(From value, std::optional<unsigned long long> fallback) const
402  {
403  DEBUG_THROW(
404  "specialized - To unsigned long long - From (const) char*");
405 
406  if(fallback) {
407  return lexical_cast_default<unsigned long long>(std::string(value), *fallback);
408  } else {
409  return lexical_cast<unsigned long long>(std::string(value));
410  }
411  }
412 };
413 
414 /**
415  * Specialized conversion class.
416  *
417  * Specialized for returning a unsigned long long from a std::string.
418  * @note is separate from the other unsigned types since a unsigned long long
419  * has a performance penalty at 32 bit systems.
420  */
421 template <>
423  unsigned long long
424  , std::string
425  >
426 {
427  unsigned long long operator()(const std::string& value, std::optional<unsigned long long> fallback) const
428  {
429  DEBUG_THROW("specialized - To unsigned long long - From std::string");
430 
431  if(value.empty()) {
432  if(fallback) {
433  return *fallback;
434  } else {
435  throw bad_lexical_cast();
436  }
437  }
438 
439  try {
440  return std::stoull(value);
441  } catch(const std::invalid_argument&) {
442  } catch(const std::out_of_range&) {
443  }
444 
445  if(fallback) {
446  return *fallback;
447  } else {
448  throw bad_lexical_cast();
449  }
450  }
451 };
452 
453 /**
454  * Specialized conversion class.
455  *
456  * Specialized for returning a unsigned type from a (const) char*.
457  */
458 template <class To, class From>
460  To
461  , From
462  , std::enable_if_t<std::is_unsigned_v<To> && !std::is_same_v<To, unsigned long long>>
463  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
464  >
465 {
466  To operator()(From value, std::optional<To> fallback) const
467  {
468  DEBUG_THROW("specialized - To unsigned - From (const) char*");
469 
470  if(fallback) {
471  return lexical_cast_default<To>(std::string(value), *fallback);
472  } else {
473  return lexical_cast<To>(std::string(value));
474  }
475  }
476 };
477 
478 /**
479  * Specialized conversion class.
480  *
481  * Specialized for returning a unsigned type from a std::string.
482  */
483 template <class To>
485  To
486  , std::string
487  , std::enable_if_t<std::is_unsigned_v<To>>
488  >
489 {
490  To operator()(const std::string& value, std::optional<To> fallback) const
491  {
492  DEBUG_THROW("specialized - To unsigned - From std::string");
493 
494  if(value.empty()) {
495  if(fallback) {
496  return *fallback;
497  } else {
498  throw bad_lexical_cast();
499  }
500  }
501 
502  try {
503  unsigned long res = std::stoul(value);
504  // No need to check the lower bound, it's zero for all unsigned types.
505  if(std::numeric_limits<To>::max() >= res) {
506  return static_cast<To>(res);
507  }
508  } catch(const std::invalid_argument&) {
509  } catch(const std::out_of_range&) {
510  }
511 
512  if(fallback) {
513  return *fallback;
514  } else {
515  throw bad_lexical_cast();
516  }
517  }
518 };
519 
520 /**
521  * Specialized conversion class.
522  *
523  * Specialized for returning a bool from a std::string.
524  * @note is specialized to silence C4804 from MSVC.
525  */
526 template <>
527 struct lexical_caster<bool, std::string>
528 {
529  bool operator()(const std::string& value, std::optional<bool>) const
530  {
531  DEBUG_THROW("specialized - To bool - From std::string");
532 
533  return value == "1";
534  }
535 };
536 
537 /**
538  * Specialized conversion class.
539  *
540  * Specialized for returning a bool from a (const) char*.
541  * @note is specialized to silence C4804 from MSVC.
542  */
543 template <class From>
545  bool
546  , From
547  , void
548  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
549  >
550 {
551  bool operator()(From value, std::optional<bool>) const
552  {
553  DEBUG_THROW("specialized - To bool - From (const) char*");
554 
555  return lexical_cast<bool>(std::string(value));
556  }
557 };
558 
559 } // namespace implementation
560 
561 #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.
Thrown when a lexical_cast fails.
const char * what() const noexcept
To operator()(const std::string &value, std::optional< To > fallback) const
bool operator()(const std::string &value, std::optional< bool >) const
long long operator()(const std::string &value, std::optional< long long > fallback) const
std::string operator()(From value, std::optional< std::string >) const
unsigned long long operator()(const std::string &value, std::optional< unsigned long long > fallback) const
Base class for the conversion.
To operator()(From value, std::optional< To > fallback) const