libcruft-util/typeidx.hpp

180 lines
5.4 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-2019 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "introspection/name.hpp"
#include <atomic>
#include <variant>
#include <vector>
#include <string_view>
#include <cassert>
namespace cruft {
namespace detail {
template <typename TagT>
int typeidx_next (void)
{
if constexpr (std::is_same_v<TagT, void>) {
// This must match the highest offset of the default set of names.
static std::atomic<int> counter = 17;
return ++counter;
} else {
static std::atomic<int> counter = 0;
return ++counter;
}
}
template <typename TagT>
std::vector<std::string_view>&
name (void)
{
if constexpr (std::is_same_v<TagT, void>) {
// This list is a propulated form of the specialisations for
// index queries that appears a little below. The order of this
// list _must_ be _exactly_ the same in both locations so that
// the indexes match.
static std::vector<std::string_view> s_names ({
"",
"void",
"nullptr_t",
"bool",
"char",
"signed char",
"unsigned char",
"signed short",
"unsigned short",
"signed int",
"unsigned int",
"signed long",
"unsigned long",
"signed long long",
"unsigned long long",
"float",
"double",
"long double",
});
return s_names;
} else {
static std::vector<std::string_view> s_names (1, "");
return s_names;
}
}
template <typename T, typename TagT = void>
struct queryidx {
static int get (void)
{
static auto id = detail::typeidx_next<TagT> ();
static bool s_done = false;
if (!s_done) {
auto &name = detail::name<TagT> ();
assert (name.size () == std::size_t (id));
name.resize (name.size () + 1);
name[id] = cruft::introspection::name::full<T> ();
s_done = true;
}
return id;
}
};
#define SPECIALISE(KLASS, IDX) \
template <> \
struct queryidx<KLASS, void> { \
static consteval int get (void) { \
return IDX; \
} \
};
SPECIALISE(void, 1)
SPECIALISE(std::nullptr_t, 2)
SPECIALISE(bool, 3)
SPECIALISE(char, 4)
SPECIALISE(signed char, 5)
SPECIALISE(unsigned char, 6)
SPECIALISE(signed short, 7)
SPECIALISE(unsigned short, 8)
SPECIALISE(signed int, 9)
SPECIALISE(unsigned int, 10)
SPECIALISE(signed long, 11)
SPECIALISE(unsigned long, 12)
SPECIALISE(signed long long, 13)
SPECIALISE(unsigned long long, 14)
SPECIALISE(float, 15)
SPECIALISE(double, 16)
SPECIALISE(long double, 17)
#undef SPECIALISE
}
/// Return a globally unique runtime ID for a given type (namespaced by a
/// tag type).
///
/// This is intended to be used as a lightweight type check for variants
/// and such without requiring RTTI.
///
/// The identifier is constructed at runtime and is not guaranteed to be
/// stable across executions (particularly in the case of threads and
/// other non-determinism). However it _is_ threadsafe to call this.
///
/// The value 0 will never be returned from this function and can be used
/// to indicate an 'unknown' state.
///
/// The range of identifiers is _probably_ contiguous. This should not
/// be relied upon for correctness, but may be used for performance
/// concerns.
///
/// \tparam T The type to register-and-retrieve the ID for
/// \tparam TagT A namespacing type; different tags will have different
/// sets of IDs. The default Tag is void.
/// \return The unique ID of the type
template <typename T, typename TagT = void>
constexpr int
typeidx (void)
{
return detail::queryidx<T,TagT>::get ();
}
template <typename TagT = void>
std::string_view
typeidx_name (int idx)
{
return detail::name<TagT> ()[idx];
}
/// Returns the typeidx of the contained value within a variant.
///
/// May throw std::bad_variant_access if the variant is
/// valueless_by_exception.
template <typename TagT=void, typename ...ComponentT>
int
value_typeidx (std::variant<ComponentT...> const &val)
{
return std::visit (
[] <typename ValueT> (ValueT const &) {
return typeidx<ValueT> ();
},
val
);
}
}