/* * 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, 2022 Danny Robson */ #include "value.hpp" #include #include #include #include #include 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 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 < typename ValueT, typename IteratorT, typename = std::enable_if_t< std::is_same_v< std::remove_cv_t, char > > > 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 < typename ValueT, typename IteratorT, typename = std::enable_if_t< std::is_same_v< std::remove_cv_t, char > > > 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_ITERATOR(ITERATOR,PREFIX, KLASS)\ template <> \ KLASS \ cruft::parse::value ( \ cruft::view &str \ ) { \ return c_ ## PREFIX ## parse (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 (cruft::view &str) { \ auto remain = str; \ auto res = value (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) /////////////////////////////////////////////////////////////////////////////// template <> bool cruft::parse::value (cruft::view &str) { static constexpr struct { char const *str; bool res; } VALUES[] = { { "yes", true }, { "y", true }, { "true",true }, { "t", true }, { "no", false, }, { "n", false, }, { "false", false, }, { "f", false, }, }; for (auto [key,val]: VALUES) { if (equal (key, str)) { str = str.consume (strlen (key)); return val; } } throw std::invalid_argument ("invalid boolean string"); } /////////////////////////////////////////////////////////////////////////////// template <> cruft::tribool cruft::parse::value (cruft::view< char const*> &str) { return tribool (parse::value (str)); }