/* * 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 2019, Danny Robson */ #pragma once #include "fwd.hpp" #include "../view.hpp" #include "../introspection.hpp" #include "../log.hpp" #include namespace cruft::parse::enumeration { namespace detail { struct lookup_base { virtual ~lookup_base () = default; virtual i08 as_i08 (cruft::view &) const& = 0; virtual i16 as_i16 (cruft::view &) const& = 0; virtual i32 as_i32 (cruft::view &) const& = 0; virtual i64 as_i64 (cruft::view &) const& = 0; virtual u08 as_u08 (cruft::view &) const& = 0; virtual u16 as_u16 (cruft::view &) const& = 0; virtual u32 as_u32 (cruft::view &) const& = 0; virtual u64 as_u64 (cruft::view &) const& = 0; }; template struct lookup_concrete : public lookup_base { static_assert (std::is_enum_v); using underlying_type = std::underlying_type_t; using cache_type = std::map; lookup_concrete (cache_type _cache) : m_cache (std::move (_cache)) { ; } template ValueT get (cruft::view &str) const& { if constexpr (std::is_same_v) { for (auto const [key,val]: m_cache) { if (equal (key, str)) { str = str.consume (key.size ()); return val; } } throw std::invalid_argument ("Unknown enum value"); } else { throw std::runtime_error ("Invalid underlying type"); } } i08 as_i08 (cruft::view &str) const& override { return get (str); } i16 as_i16 (cruft::view &str) const& override { return get (str); } i32 as_i32 (cruft::view &str) const& override { return get (str); } i64 as_i64 (cruft::view &str) const& override { return get (str); } u08 as_u08 (cruft::view &str) const& override { return get (str); } u16 as_u16 (cruft::view &str) const& override { return get (str); } u32 as_u32 (cruft::view &str) const& override { return get (str); } u64 as_u64 (cruft::view &str) const& override { return get (str); } private: cache_type m_cache; }; std::map>& cache (void); template struct underlying_else_identity { using type = EnumT; }; template struct underlying_else_identity< EnumT, std::enable_if_t> > { using type = std::underlying_type_t; }; }; template cookie setup [[nodiscard]] (std::map mapping) { auto &cache = detail::cache (); auto const index = cruft::typeidx (); auto lookup = std::make_unique> (std::move (mapping)); auto [pos, success] = cache.insert ({ index, std::move (lookup) }); if (!success) LOG_WARN ("duplicate parse setup for %! was ignored", cruft::type_name ()); return cookie {}; }; template EnumT value (int const idx, cruft::view &str) { auto const ® = detail::cache (); auto const pos = reg.find (idx); if (pos == reg.cend ()) throw std::invalid_argument ("Unknown enumeration"); auto const &obj = *pos->second; static_assert ( sizeof (EnumT) == 1 || sizeof (EnumT) == 2 || sizeof (EnumT) == 4 || sizeof (EnumT) == 8 ); using underlying_type = typename detail::underlying_else_identity::type; static_assert (std::is_integral_v); if constexpr (std::is_signed_v) { if constexpr (sizeof (EnumT) == 1) return static_cast (obj.as_i08 (str)); if constexpr (sizeof (EnumT) == 2) return static_cast (obj.as_i16 (str)); if constexpr (sizeof (EnumT) == 4) return static_cast (obj.as_i32 (str)); if constexpr (sizeof (EnumT) == 8) return static_cast (obj.as_i64 (str)); unreachable (); } else { if constexpr (sizeof (EnumT) == 1) return static_cast (obj.as_u08 (str)); if constexpr (sizeof (EnumT) == 2) return static_cast (obj.as_u16 (str)); if constexpr (sizeof (EnumT) == 4) return static_cast (obj.as_u32 (str)); if constexpr (sizeof (EnumT) == 8) return static_cast (obj.as_u64 (str)); unreachable (); } } template EnumT value (cruft::view &str) { return value (typeidx (), str); } template EnumT from_string (int const idx, cruft::view src) { auto const res = value (idx, src); if (!src.empty ()) throw std::runtime_error ("Invalid conversion"); return res; } template EnumT from_string (cruft::view src) { return from_string (typeidx (), src); } }