Skip to content

Instantly share code, notes, and snippets.

@kerrytazi
Created June 19, 2024 20:31
Show Gist options
  • Save kerrytazi/3686a6b57d8e0a84b52f77accb7048ec to your computer and use it in GitHub Desktop.
Save kerrytazi/3686a6b57d8e0a84b52f77accb7048ec to your computer and use it in GitHub Desktop.
#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