parselink-old/include/parselink/utility/ctreflect.h

175 lines
5.6 KiB
C++

//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Utility
//
// Compile-time reflection for structures.
//
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
#ifndef parselink_ct_reflect_080596cd36d52770
#define parselink_ct_reflect_080596cd36d52770
#include <array>
#include <source_location>
#include <string_view>
#include <tuple>
namespace parselink {
namespace ct {
namespace detail {
// This allows us to reference a type instance without ever instantiating it.
template <typename T>
extern T const phantom;
// This type binds to anything, allowing us to sus out the number of data
// members in an aggregate.
struct any_type {
template <typename T>
constexpr operator T();
};
// clang-format off
// This template takes a wrapped pointer to a data member of a theoretical
// instance of that type, in order to retrieve the name at compile time.
// Examples:
// gcc:
// constexpr auto parselink::ct::detail::src_string() [with auto Member = const_ptr<int>{(& phantom<structure>.structure::field)}]
//
// clang:
// auto parselink::ct::detail::src_string() [Member = const_ptr<member_ptr>{&phantom.field}]
// clang-format on
template <auto Member>
constexpr auto src_string() {
return std::source_location::current().function_name();
}
template <char StartDelim, char EndDelim>
struct base_parse_traits {
static constexpr char start_delim = StartDelim;
static constexpr char end_delim = EndDelim;
};
#ifdef __clang__
struct parse : base_parse_traits<'.', '}'> {};
#elif defined(__GNUC__)
struct parse : base_parse_traits<':', ')'> {};
#endif
static constexpr std::size_t max_members = 8;
// TODO: Logarithmic approach
// This needs to be limited to max_members, otherwise clang will go off into
// the weeds.
template <typename T, typename... Members>
requires(sizeof...(Members) <= max_members)
consteval std::size_t count_members(Members&&... members) {
if constexpr (requires { T{any_type{}, members...}; } == false) {
return sizeof...(members);
} else if constexpr (sizeof...(members) <= max_members) {
return count_members<T>(members..., any_type{});
} else {
throw "Too many members / something went wrong!";
}
};
// This function takes an aggregate and attempts to bind it into a tuple.
// Unfortunately, without variadic packs for structured bindings, this will
// rely on some heavy boilerplate.
template <typename T, std::size_t MemberCount = count_members<T>()>
requires(MemberCount <= 8)
consteval decltype(auto) as_tuple(T&& t) {
if constexpr (MemberCount == 0) {
return std::tuple{};
} else if constexpr (MemberCount == 1) {
auto&& [a0] = t;
return std::tie(a0);
} else if constexpr (MemberCount == 2) {
auto&& [a0, a1] = t;
return std::tie(a0, a1);
} else if constexpr (MemberCount == 3) {
auto&& [a0, a1, a2] = t;
return std::tie(a0, a1, a2);
} else if constexpr (MemberCount == 4) {
auto&& [a0, a1, a2, a3] = t;
return std::tie(a0, a1, a2, a3);
} else if constexpr (MemberCount == 5) {
auto&& [a0, a1, a2, a3, a4] = t;
return std::tie(a0, a1, a2, a3, a4);
} else if constexpr (MemberCount == 6) {
auto&& [a0, a1, a2, a3, a4, a5] = t;
return std::tie(a0, a1, a2, a3, a4, a5);
} else if constexpr (MemberCount == 7) {
auto&& [a0, a1, a2, a3, a4, a5, a6] = t;
return std::tie(a0, a1, a2, a3, a4, a5, a6);
} else if constexpr (MemberCount == 8) {
auto&& [a0, a1, a2, a3, a4, a5, a6, a7] = t;
return std::tie(a0, a1, a2, a3, a4, a5, a6, a7);
} else {
throw "Too many members!";
}
}
template <class T>
struct const_ptr {
T const* ptr;
};
template <std::size_t I, typename T>
consteval decltype(auto) as_ptr(T&& t) noexcept {
auto&& member = std::get<I>(as_tuple(t));
using member_ptr = std::decay_t<decltype(member)>;
return const_ptr<member_ptr>(&member);
}
template <typename T, std::size_t I>
consteval decltype(auto) raw_member_name() noexcept {
return src_string<as_ptr<I>(phantom<T>)>();
}
template <typename T, std::size_t I>
consteval decltype(auto) get_member_name() noexcept {
std::string_view raw = raw_member_name<T, I>();
auto s = raw.rfind(parse::start_delim);
auto e = raw.rfind(parse::end_delim);
if (s == raw.npos || e == raw.npos) throw "name parsing is broken!";
return raw.substr(s + 1, e - (s + 1));
}
template <typename T, std::size_t... Is>
consteval decltype(auto) get_member_names(std::index_sequence<Is...>) noexcept {
return std::array{get_member_name<T, Is>()...};
}
} // namespace detail
template <typename T, std::size_t I>
consteval decltype(auto) member_name() noexcept {
std::string_view raw = detail::raw_member_name<T, I>();
auto s = raw.rfind(detail::parse::start_delim);
auto e = raw.rfind(detail::parse::end_delim);
if (s == raw.npos || e == raw.npos) throw "name_of is broken!";
return raw.substr(s + 1, e - (s + 1));
}
template <typename T>
consteval decltype(auto) member_names() noexcept {
return detail::get_member_names<T>(
std::make_index_sequence<detail::count_members<T>()>{});
}
} // namespace ct
} // namespace parselink
#endif // parselink_ct_reflect_080596cd36d52770