133 lines
4.7 KiB
C++
133 lines
4.7 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 "parse.hpp"
|
|
|
|
#include "cast.hpp"
|
|
#include <cruft/util/preprocessor.hpp>
|
|
|
|
#include <cstdlib>
|
|
#include <stdexcept>
|
|
|
|
using cruft::parse;
|
|
|
|
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>
|
|
ValueT
|
|
c_iparse (cruft::view<char const*> &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>
|
|
ValueT
|
|
c_fparse (cruft::view<char const*> &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(PREFIX, KLASS) \
|
|
template <> \
|
|
KLASS \
|
|
cruft::parse<KLASS> ( \
|
|
cruft::view<const char *> &str \
|
|
) { \
|
|
return c_ ## PREFIX ## parse<KLASS> (str); \
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
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<FINAL> (cruft::view<char const*> &str) { \
|
|
auto remain = str; \
|
|
auto res = parse<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)
|