The Battle for Wesnoth  1.17.10+dev
lexical_cast.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2022
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  >
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 >
136 struct lexical_caster
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  try {
224  return std::stoll(value);
225  } catch(const std::invalid_argument&) {
226  } catch(const std::out_of_range&) {
227  }
228 
229  if(fallback) {
230  return *fallback;
231  } else {
232  throw bad_lexical_cast();
233  }
234  }
235 };
236 
237 /**
238  * Specialized conversion class.
239  *
240  * Specialized for returning a signed type from a (const) char*.
241  */
242 template <class To, class From>
244  To
245  , From
246  , std::enable_if_t<std::is_integral_v<To> && std::is_signed_v<To> && !std::is_same_v<To, long long>>
247  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
248  >
249 {
250  To operator()(From value, std::optional<To> fallback) const
251  {
252  DEBUG_THROW("specialized - To signed - From (const) char*");
253 
254  if(fallback) {
255  return lexical_cast_default<To>(std::string(value), *fallback);
256  } else {
257  return lexical_cast<To>(std::string(value));
258  }
259  }
260 };
261 
262 /**
263  * Specialized conversion class.
264  *
265  * Specialized for returning a signed type from a std::string.
266  */
267 template <class To>
269  To
270  , std::string
271  , std::enable_if_t<std::is_integral_v<To> && std::is_signed_v<To> && !std::is_same_v<To, long long>>
272  >
273 {
274  To operator()(const std::string& value, std::optional<To> fallback) const
275  {
276  DEBUG_THROW("specialized - To signed - From std::string");
277 
278  try {
279  long res = std::stol(value);
280  if(std::numeric_limits<To>::lowest() <= res && std::numeric_limits<To>::max() >= res) {
281  return static_cast<To>(res);
282  }
283  } catch(const std::invalid_argument&) {
284  } catch(const std::out_of_range&) {
285  }
286 
287  if(fallback) {
288  return *fallback;
289  } else {
290  throw bad_lexical_cast();
291  }
292  }
293 };
294 
295 /**
296  * Specialized conversion class.
297  *
298  * Specialized for returning a floating point type from a (const) char*.
299  */
300 template <class To, class From>
302  To
303  , From
304  , std::enable_if_t<std::is_floating_point_v<To>>
305  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
306  >
307 {
308  To operator()(From value, std::optional<To> fallback) const
309  {
310  DEBUG_THROW("specialized - To floating point - From (const) char*");
311 
312  if(fallback) {
313  return lexical_cast_default<To>(std::string(value), *fallback);
314  } else {
315  return lexical_cast<To>(std::string(value));
316  }
317  }
318 };
319 
320 /**
321  * Specialized conversion class.
322  *
323  * Specialized for returning a floating point type from a std::string.
324  */
325 template <class To>
327  To
328  , std::string
329  , std::enable_if_t<std::is_floating_point_v<To>>
330  >
331 {
332  To operator()(const std::string& value, std::optional<To> fallback) const
333  {
334  DEBUG_THROW("specialized - To floating point - From std::string");
335 
336  // Explicitly reject hexadecimal values. Unit tests of the config class require that.
337  if(value.find_first_of("Xx") != std::string::npos) {
338  if(fallback) {
339  return *fallback;
340  } else {
341  throw bad_lexical_cast();
342  }
343  }
344 
345  try {
346  long double res = std::stold(value);
347  if((static_cast<long double>(std::numeric_limits<To>::lowest()) <= res) && (static_cast<long double>(std::numeric_limits<To>::max()) >= res)) {
348  return static_cast<To>(res);
349  }
350  } catch(const std::invalid_argument&) {
351  } catch(const std::out_of_range&) {
352  }
353 
354  if(fallback) {
355  return *fallback;
356  } else {
357  throw bad_lexical_cast();
358  }
359  }
360 };
361 
362 /**
363  * Specialized conversion class.
364  *
365  * Specialized for returning a unsigned long long from a (const) char*.
366  * @note is separate from the other unsigned types since a unsigned long long
367  * has a performance penalty at 32 bit systems.
368  */
369 template <class From>
371  unsigned long long
372  , From
373  , void
374  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
375  >
376 {
377  unsigned long long operator()(From value, std::optional<unsigned long long> fallback) const
378  {
379  DEBUG_THROW(
380  "specialized - To unsigned long long - From (const) char*");
381 
382  if(fallback) {
383  return lexical_cast_default<unsigned long long>(std::string(value), *fallback);
384  } else {
385  return lexical_cast<unsigned long long>(std::string(value));
386  }
387  }
388 };
389 
390 /**
391  * Specialized conversion class.
392  *
393  * Specialized for returning a unsigned long long from a std::string.
394  * @note is separate from the other unsigned types since a unsigned long long
395  * has a performance penalty at 32 bit systems.
396  */
397 template <>
399  unsigned long long
400  , std::string
401  >
402 {
403  unsigned long long operator()(const std::string& value, std::optional<unsigned long long> fallback) const
404  {
405  DEBUG_THROW("specialized - To unsigned long long - From std::string");
406 
407  try {
408  return std::stoull(value);
409  } catch(const std::invalid_argument&) {
410  } catch(const std::out_of_range&) {
411  }
412 
413  if(fallback) {
414  return *fallback;
415  } else {
416  throw bad_lexical_cast();
417  }
418  }
419 };
420 
421 /**
422  * Specialized conversion class.
423  *
424  * Specialized for returning a unsigned type from a (const) char*.
425  */
426 template <class To, class From>
428  To
429  , From
430  , std::enable_if_t<std::is_unsigned_v<To> && !std::is_same_v<To, unsigned long long>>
431  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
432  >
433 {
434  To operator()(From value, std::optional<To> fallback) const
435  {
436  DEBUG_THROW("specialized - To unsigned - From (const) char*");
437 
438  if(fallback) {
439  return lexical_cast_default<To>(std::string(value), *fallback);
440  } else {
441  return lexical_cast<To>(std::string(value));
442  }
443  }
444 };
445 
446 /**
447  * Specialized conversion class.
448  *
449  * Specialized for returning a unsigned type from a std::string.
450  */
451 template <class To>
453  To
454  , std::string
455  , std::enable_if_t<std::is_unsigned_v<To>>
456  >
457 {
458  To operator()(const std::string& value, std::optional<To> fallback) const
459  {
460  DEBUG_THROW("specialized - To unsigned - From std::string");
461 
462  try {
463  unsigned long res = std::stoul(value);
464  // No need to check the lower bound, it's zero for all unsigned types.
465  if(std::numeric_limits<To>::max() >= res) {
466  return static_cast<To>(res);
467  }
468  } catch(const std::invalid_argument&) {
469  } catch(const std::out_of_range&) {
470  }
471 
472  if(fallback) {
473  return *fallback;
474  } else {
475  throw bad_lexical_cast();
476  }
477  }
478 };
479 
480 /**
481  * Specialized conversion class.
482  *
483  * Specialized for returning a bool from a std::string.
484  * @note is specialized to silence C4804 from MSVC.
485  */
486 template <>
487 struct lexical_caster<bool, std::string>
488 {
489  bool operator()(const std::string& value, std::optional<bool>) const
490  {
491  DEBUG_THROW("specialized - To bool - From std::string");
492 
493  return value == "1";
494  }
495 };
496 
497 /**
498  * Specialized conversion class.
499  *
500  * Specialized for returning a bool from a (const) char*.
501  * @note is specialized to silence C4804 from MSVC.
502  */
503 template <class From>
505  bool
506  , From
507  , void
508  , std::enable_if_t<std::is_same_v<From, const char*> || std::is_same_v<From, char*>>
509  >
510 {
511  bool operator()(From value, std::optional<bool>) const
512  {
513  DEBUG_THROW("specialized - To bool - From (const) char*");
514 
515  return lexical_cast<bool>(std::string(value));
516  }
517 };
518 
519 } // namespace implementation
520 
521 #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.