libcruft-util/parse/enum.hpp

176 lines
5.9 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 2019, Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "fwd.hpp"
#include "../view.hpp"
#include "../introspection.hpp"
#include "../log.hpp"
#include <map>
namespace cruft::parse::enumeration {
namespace detail {
struct lookup_base {
virtual ~lookup_base () = default;
virtual i08 as_i08 (cruft::view<char const*> &) const& = 0;
virtual i16 as_i16 (cruft::view<char const*> &) const& = 0;
virtual i32 as_i32 (cruft::view<char const*> &) const& = 0;
virtual i64 as_i64 (cruft::view<char const*> &) const& = 0;
virtual u08 as_u08 (cruft::view<char const*> &) const& = 0;
virtual u16 as_u16 (cruft::view<char const*> &) const& = 0;
virtual u32 as_u32 (cruft::view<char const*> &) const& = 0;
virtual u64 as_u64 (cruft::view<char const*> &) const& = 0;
};
template <typename EnumT>
struct lookup_concrete : public lookup_base {
static_assert (std::is_enum_v<EnumT>);
using underlying_type = std::underlying_type_t<EnumT>;
using cache_type = std::map<std::string_view, EnumT>;
lookup_concrete (cache_type _cache)
: m_cache (std::move (_cache))
{ ; }
template <typename ValueT>
ValueT
get (cruft::view<char const*> &str) const&
{
if constexpr (std::is_same_v<ValueT, underlying_type>) {
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<char const*> &str) const& override { return get<i08> (str); }
i16 as_i16 (cruft::view<char const*> &str) const& override { return get<i16> (str); }
i32 as_i32 (cruft::view<char const*> &str) const& override { return get<i32> (str); }
i64 as_i64 (cruft::view<char const*> &str) const& override { return get<i64> (str); }
u08 as_u08 (cruft::view<char const*> &str) const& override { return get<u08> (str); }
u16 as_u16 (cruft::view<char const*> &str) const& override { return get<u16> (str); }
u32 as_u32 (cruft::view<char const*> &str) const& override { return get<u32> (str); }
u64 as_u64 (cruft::view<char const*> &str) const& override { return get<u64> (str); }
private:
cache_type m_cache;
};
std::map<int, std::unique_ptr<lookup_base>>& cache (void);
template <typename EnumT, typename = void>
struct underlying_else_identity { using type = EnumT; };
template <typename EnumT>
struct underlying_else_identity<
EnumT,
std::enable_if_t<std::is_enum_v<EnumT>>
> { using type = std::underlying_type_t<EnumT>; };
};
template <typename EnumT>
cookie setup [[nodiscard]] (std::map<std::string_view, EnumT> mapping)
{
auto &cache = detail::cache ();
auto const index = cruft::typeidx<EnumT> ();
auto lookup = std::make_unique<detail::lookup_concrete<EnumT>> (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<EnumT> ());
return cookie {};
};
template <typename EnumT>
EnumT
value (int const idx, cruft::view<char const*> &str)
{
auto const &reg = 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<EnumT>::type;
static_assert (std::is_integral_v<underlying_type>);
if constexpr (std::is_signed_v<underlying_type>) {
if constexpr (sizeof (EnumT) == 1) return static_cast<EnumT> (obj.as_i08 (str));
if constexpr (sizeof (EnumT) == 2) return static_cast<EnumT> (obj.as_i16 (str));
if constexpr (sizeof (EnumT) == 4) return static_cast<EnumT> (obj.as_i32 (str));
if constexpr (sizeof (EnumT) == 8) return static_cast<EnumT> (obj.as_i64 (str));
unreachable ();
} else {
if constexpr (sizeof (EnumT) == 1) return static_cast<EnumT> (obj.as_u08 (str));
if constexpr (sizeof (EnumT) == 2) return static_cast<EnumT> (obj.as_u16 (str));
if constexpr (sizeof (EnumT) == 4) return static_cast<EnumT> (obj.as_u32 (str));
if constexpr (sizeof (EnumT) == 8) return static_cast<EnumT> (obj.as_u64 (str));
unreachable ();
}
}
template <typename EnumT>
EnumT
value (cruft::view<char const*> &str)
{
return value<EnumT> (typeidx<EnumT> (), str);
}
template <typename EnumT>
EnumT
from_string (int const idx, cruft::view<char const*> src)
{
auto const res = value<EnumT> (idx, src);
if (!src.empty ())
throw std::runtime_error ("Invalid conversion");
return res;
}
template <typename EnumT>
EnumT
from_string (cruft::view<char const*> src)
{
return from_string<EnumT> (typeidx<EnumT> (), src);
}
}