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