Created
June 19, 2024 20:31
-
-
Save kerrytazi/3686a6b57d8e0a84b52f77accb7048ec to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <cstddef> | |
#include <type_traits> | |
#include <tuple> | |
#include <string_view> | |
#include <span> | |
namespace rtti | |
{ | |
namespace internal | |
{ | |
[[nodiscard]] | |
static constexpr char const *strend(char const *s) | |
{ | |
while (*s) ++s; | |
return s; | |
} | |
[[nodiscard]] | |
static constexpr size_t strlen(char const *s) | |
{ | |
return strend(s) - s; | |
} | |
template <auto val> | |
struct static_wrapper_val | |
{ | |
static constexpr inline auto value = val; | |
}; | |
template <typename T> | |
struct static_wrapper | |
{ | |
static constexpr inline T value{}; | |
}; | |
template <size_t N> | |
struct const_str | |
{ | |
char _arr[N + 1]; | |
consteval const_str(char const *ptr) | |
{ | |
for (size_t i = 0; i < N; ++i) | |
_arr[i] = ptr[i]; | |
_arr[N] = '\0'; | |
} | |
[[nodiscard]] | |
consteval size_t length() const { return N; } | |
template <size_t N2> | |
[[nodiscard]] | |
consteval size_t find(const_str<N2> const &other) const | |
{ | |
if (length() < other.length()) | |
return (size_t)-1; | |
for (size_t i = 0; i < length() - other.length() + 1; ++i) | |
{ | |
bool found = true; | |
for (size_t j = 0; j < other.length(); ++j) | |
{ | |
if (_arr[i + j] != other._arr[j]) | |
{ | |
found = false; | |
break; | |
} | |
} | |
if (found) | |
return i; | |
} | |
return (size_t)-1; | |
} | |
template <size_t from, size_t to> | |
[[nodiscard]] | |
consteval auto substr() const | |
{ | |
return const_str<to - from>(_arr + from); | |
} | |
[[nodiscard]] | |
consteval char const *c_str() const { return _arr; } | |
}; | |
template <size_t N> | |
[[nodiscard]] | |
static consteval auto lit_const_str(char const (&ptr)[N]) | |
{ | |
return const_str<N - 1>(ptr); | |
} | |
template <typename T> | |
[[nodiscard]] | |
static consteval auto get_type_name() | |
{ | |
auto constexpr name = lit_const_str(__FUNCSIG__); | |
auto constexpr from_s = lit_const_str("get_type_name<"); | |
auto constexpr to_s = lit_const_str(">(void)"); | |
size_t constexpr from = name.find(from_s) + from_s.length(); | |
auto constexpr new_name = name.substr<from, name.length() - to_s.length()>(); | |
return new_name; | |
} | |
template <typename TType, auto TName> | |
struct field_info_t | |
{ | |
using type = TType; | |
decltype(TName) name = TName; | |
}; | |
template <auto ptr> | |
[[nodiscard]] | |
static consteval auto get_field_info() | |
{ | |
auto constexpr name = lit_const_str(__FUNCSIG__); | |
auto constexpr from_s = lit_const_str("&value->"); | |
auto constexpr to_s = lit_const_str(">(void)"); | |
size_t constexpr from = name.find(from_s) + from_s.length(); | |
auto constexpr new_name = name.substr<from, name.length() - to_s.length()>(); | |
return field_info_t<std::remove_cv_t<std::remove_pointer_t<decltype(ptr)>>, new_name>{}; | |
} | |
struct any | |
{ | |
any(std::size_t); | |
template <typename T> | |
constexpr operator T() const noexcept; | |
}; | |
template <typename T, size_t N> | |
[[nodiscard]] | |
static consteval bool constructible() | |
{ | |
auto const check = []<size_t... Is>(std::index_sequence<Is...>) { | |
return requires { T{ any(Is)... }; }; | |
}; | |
return check(std::make_index_sequence<N>()); | |
} | |
template <typename T, size_t N = 0> | |
[[nodiscard]] | |
static consteval size_t count_max_args_in_agg_init() | |
{ | |
static_assert(N <= sizeof(T), "what?"); | |
if constexpr (constructible<T, N>() && !constructible<T, N + 1>()) | |
return N; | |
else | |
return count_max_args_in_agg_init<T, N + 1>(); | |
} | |
template <typename T> | |
constexpr size_t num_fields = count_max_args_in_agg_init<T>(); | |
template <typename T> | |
[[nodiscard]] | |
static constexpr auto to_tuple(T const &s) | |
{ | |
#define DEFINE_TUPLE_CHECK(_N, ...) \ | |
if constexpr (num_fields<T> == _N) \ | |
{ \ | |
auto const &[__VA_ARGS__] = s; \ | |
return std::make_tuple(__VA_ARGS__); \ | |
} \ | |
else | |
DEFINE_TUPLE_CHECK(5, f0, f1, f2, f3, f4); | |
DEFINE_TUPLE_CHECK(4, f0, f1, f2, f3); | |
DEFINE_TUPLE_CHECK(3, f0, f1, f2); | |
DEFINE_TUPLE_CHECK(2, f0, f1); | |
DEFINE_TUPLE_CHECK(1, f0); | |
{ | |
return std::make_tuple(); | |
} | |
} | |
template <typename T, size_t N> | |
struct struct_view; | |
template <typename T> | |
struct struct_view<T, 0> | |
{ | |
[[nodiscard]] | |
static consteval auto view() | |
{ | |
return std::make_tuple(); | |
} | |
}; | |
#define DEFINE_STRUCT_VIEW(_N, ...) \ | |
template <typename T> \ | |
struct struct_view<T, _N> \ | |
{ \ | |
[[nodiscard]] \ | |
static consteval auto view() \ | |
{ \ | |
auto const &[__VA_ARGS__] = static_wrapper<T>::value; \ | |
auto const ref_tup = std::tie(__VA_ARGS__); \ | |
auto const get_ptrs = [](auto const &... refs) \ | |
{ \ | |
return std::make_tuple(&refs...); \ | |
}; \ | |
return std::apply(get_ptrs, ref_tup); \ | |
} \ | |
} | |
DEFINE_STRUCT_VIEW(1, f0); | |
DEFINE_STRUCT_VIEW(2, f0, f1); | |
DEFINE_STRUCT_VIEW(3, f0, f1, f2); | |
DEFINE_STRUCT_VIEW(4, f0, f1, f2, f3); | |
DEFINE_STRUCT_VIEW(5, f0, f1, f2, f3, f4); | |
#undef DEFINE_STRUCT_VIEW | |
template <typename T> | |
[[nodiscard]] | |
static consteval auto get_field_infos_tuple() | |
{ | |
if constexpr (std::is_class_v<T>) | |
{ | |
auto constexpr size = num_fields<T>; | |
auto constexpr view_tup = struct_view<T, size>::view(); | |
auto get_infos = [&]<size_t... Is>(std::index_sequence<Is...>) { | |
return std::make_tuple(get_field_info<std::get<Is>(view_tup)>()...); | |
}; | |
return get_infos(std::make_index_sequence<size>()); | |
} | |
else | |
{ | |
return std::make_tuple(); | |
} | |
} | |
template <typename T> | |
struct tag | |
{ | |
}; | |
template <auto T> | |
struct tag_v | |
{ | |
}; | |
template <auto... infos> | |
struct static_wrapper_val_fields_info; | |
} // namespace internal | |
class type_info; | |
class field_info; | |
class type_info | |
{ | |
public: | |
std::string_view name; | |
std::span<const field_info> fields; | |
bool is_class; | |
bool is_pointer; | |
bool is_reference; | |
bool is_const; | |
bool is_empty; | |
size_t byte_size; | |
protected: | |
type_info() = delete; | |
type_info(type_info const &) = delete; | |
type_info &operator=(type_info const &) = delete; | |
type_info(type_info &&) = delete; | |
type_info &operator=(type_info &&) = delete; | |
template <typename T> | |
consteval type_info(internal::tag<T>); | |
}; | |
class field_info | |
{ | |
public: | |
std::string_view name; | |
type_info const &type; | |
protected: | |
field_info() = delete; | |
field_info(field_info const &) = delete; | |
field_info &operator=(field_info const &) = delete; | |
field_info(field_info &&) = delete; | |
field_info &operator=(field_info &&) = delete; | |
template <auto info> | |
consteval field_info(internal::tag_v<info>); | |
template <auto... infos> | |
friend struct internal::static_wrapper_val_fields_info; | |
}; | |
namespace internal | |
{ | |
template <typename T> | |
struct _type_info : type_info | |
{ | |
consteval _type_info() | |
: type_info(tag<T>{}) | |
{ | |
} | |
}; | |
template <auto... infos> | |
struct static_wrapper_val_fields_info | |
{ | |
static constexpr inline field_info arr[sizeof...(infos)]{ field_info(tag_v<infos>{})... }; | |
}; | |
} // namespace internal | |
template <typename T> | |
[[nodiscard]] | |
static consteval type_info const &get_type_info() | |
{ | |
return internal::static_wrapper<internal::_type_info<T>>::value; | |
}; | |
template <typename T> | |
[[nodiscard]] | |
static consteval std::span<const field_info> get_fields_info() | |
{ | |
if constexpr (std::is_class_v<T> && !std::is_empty_v<T>) | |
{ | |
auto const cc = []<size_t... Is>(std::index_sequence<Is...>) -> std::span<const field_info>{ | |
constexpr auto t = internal::get_field_infos_tuple<T>(); | |
return internal::static_wrapper_val_fields_info<std::get<Is>(t)...>::arr; | |
}; | |
return cc(std::make_index_sequence<internal::num_fields<T>>()); | |
} | |
else | |
{ | |
return {}; | |
} | |
}; | |
template <typename T> | |
consteval type_info::type_info(internal::tag<T>) | |
: name{ internal::static_wrapper_val<internal::get_type_name<T>()>::value.c_str() } | |
, fields{ get_fields_info<T>() } | |
, is_class{ std::is_class_v<T> } | |
, is_pointer{ std::is_pointer_v<T> } | |
, is_reference{ std::is_reference_v<T> } | |
, is_const{ std::is_const_v<T> } | |
, is_empty{ std::is_empty_v<T> } | |
, byte_size{ sizeof(T) } | |
{ | |
} | |
template <auto info> | |
consteval field_info::field_info(internal::tag_v<info>) | |
: name{ internal::static_wrapper_val<info.name>::value.c_str() } | |
, type{ get_type_info<typename decltype(info)::type>() } | |
{ | |
} | |
} // namespace rtti | |
#if 0 // test | |
#include <iostream> | |
#include <string> | |
#include <sstream> | |
std::string struct_to_string(rtti::type_info const &struct_info, size_t tabs = 0) | |
{ | |
std::stringstream ss; | |
ss << struct_info.name << "\n"; | |
ss << std::string(tabs * 4, ' ') << "{\n"; | |
for (auto const &field_info : struct_info.fields) | |
{ | |
auto field_to_string = [&](auto const &field_info) { | |
if (field_info.type.is_class) | |
return struct_to_string(field_info.type, tabs + 1); | |
else | |
return std::string(field_info.type.name); | |
}; | |
ss | |
<< std::string(tabs * 4, ' ') | |
<< " " | |
<< field_to_string(field_info) | |
<< " " | |
<< field_info.name | |
<< ";\n"; | |
} | |
ss << std::string(tabs * 4, ' ') << "}"; | |
if (tabs == 0) | |
ss << "\n"; | |
return ss.str(); | |
} | |
int main() | |
{ | |
struct CTest | |
{ | |
struct CAnotherTest | |
{ | |
int my_field_int; | |
float another_field; | |
}; | |
int my_field_int; | |
CAnotherTest another_test; | |
float another_field; | |
char haha; | |
}; | |
auto constexpr const &info = rtti::get_type_info<CTest>(); | |
std::cout << struct_to_string(info) << "\n"; | |
return 0; | |
} | |
#endif // 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment