libcruft-util/parse/value.cpp

155 lines
5.2 KiB
C++

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2017-2018 Danny Robson <danny@nerdcruft.net>
*/
#include "value.hpp"
#include "../cast.hpp"
#include <cruft/util/preprocessor.hpp>
#include <cstdlib>
#include <stdexcept>
using cruft::parse::value;
namespace {
///////////////////////////////////////////////////////////////////////////
/// A trait class that records the C string parsing function for a given
/// type.
///
/// Not all types will necessarily have a corresponding parsing function.
template <typename ValueT> struct parse_traits { };
//-----------------------------------------------------------------------------
template <> struct parse_traits<long> { static constexpr auto value = strtol; };
template <> struct parse_traits<long long> { static constexpr auto value = strtoll; };
template <> struct parse_traits<unsigned long> { static constexpr auto value = strtoul; };
template <> struct parse_traits<unsigned long long> { static constexpr auto value = strtoull; };
//-----------------------------------------------------------------------------
template <> struct parse_traits<float> { static constexpr auto value = strtof; };
template <> struct parse_traits<double> { static constexpr auto value = strtod; };
template <> struct parse_traits<long double> { static constexpr auto value = strtold; };
///////////////////////////////////////////////////////////////////////////
template <
typename ValueT,
typename IteratorT,
typename = std::enable_if_t<
std::is_same_v<
std::remove_cv_t<IteratorT>,
char
>
>
>
ValueT
c_iparse (cruft::view<IteratorT*> &data)
{
auto head = const_cast<char *> (data.begin ());
auto tail = head;
auto val = parse_traits<ValueT>::value (head, &tail, 0);
if (tail == head)
throw std::invalid_argument ("unable to parse integer");
data = data.consume (tail);
return val;
}
//-------------------------------------------------------------------------
template <
typename ValueT,
typename IteratorT,
typename = std::enable_if_t<
std::is_same_v<
std::remove_cv_t<IteratorT>,
char
>
>
>
ValueT
c_fparse (cruft::view<IteratorT*> &data)
{
auto head = const_cast<char *> (data.begin ());
auto tail = head;
auto val = parse_traits<ValueT>::value (head, &tail);
if (tail == head)
throw std::invalid_argument ("unable to parse float");
data = data.consume (tail);
return val;
}
}
///////////////////////////////////////////////////////////////////////////////
/// Specialise cruft::parse for the type KLASS and dispatch the actual parsing
/// to safe template wrappers around the C parsing functions.
#define C_PARSE_ITERATOR(ITERATOR,PREFIX, KLASS)\
template <> \
KLASS \
cruft::parse::value<KLASS> ( \
cruft::view<ITERATOR> &str \
) { \
return c_ ## PREFIX ## parse<KLASS> (str); \
}
#define C_PARSE(PREFIX,KLASS) \
C_PARSE_ITERATOR(char *, PREFIX, KLASS) \
C_PARSE_ITERATOR(char const *, PREFIX, KLASS)
//-----------------------------------------------------------------------------
MAP1(C_PARSE, i,
unsigned long,
unsigned long long,
long,
long long
)
//-----------------------------------------------------------------------------
MAP1(C_PARSE, f,
float,
double,
long double
)
///////////////////////////////////////////////////////////////////////////////
/// Specialise cruft::parse for the type FINAL and dispatch the actual parsing
/// to the safe template wrappers defined above, but use the type USED as an
/// intermediary type.
///
/// We need to parse using the type `USED` because there aren't C parsing
/// routines for all the fundamental types.
///
/// We check that the final result can safely fit within `FINAL` before
/// returning the value, else we raise an exception.
#define C_PARSE_WITH_CAST(FINAL, USED) \
template<> \
FINAL \
cruft::parse::value<FINAL> (cruft::view<char const*> &str) { \
auto remain = str; \
auto res = value<USED> (remain); \
if (res > std::numeric_limits<FINAL>::max ()) \
throw std::invalid_argument ("overflow during parse"); \
\
str = remain; \
return cruft::cast::narrow<FINAL> (res); \
}
C_PARSE_WITH_CAST(short, long)
C_PARSE_WITH_CAST(int, long)
C_PARSE_WITH_CAST(unsigned short, unsigned long)
C_PARSE_WITH_CAST(unsigned int, unsigned long)