#ifdef CHILON_LEXICAL_CAST_HPP
#ifndef CHILON_DETAIL_LEXICAL_CAST_HPP
#define CHILON_DETAIL_LEXICAL_CAST_HPP
#include <string>
#include <typeinfo>
#include <chilon/iterator_range.hpp>
#include <chilon/meta/identity.hpp>
#include <chilon/meta/return.hpp>
#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 <boost/range/iterator_range.hpp>
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 <class T>
struct get_type< chilon::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 string_op {
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 string_op<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 string_to_number : private string_op<String> {
typedef typename string_op<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 & (CHILON_CAST_ROUND | CHILON_CAST_FLOOR | CHILON_CAST_CEILING)) && *ptr == '.') {
++ptr;
int mantissa1 = *ptr - '0';
if (mantissa1 < 0 || mantissa1 > 9) throw bad_lexical_cast();
if (options & CHILON_CAST_FLOOR)
return ret;
else if (options & CHILON_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 & CHILON_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 & CHILON_CAST_NO_PLUS)) {
if (*ptr == '+') {
++ptr;
}
}
ret = parseNumberPart<false>(src, ptr);
}
return ret;
}
};
template <class TypeId, class Target, class Source, int options>
struct lexical_cast_helper {
static Target exec(Source src) {
return src;
}
};
template <class Source, class Target, int options>
struct lexical_cast_helper<string_type, Target, Source, options> {
typedef typename boost::mpl::if_<
is_arithmetic<Target>,
string_to_number<Target, Source, options>,
meta::identity >::type type;
static auto exec(Source&& src) CHILON_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 lexical_cast {
typedef typename boost::call_traits<
typename make_pointer_const<Source>::type >::param_type param_type;
typedef typename get_type<param_type>::type type;
static auto exec(Source&& src)
CHILON_RETURN(lexical_cast_helper<
type, Target, param_type, options>::exec(
std::forward<Source>(src)))
};
template <int options, class T>
struct lexical_cast<std::string, boost::iterator_range<T>, options> {
static std::string exec(boost::iterator_range<T> const& src) {
return std::string(src.begin(), src.end());
}
};
template <int options, class T>
struct lexical_cast<std::string, chilon::iterator_range<T>, options> {
static std::string exec(chilon::iterator_range<T> const& src) {
return std::string(src.begin(), src.end());
}
};
} }
#endif
#endif