#ifndef _CHILON_LEXICAL_CAST_
#define _CHILON_LEXICAL_CAST_
#include <exception>
#include <string>
#include <typeinfo>
#include <boost/mpl/if.hpp>
#include <boost/type_traits/is_signed.hpp>
#include <boost/type_traits/is_arithmetic.hpp>
#include <boost/type_traits/is_floating_point.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/call_traits.hpp>
#include <chilon/iterator_range.hpp>
#ifndef _CHILON_DEFAULT_CAST_OPTIONS_
#define _CHILON_DEFAULT_CAST_OPTIONS_ 0
#endif
namespace chilon {
struct bad_lexical_cast : std::exception {};
/// These cast options can be passed in via the optional second parameter of
/// chilon::lexical_cast in order to alter the cast procedure.
enum cast_options {
CAST_NO_PLUS = 1, ///< Don't allow + before positive numbers.
CAST_NO_NEGATIVE = 2, ///< Only accept positive numbers for signed types.
CAST_ROUND = 4, ///< In integer conversion allow and round floating point strings.
CAST_FLOOR = 8, ///< In integer conversion allow and floor floating point strings.
CAST_CEILING = 16, ///< In integer conversion allow and floor floating point strings.
CAST_NO_UPPER_CASE = 32, ///< Don't accept A-F for hexadecimal numbers or E for exponents.
CAST_NO_LOWER_CASE = 64, ///< Don't accept a-f for hexadecimal numbers or e for exponents.
};
}
namespace chilon {
namespace detail {
/// Our lexical cast consider boolean as seperate from the other numerical
/// types hence this overload of is_arithmetic
template <class T>
struct is_arithmetic {
enum { value = boost::is_arithmetic<T>::value };
};
template <>
struct is_arithmetic<bool> {
enum { value = false };
};
////////////////////////////////////////////////////////////////////////////////
/// This tag is used internally by chilon to represent types that should
/// be treated as strings.
struct string_type {};
/// This tag is used internally by chilon to represent types that should
/// be treated as numbers.
struct number_type {};
/// This tag is used internally by chilon to represent types that it does
/// not know what to do with.
struct unknown_type {};
/// This meta-program resolves a type to a tag.
/// @see string_type
/// @see number_type
/// @see unknown_type
template <class T>
struct get_type {
typedef typename boost::mpl::if_<
is_arithmetic<T>,
number_type,
unknown_type
>::type type;
};
template <class T>
struct get_type<T const * const> {
typedef string_type type;
};
template <class T>
struct get_type< boost::iterator_range<T> const &> {
typedef string_type type;
};
template <>
struct get_type<std::string const &> {
typedef string_type type;
};
////////////////////////////////////////////////////////////////////////////////
// helper
////////////////////////////////////////////////////////////////////////////////
/// This traits class provides convenience functions for generic C++ container
/// style strings.
template <class String>
struct StringOp {
typedef typename boost::remove_const<
typename boost::remove_reference<String>::type
>::type type;
typedef typename type::const_iterator iterator;
static bool atEnd(String str, iterator it) {
return it == str.end();
}
static bool empty(String str) {
return str.empty();
}
static iterator begin(String str) {
return str.begin();
}
};
/// This traits class provides convenience functions for pointer
/// style strings.
template <class String>
struct StringOp<String const * const> {
typedef char const * iterator;
static bool atEnd(char const * const str, char const * const it) {
return *it == '\0';
}
static bool empty(char const * const str) {
return *str == '\0';
}
static iterator begin(char const * str) {
return str;
}
};
/// This meta-program returns true for signed integrals and floating point numbers.
template <class T>
struct is_signed {
enum { value = boost::is_signed<T>::value || boost::is_floating_point<T>::value };
};
////////////////////////////////////////////////////////////////////////////////
// default case: try default conversion
////////////////////////////////////////////////////////////////////////////////
template <class Numeric, class String, int options>
struct StringToNumber : private StringOp<String> {
typedef typename StringOp<String>::iterator iterator;
template <bool negative>
static void parseMantissa(Numeric& ret, String src, iterator ptr) {
Numeric floatDivide = 0.1;
for (++ptr; ! atEnd(src, ptr); ++ptr) {
if (*ptr < '0' || *ptr > '9') throw bad_lexical_cast();
if (negative)
ret -= ((*ptr - '0') * floatDivide);
else
ret += ((*ptr - '0') * floatDivide);
floatDivide /= 10;
}
}
template <bool negative>
static Numeric parseNumberPart(String src, iterator ptr) {
if (*ptr < '0' || *ptr > '9') throw bad_lexical_cast();
Numeric ret = 0;
if (negative)
ret = -( *ptr - '0' );
else
ret = *ptr - '0';
for (++ptr; ! atEnd(src, ptr); ++ptr) {
if (*ptr < '0' || *ptr > '9') {
if (boost::is_floating_point<Numeric>::value && *ptr == '.') {
parseMantissa<negative>(ret, src, ptr);
break;
}
else if ((options & (CAST_ROUND | CAST_FLOOR | CAST_CEILING)) && *ptr == '.') {
++ptr;
int mantissa1 = *ptr - '0';
if (mantissa1 < 0 || mantissa1 > 9) throw bad_lexical_cast();
if (options & CAST_FLOOR)
return ret;
else if (options & CAST_CEILING)
return ret + 1;
else if (mantissa1 > 4)
return ret + 1;
else
return ret;
}
else throw bad_lexical_cast();
}
if (negative)
ret = ret * 10 - (*ptr - '0');
else
ret = ret * 10 + (*ptr - '0');
}
return ret;
}
inline static Numeric exec(String src) {
if (empty(src)) throw bad_lexical_cast();
iterator ptr = begin(src);
Numeric ret;
if (! (options & CAST_NO_NEGATIVE) &&
is_signed<Numeric>::value && *ptr == '-')
{
++ptr;
ret = parseNumberPart<true>(src, ptr);
}
else {
// accept a + at the begining of a positive integer?
if (! (options & CAST_NO_PLUS)) {
if (*ptr == '+') {
++ptr;
}
}
ret = parseNumberPart<false>(src, ptr);
}
return ret;
}
};
template <class Target, class Source>
struct Identity {
static Target exec(Source src) {
return src;
}
};
template <class TypeId, class Target, class Source, int options>
struct LexicalCastHelper {
static Target exec(Source src) {
return src;
}
};
template <class Source, class Target, int options>
struct LexicalCastHelper<string_type, Target, Source, options> {
static Target exec(Source src) {
typedef typename boost::mpl::if_<
is_arithmetic<Target>,
StringToNumber<Target, Source, options>,
Identity<Target, Source> >::type type;
return type::exec(src);
}
};
////////////////////////////////////////////////////////////////////////////////
/// This meta-program enforces a const pointer for the input type.
template <class T>
struct make_pointer_const {
typedef T type;
};
template <class T>
struct make_pointer_const<T *> {
typedef T const * type;
};
////////////////////////////////////////////////////////////////////////////////
/// This lexical cast helper forwards the lexical cast to the appropriate
/// helper based on whether the target/source are string/numeric.
template <class Target, class Source, int options>
struct LexicalCast {
typedef typename boost::call_traits<
typename make_pointer_const<Source>::type >::param_type param_type;
typedef typename get_type<param_type>::type type;
static Target exec(param_type src) {
return LexicalCastHelper<type, Target, param_type, options>::exec(src);
}
};
template <int options, class T>
struct LexicalCast<std::string, boost::iterator_range<T>, options> {
static std::string exec(boost::iterator_range<T> const& src) {
return std::string(src.begin(), src.end());
}
};
}
// the inline optimises g++ (4.2.3 at least)
/// Perform a lexical_cast similar to boost::lexical_cast, with several enhancements.
///
/// @detailed lexical_cast is similar to boost::lexical_cast but converts the types
/// directly in C++ for optimal speed rather than redirecting through a
/// stringstream type. This form of lexical_cast also has other improvements,
/// for example a type can be lexically transformed to the same type without
/// causing a compiler error, allowing this function to be used generically.
/// Additionally a second template argument can be used to pass in options.
/// @tparam Target The target type to cast to.
/// @tparam options cast_options to affect the casting procedure.
/// @see cast_options
/// @tparam Source Source type, automatically determined by the compiler.
/// @param src Input type to lexically cast based on first template argument.
template <class Target, class Source>
inline Target lexical_cast(Source const& src) {
return detail::LexicalCast<Target, Source, _CHILON_DEFAULT_CAST_OPTIONS_>::exec(src);
}
template <class Target, int options, class Source>
inline Target lexical_cast(Source const& src) {
return detail::LexicalCast<Target, Source, options>::exec(src);
}
}
#endif // end guard