/* * 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 */ #include "parse.hpp" #include "cast.hpp" #include "preprocessor.hpp" #include #include 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 struct parse_traits { }; //----------------------------------------------------------------------------- template <> struct parse_traits { static constexpr auto value = strtol; }; template <> struct parse_traits { static constexpr auto value = strtoll; }; template <> struct parse_traits { static constexpr auto value = strtoul; }; template <> struct parse_traits { static constexpr auto value = strtoull; }; //----------------------------------------------------------------------------- template <> struct parse_traits { static constexpr auto value = strtof; }; template <> struct parse_traits { static constexpr auto value = strtod; }; template <> struct parse_traits { static constexpr auto value = strtold; }; /////////////////////////////////////////////////////////////////////////// template ValueT c_iparse (cruft::view &data) { auto head = const_cast (data.begin ()); auto tail = head; auto val = parse_traits::value (head, &tail, 0); if (tail == head) throw std::invalid_argument ("unable to parse integer"); data = data.consume (tail); return val; } //------------------------------------------------------------------------- template ValueT c_fparse (cruft::view &data) { auto head = const_cast (data.begin ()); auto tail = head; auto val = parse_traits::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 ( \ cruft::view &str \ ) { \ return c_ ## PREFIX ## parse (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 (cruft::view &str) { \ auto remain = str; \ auto res = parse (remain); \ if (res > std::numeric_limits::max ()) \ throw std::invalid_argument ("overflow during parse"); \ \ str = remain; \ return cruft::cast::narrow (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)