The Battle for Wesnoth  1.17.0-dev
lexical_cast.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2021
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 #ifdef __FreeBSD__
46 #define __LONG_LONG_SUPPORTED
47 #endif
48 
49 #include <optional>
50 
51 #include <cstdlib>
52 #include <limits>
53 #include <string>
54 #include <sstream>
55 #include <type_traits>
56 
57 #define DEBUG_THROW(id)
58 #endif
59 
60 /**
61  * @namespace implementation
62  * Contains the implementation details for lexical_cast and shouldn't be used
63  * directly.
64  */
65 namespace implementation {
66 
67  template<
68  typename To
69  , typename From
70  , typename ToEnable = void
71  , typename FromEnable = void
72  >
74 
75 } // namespace implementation
76 
77 /**
78  * Lexical cast converts one type to another.
79  *
80  * @tparam To The type to convert to.
81  * @tparam From The type to convert from.
82  *
83  * @param value The value to convert.
84  *
85  * @returns The converted value.
86  *
87  * @throw bad_lexical_cast if the cast was unsuccessful.
88  */
89 template<typename To, typename From>
90 inline To lexical_cast(From value)
91 {
92  return implementation::lexical_caster<To, From>().operator()(value, std::nullopt);
93 }
94 
95 /**
96  * Lexical cast converts one type to another with a fallback.
97  *
98  * @tparam To The type to convert to.
99  * @tparam From The type to convert from.
100  *
101  * @param value The value to convert.
102  * @param fallback The fallback value to return if the cast fails.
103  *
104  * @returns The converted value.
105  */
106 template<typename To, typename From>
107 inline To lexical_cast_default(From value, To fallback = To())
108 {
109  return implementation::lexical_caster<To, From>().operator()(value, fallback);
110 }
111 
112 /** Thrown when a lexical_cast fails. */
113 struct bad_lexical_cast : std::exception
114 {
115  const char* what() const noexcept
116  {
117  return "bad_lexical_cast";
118  }
119 };
120 
121 namespace implementation {
122 
123 /**
124  * Base class for the conversion.
125  *
126  * Since functions can't be partially specialized we use a class, which can be
127  * partially specialized for the conversion.
128  *
129  * @tparam To The type to convert to.
130  * @tparam From The type to convert from.
131  * @tparam ToEnable Filter to enable the To type.
132  * @tparam FromEnable Filter to enable the From type.
133  */
134 template<
135  typename To
136  , typename From
137  , typename ToEnable
138  , typename FromEnable
139 >
140 struct lexical_caster
141 {
142  To operator()(From value, std::optional<To> fallback) const
143  {
144  DEBUG_THROW("generic");
145 
146  To result = To();
147  std::stringstream sstr;
148 
149  if(!(sstr << value && sstr >> result)) {
150  if(fallback) { return *fallback; }
151 
152  throw bad_lexical_cast();
153  } else {
154  return result;
155  }
156  }
157 };
158 
159 /**
160  * Specialized conversion class.
161  *
162  * Specialized for returning strings from an integral type or a pointer to an
163  * integral type.
164  */
165 template <typename From>
167  std::string
168  , From
169  , void
170  , std::enable_if_t<std::is_integral_v<std::remove_pointer_t<From>>>
171 >
172 {
173  std::string operator()(From value, std::optional<std::string>) const
174  {
175  DEBUG_THROW("specialized - To std::string - From integral (pointer)");
176 
177  std::stringstream sstr;
178  sstr << value;
179  return sstr.str();
180  }
181 };
182 
183 /**
184  * Specialized conversion class.
185  *
186  * Specialized for returning a long long from a (const) char*.
187  * @note is separate from the other signed types since a long long has a
188  * performance penalty at 32 bit systems.
189  */
190 template <class From>
192  long long
193  , From
194  , void
195  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
196  >
197 {
198  long long operator()(From value, std::optional<long long> fallback) const
199  {
200  DEBUG_THROW("specialized - To long long - From (const) char*");
201 
202  if(fallback) {
203  return lexical_cast_default<long long>(std::string(value), *fallback);
204  } else {
205  return lexical_cast<long long>(std::string(value));
206  }
207  }
208 };
209 
210 /**
211  * Specialized conversion class.
212  *
213  * Specialized for returning a long long from a std::string.
214  * @note is separate from the other signed types since a long long has a
215  * performance penalty at 32 bit systems.
216  */
217 template <>
219  long long
220  , std::string
221  >
222 {
223  long long operator()(const std::string& value, std::optional<long long> fallback) const
224  {
225  DEBUG_THROW("specialized - To long long - From std::string");
226 
227  try {
228  return std::stoll(value);
229  } catch(const std::invalid_argument&) {
230  } catch(const std::out_of_range&) {
231  }
232 
233  if(fallback) {
234  return *fallback;
235  } else {
236  throw bad_lexical_cast();
237  }
238  }
239 };
240 
241 /**
242  * Specialized conversion class.
243  *
244  * Specialized for returning a signed type from a (const) char*.
245  */
246 template <class To, class From>
248  To
249  , From
250  , std::enable_if_t<std::is_integral_v<To> && std::is_signed_v<To> && !std::is_same_v<To, long long>>
251  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
252  >
253 {
254  To operator()(From value, std::optional<To> fallback) const
255  {
256  DEBUG_THROW("specialized - To signed - From (const) char*");
257 
258  if(fallback) {
259  return lexical_cast_default<To>(std::string(value), *fallback);
260  } else {
261  return lexical_cast<To>(std::string(value));
262  }
263  }
264 };
265 
266 /**
267  * Specialized conversion class.
268  *
269  * Specialized for returning a signed type from a std::string.
270  */
271 template <class To>
273  To
274  , std::string
275  , std::enable_if_t<std::is_integral_v<To> && std::is_signed_v<To> && !std::is_same_v<To, long long>>
276  >
277 {
278  To operator()(const std::string& value, std::optional<To> fallback) const
279  {
280  DEBUG_THROW("specialized - To signed - From std::string");
281 
282  try {
283  long res = std::stol(value);
284  if(std::numeric_limits<To>::lowest() <= res && std::numeric_limits<To>::max() >= res) {
285  return static_cast<To>(res);
286  }
287  } catch(const std::invalid_argument&) {
288  } catch(const std::out_of_range&) {
289  }
290 
291  if(fallback) {
292  return *fallback;
293  } else {
294  throw bad_lexical_cast();
295  }
296  }
297 };
298 
299 /**
300  * Specialized conversion class.
301  *
302  * Specialized for returning a floating point type from a (const) char*.
303  */
304 template <class To, class From>
306  To
307  , From
308  , std::enable_if_t<std::is_floating_point_v<To>>
309  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
310  >
311 {
312  To operator()(From value, std::optional<To> fallback) const
313  {
314  DEBUG_THROW("specialized - To floating point - From (const) char*");
315 
316  if(fallback) {
317  return lexical_cast_default<To>(std::string(value), *fallback);
318  } else {
319  return lexical_cast<To>(std::string(value));
320  }
321  }
322 };
323 
324 /**
325  * Specialized conversion class.
326  *
327  * Specialized for returning a floating point type from a std::string.
328  */
329 template <class To>
331  To
332  , std::string
333  , std::enable_if_t<std::is_floating_point_v<To>>
334  >
335 {
336  To operator()(const std::string& value, std::optional<To> fallback) const
337  {
338  DEBUG_THROW("specialized - To floating point - From std::string");
339 
340  // Explicitly reject hexadecimal values. Unit tests of the config class require that.
341  if(value.find_first_of("Xx") != std::string::npos) {
342  if(fallback) {
343  return *fallback;
344  } else {
345  throw bad_lexical_cast();
346  }
347  }
348 
349  try {
350  long double res = std::stold(value);
351  if((static_cast<long double>(std::numeric_limits<To>::lowest()) <= res) && (static_cast<long double>(std::numeric_limits<To>::max()) >= res)) {
352  return static_cast<To>(res);
353  }
354  } catch(const std::invalid_argument&) {
355  } catch(const std::out_of_range&) {
356  }
357 
358  if(fallback) {
359  return *fallback;
360  } else {
361  throw bad_lexical_cast();
362  }
363  }
364 };
365 
366 /**
367  * Specialized conversion class.
368  *
369  * Specialized for returning a unsigned long long from a (const) char*.
370  * @note is separate from the other unsigned types since a unsigned long long
371  * has a performance penalty at 32 bit systems.
372  */
373 template <class From>
375  unsigned long long
376  , From
377  , void
378  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
379  >
380 {
381  unsigned long long operator()(From value, std::optional<unsigned long long> fallback) const
382  {
383  DEBUG_THROW(
384  "specialized - To unsigned long long - From (const) char*");
385 
386  if(fallback) {
387  return lexical_cast_default<unsigned long long>(std::string(value), *fallback);
388  } else {
389  return lexical_cast<unsigned long long>(std::string(value));
390  }
391  }
392 };
393 
394 /**
395  * Specialized conversion class.
396  *
397  * Specialized for returning a unsigned long long from a std::string.
398  * @note is separate from the other unsigned types since a unsigned long long
399  * has a performance penalty at 32 bit systems.
400  */
401 template <>
403  unsigned long long
404  , std::string
405  >
406 {
407  unsigned long long operator()(const std::string& value, std::optional<unsigned long long> fallback) const
408  {
409  DEBUG_THROW("specialized - To unsigned long long - From std::string");
410 
411  try {
412  return std::stoull(value);
413  } catch(const std::invalid_argument&) {
414  } catch(const std::out_of_range&) {
415  }
416 
417  if(fallback) {
418  return *fallback;
419  } else {
420  throw bad_lexical_cast();
421  }
422  }
423 };
424 
425 /**
426  * Specialized conversion class.
427  *
428  * Specialized for returning a unsigned type from a (const) char*.
429  */
430 template <class To, class From>
432  To
433  , From
434  , std::enable_if_t<std::is_unsigned_v<To> && !std::is_same_v<To, unsigned long long>>
435  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
436  >
437 {
438  To operator()(From value, std::optional<To> fallback) const
439  {
440  DEBUG_THROW("specialized - To unsigned - From (const) char*");
441 
442  if(fallback) {
443  return lexical_cast_default<To>(std::string(value), *fallback);
444  } else {
445  return lexical_cast<To>(std::string(value));
446  }
447  }
448 };
449 
450 /**
451  * Specialized conversion class.
452  *
453  * Specialized for returning a unsigned type from a std::string.
454  */
455 template <class To>
457  To
458  , std::string
459  , std::enable_if_t<std::is_unsigned_v<To>>
460  >
461 {
462  To operator()(const std::string& value, std::optional<To> fallback) const
463  {
464  DEBUG_THROW("specialized - To unsigned - From std::string");
465 
466  try {
467  unsigned long res = std::stoul(value);
468  // No need to check the lower bound, it's zero for all unsigned types.
469  if(std::numeric_limits<To>::max() >= res) {
470  return static_cast<To>(res);
471  }
472  } catch(const std::invalid_argument&) {
473  } catch(const std::out_of_range&) {
474  }
475 
476  if(fallback) {
477  return *fallback;
478  } else {
479  throw bad_lexical_cast();
480  }
481  }
482 };
483 
484 /**
485  * Specialized conversion class.
486  *
487  * Specialized for returning a bool from a std::string.
488  * @note is specialized to silence C4804 from MSVC.
489  */
490 template <>
491 struct lexical_caster<bool, std::string>
492 {
493  bool operator()(const std::string& value, std::optional<bool>) const
494  {
495  DEBUG_THROW("specialized - To bool - From std::string");
496 
497  return value == "1";
498  }
499 };
500 
501 /**
502  * Specialized conversion class.
503  *
504  * Specialized for returning a bool from a (const) char*.
505  * @note is specialized to silence C4804 from MSVC.
506  */
507 template <class From>
509  bool
510  , From
511  , void
512  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
513  >
514 {
515  bool operator()(From value, std::optional<bool>) const
516  {
517  DEBUG_THROW("specialized - To bool - From (const) char*");
518 
519  return lexical_cast<bool>(std::string(value));
520  }
521 };
522 
523 } // namespace implementation
524 
525 #endif
unsigned long long operator()(const std::string &value, std::optional< unsigned long long > fallback) const
To lexical_cast_default(From value, To fallback=To())
Lexical cast converts one type to another with a fallback.
#define DEBUG_THROW(id)
Throws an exception for debugging.
STL namespace.
Base class for the conversion.
bool operator()(const std::string &value, std::optional< bool >) const
To lexical_cast(From value)
Lexical cast converts one type to another.
To operator()(From value, std::optional< To > fallback) const
long long operator()(const std::string &value, std::optional< long long > fallback) const
To operator()(const std::string &value, std::optional< To > fallback) const
const char * what() const noexcept
std::string operator()(From value, std::optional< std::string >) const
Thrown when a lexical_cast fails.
Contains the implementation details for lexical_cast and shouldn&#39;t be used directly.