wesnoth/src/lexical_cast.hpp
Martin Hrubý (hrubymar10) 674fda85b7 Migrate links to https if available - Fwd c18537edc0
(cherry-picked from commit bc4d22dc72)
2018-10-07 03:23:36 +00:00

488 lines
12 KiB
C++

/*
Copyright (C) 2009 - 2018 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* New lexcical_cast header.
*
* For debugging you can include this header _in_ a namespace (to honor ODR)
* and have a set of functions that throws exceptions instead of doing the
* real job. This is done for the unit tests but should normally not be done.
*/
#ifdef LEXICAL_CAST_DEBUG
#undef LEXICAL_CAST_HPP_INCLUDED
#endif
#ifndef LEXICAL_CAST_HPP_INCLUDED
#define LEXICAL_CAST_HPP_INCLUDED
#ifdef LEXICAL_CAST_DEBUG
#undef DEBUG_THROW
/**
* Throws an exception for debugging.
*
* @param id The unique name to identify the function.
* @note this name is a user defined string and
* should not be modified once used!
*/
#define DEBUG_THROW(id) throw id;
#else
#ifdef __FreeBSD__
#define __LONG_LONG_SUPPORTED
#endif
#include "global.hpp"
#include <cstdlib>
#include <limits>
#include <string>
#include <sstream>
#include <type_traits>
#include <boost/mpl/set.hpp>
#include <boost/optional.hpp>
#define DEBUG_THROW(id)
#endif
/**
* @namespace implementation
* Contains the implementation details for lexical_cast and shouldn't be used
* directly.
*/
namespace implementation {
template<
typename To
, typename From
, typename ToEnable = void
, typename FromEnable = void
>
struct lexical_caster;
} // namespace implementation
/**
* Lexical cast converts one type to another.
*
* @tparam To The type to convert to.
* @tparam From The type to convert from.
*
* @param value The value to convert.
*
* @returns The converted value.
*
* @throw bad_lexical_cast if the cast was unsuccessful.
*/
template<typename To, typename From>
inline To lexical_cast(From value)
{
return implementation::lexical_caster<To, From>().operator()(value, boost::none);
}
/**
* Lexical cast converts one type to another with a fallback.
*
* @tparam To The type to convert to.
* @tparam From The type to convert from.
*
* @param value The value to convert.
* @param fallback The fallback value to return if the cast fails.
*
* @returns The converted value.
*/
template<typename To, typename From>
inline To lexical_cast_default(From value, To fallback = To())
{
return implementation::lexical_caster<To, From>().operator()(value, fallback);
}
/** Thrown when a lexical_cast fails. */
struct bad_lexical_cast : std::exception
{
const char* what() const noexcept
{
return "bad_lexical_cast";
}
};
namespace implementation {
/**
* Base class for the conversion.
*
* Since functions can't be partially specialized we use a class, which can be
* partially specialized for the conversion.
*
* @tparam To The type to convert to.
* @tparam From The type to convert from.
* @tparam ToEnable Filter to enable the To type.
* @tparam FromEnable Filter to enable the From type.
*/
template<
typename To
, typename From
, typename ToEnable
, typename FromEnable
>
struct lexical_caster
{
To operator()(From value, boost::optional<To> fallback) const
{
DEBUG_THROW("generic");
To result = To();
std::stringstream sstr;
if(!(sstr << value && sstr >> result)) {
if(fallback) { return fallback.get(); }
throw bad_lexical_cast();
} else {
return result;
}
}
};
/**
* Specialized conversion class.
*
* Specialized for returning strings from an integral type or a pointer to an
* integral type.
*/
template <typename From>
struct lexical_caster<
std::string
, From
, void
, std::enable_if_t<std::is_integral<std::remove_pointer_t<From>>::value>
>
{
std::string operator()(From value, boost::optional<std::string>) const
{
DEBUG_THROW("specialized - To std::string - From integral (pointer)");
std::stringstream sstr;
sstr << value;
return sstr.str();
}
};
/**
* Specialized conversion class.
*
* Specialized for returning a long long from a (const) char*.
* @note is separate from the other signed types since a long long has a
* performance penalty at 32 bit systems.
*/
template <class From>
struct lexical_caster<
long long
, From
, void
, std::enable_if_t<boost::mpl::has_key<boost::mpl::set<char*, const char*> , From>::value>
>
{
long long operator()(From value, boost::optional<long long> fallback) const
{
DEBUG_THROW("specialized - To long long - From (const) char*");
if(fallback) {
return lexical_cast_default<long long>(std::string(value), fallback.get());
} else {
return lexical_cast<long long>(std::string(value));
}
}
};
/**
* Specialized conversion class.
*
* Specialized for returning a long long from a std::string.
* @note is separate from the other signed types since a long long has a
* performance penalty at 32 bit systems.
*/
template <>
struct lexical_caster<
long long
, std::string
>
{
long long operator()(const std::string& value, boost::optional<long long> fallback) const
{
DEBUG_THROW("specialized - To long long - From std::string");
try {
return std::stoll(value);
} catch(const std::invalid_argument&) {
} catch(const std::out_of_range&) {
}
if(fallback) {
return fallback.get();
} else {
throw bad_lexical_cast();
}
}
};
/**
* Specialized conversion class.
*
* Specialized for returning a signed type from a (const) char*.
*/
template <class To, class From>
struct lexical_caster<
To
, From
, std::enable_if_t<std::is_integral<To>::value && std::is_signed<To>::value && !std::is_same<To, long long>::value>
, std::enable_if_t<boost::mpl::has_key<boost::mpl::set<char*, const char*> , From>::value>
>
{
To operator()(From value, boost::optional<To> fallback) const
{
DEBUG_THROW("specialized - To signed - From (const) char*");
if(fallback) {
return lexical_cast_default<To>(std::string(value), fallback.get());
} else {
return lexical_cast<To>(std::string(value));
}
}
};
/**
* Specialized conversion class.
*
* Specialized for returning a signed type from a std::string.
*/
template <class To>
struct lexical_caster<
To
, std::string
, std::enable_if_t<std::is_integral<To>::value && std::is_signed<To>::value && !std::is_same<To, long long>::value>
>
{
To operator()(const std::string& value, boost::optional<To> fallback) const
{
DEBUG_THROW("specialized - To signed - From std::string");
try {
long res = std::stol(value);
if(std::numeric_limits<To>::lowest() <= res && std::numeric_limits<To>::max() >= res) {
return static_cast<To>(res);
}
} catch(const std::invalid_argument&) {
} catch(const std::out_of_range&) {
}
if(fallback) {
return fallback.get();
} else {
throw bad_lexical_cast();
}
}
};
/**
* Specialized conversion class.
*
* Specialized for returning a floating point type from a (const) char*.
*/
template <class To, class From>
struct lexical_caster<
To
, From
, std::enable_if_t<std::is_floating_point<To>::value>
, std::enable_if_t<boost::mpl::has_key<boost::mpl::set<char*, const char*> , From>::value>
>
{
To operator()(From value, boost::optional<To> fallback) const
{
DEBUG_THROW("specialized - To floating point - From (const) char*");
if(fallback) {
return lexical_cast_default<To>(std::string(value), fallback.get());
} else {
return lexical_cast<To>(std::string(value));
}
}
};
/**
* Specialized conversion class.
*
* Specialized for returning a floating point type from a std::string.
*/
template <class To>
struct lexical_caster<
To
, std::string
, std::enable_if_t<std::is_floating_point<To>::value>
>
{
To operator()(const std::string& value, boost::optional<To> fallback) const
{
DEBUG_THROW("specialized - To floating point - From std::string");
// Explicitly reject hexadecimal values. Unit tests of the config class require that.
if(value.find_first_of("Xx") != std::string::npos) {
if(fallback) {
return fallback.get();
} else {
throw bad_lexical_cast();
}
}
try {
long double res = std::stold(value);
if((static_cast<long double>(std::numeric_limits<To>::lowest()) <= res) && (static_cast<long double>(std::numeric_limits<To>::max()) >= res)) {
return static_cast<To>(res);
}
} catch(const std::invalid_argument&) {
} catch(const std::out_of_range&) {
}
if(fallback) {
return fallback.get();
} else {
throw bad_lexical_cast();
}
}
};
/**
* Specialized conversion class.
*
* Specialized for returning a unsigned long long from a (const) char*.
* @note is separate from the other unsigned types since a unsigned long long
* has a performance penalty at 32 bit systems.
*/
template <class From>
struct lexical_caster<
unsigned long long
, From
, void
, std::enable_if_t<boost::mpl::has_key<boost::mpl::set<char*, const char*> , From>::value>
>
{
unsigned long long operator()(From value, boost::optional<unsigned long long> fallback) const
{
DEBUG_THROW(
"specialized - To unsigned long long - From (const) char*");
if(fallback) {
return lexical_cast_default<unsigned long long>(std::string(value), fallback.get());
} else {
return lexical_cast<unsigned long long>(std::string(value));
}
}
};
/**
* Specialized conversion class.
*
* Specialized for returning a unsigned long long from a std::string.
* @note is separate from the other unsigned types since a unsigned long long
* has a performance penalty at 32 bit systems.
*/
template <>
struct lexical_caster<
unsigned long long
, std::string
>
{
unsigned long long operator()(const std::string& value, boost::optional<unsigned long long> fallback) const
{
DEBUG_THROW("specialized - To unsigned long long - From std::string");
try {
return std::stoull(value);
} catch(const std::invalid_argument&) {
} catch(const std::out_of_range&) {
}
if(fallback) {
return fallback.get();
} else {
throw bad_lexical_cast();
}
}
};
/**
* Specialized conversion class.
*
* Specialized for returning a unsigned type from a (const) char*.
*/
template <class To, class From>
struct lexical_caster<
To
, From
, std::enable_if_t<std::is_unsigned<To>::value && !std::is_same<To, unsigned long long>::value>
, std::enable_if_t<boost::mpl::has_key<boost::mpl::set<char*, const char*> , From>::value>
>
{
To operator()(From value, boost::optional<To> fallback) const
{
DEBUG_THROW("specialized - To unsigned - From (const) char*");
if(fallback) {
return lexical_cast_default<To>(std::string(value), fallback.get());
} else {
return lexical_cast<To>(std::string(value));
}
}
};
/**
* Specialized conversion class.
*
* Specialized for returning a unsigned type from a std::string.
*/
template <class To>
struct lexical_caster<
To
, std::string
, std::enable_if_t<std::is_unsigned<To>::value>
>
{
To operator()(const std::string& value, boost::optional<To> fallback) const
{
DEBUG_THROW("specialized - To unsigned - From std::string");
try {
unsigned long res = std::stoul(value);
// No need to check the lower bound, it's zero for all unsigned types.
if(std::numeric_limits<To>::max() >= res) {
return static_cast<To>(res);
}
} catch(const std::invalid_argument&) {
} catch(const std::out_of_range&) {
}
if(fallback) {
return fallback.get();
} else {
throw bad_lexical_cast();
}
}
};
} // namespace implementation
#endif