The Battle for Wesnoth  1.15.9+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 "global.hpp"
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 } // namespace implementation
485 
486 #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.
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.