add relf and format utilities

This commit is contained in:
DH 2024-10-13 16:32:36 +03:00
parent 444fc1a387
commit 99b0140dc5
2 changed files with 475 additions and 0 deletions

315
rx/include/rx/format.hpp Normal file
View file

@ -0,0 +1,315 @@
#pragma once
#include "refl.hpp"
#include <array>
#include <format>
#include <optional>
#include <string_view>
#include <type_traits>
#include <unordered_map>
#include <utility>
namespace rx {
namespace detail {
struct StructFieldInfo {
std::size_t size = 0;
std::size_t align = 0;
std::size_t offset = 0;
std::format_context::iterator (*format)(void *,
std::format_context &ctx) = nullptr;
std::string_view name;
};
struct StructFieldQuery {
StructFieldInfo info;
template <typename T> constexpr operator T() {
info.size = sizeof(T);
info.align = alignof(T);
if constexpr (std::is_default_constructible_v<std::formatter<T>>) {
info.format =
[](void *object,
std::format_context &ctx) -> std::format_context::iterator {
std::formatter<T> formatter;
return formatter.format(*static_cast<T *>(object), ctx);
};
}
return {};
}
};
template <typename StructT, typename T>
std::size_t getFieldOffset(T StructT::*ptr) {
StructT queryStruct;
return std::bit_cast<std::byte *>(&(queryStruct.*ptr)) -
std::bit_cast<std::byte *>(&queryStruct);
}
template <typename> struct StructRuntimeInfo {
std::unordered_map<std::size_t, std::string_view> fieldInfo;
template <auto Field> void registerField() {
fieldInfo[getFieldOffset(Field)] = getNameOf<Field>();
}
std::string_view getFieldName(std::size_t offset) {
if (auto it = fieldInfo.find(offset); it != fieldInfo.end()) {
return it->second;
}
return {};
}
};
struct VariableRuntimeInfo {
std::unordered_map<void *, std::string_view> infos;
std::string_view getVariableName(void *pointer) {
if (auto it = infos.find(pointer); it != infos.end()) {
return it->second;
}
return {};
}
};
template <typename> struct UnwrapFieldInfo;
template <typename T, typename StructT> struct UnwrapFieldInfo<T StructT::*> {
using struct_type = StructT;
using field_type = T;
};
template <typename StructT> auto &getStructStorage() {
static StructRuntimeInfo<StructT> structStorage;
return structStorage;
}
inline auto &getVariableStorage() {
static VariableRuntimeInfo storage;
return storage;
}
template <typename StructT, std::size_t N = fieldCount<StructT>>
constexpr auto getConstStructInfo() {
auto genInfo =
[]<std::size_t... I>(
std::index_sequence<I...>) -> std::array<StructFieldInfo, N> {
std::array<StructFieldQuery, N> queries;
static_cast<void>(StructT{queries[I]...});
auto result = std::array<StructFieldInfo, N>{queries[I].info...};
std::size_t nextOffset = 0;
for (auto &elem : result) {
elem.offset = (nextOffset + (elem.align - 1)) & ~(elem.align - 1);
nextOffset = elem.offset + elem.size;
}
return result;
};
return genInfo(std::make_index_sequence<N>());
}
template <typename StructT> constexpr auto getStructInfo() {
auto structInfo = getConstStructInfo<StructT>();
auto &runtimeInfo = getStructStorage<StructT>();
for (auto &field : structInfo) {
field.name = runtimeInfo.getFieldName(field.offset);
}
return structInfo;
}
} // namespace detail
template <auto Field> void registerField() {
using Info = detail::UnwrapFieldInfo<decltype(Field)>;
auto &storage = detail::getStructStorage<typename Info::struct_type>();
storage.template registerField<Field>();
}
template <auto &&Variable> void registerVariable() {
auto &storage = detail::getVariableStorage();
storage.infos[&Variable] = rx::getNameOf<Variable>();
}
} // namespace rx
template <typename T>
requires(std::is_standard_layout_v<T> && std::is_class_v<T> &&
rx::fieldCount<T> > 0) &&
(!requires(T value) { std::begin(value) != std::end(value); })
struct std::formatter<T> {
constexpr std::format_parse_context::iterator
parse(std::format_parse_context &ctx) {
return ctx.begin();
}
std::format_context::iterator format(T &s, std::format_context &ctx) const {
std::format_to(ctx.out(), "{}", rx::getNameOf<T>());
std::format_to(ctx.out(), "{{");
auto structInfo = rx::detail::getStructInfo<T>();
auto bytes = reinterpret_cast<std::byte *>(&s);
for (std::size_t i = 0; i < rx::fieldCount<T>; ++i) {
if (i != 0) {
std::format_to(ctx.out(), ", ");
}
if (!structInfo[i].name.empty()) {
std::format_to(ctx.out(), ".{} = ", structInfo[i].name);
}
structInfo[i].format(bytes + structInfo[i].offset, ctx);
}
std::format_to(ctx.out(), "}}");
return ctx.out();
}
};
template <typename T>
requires(std::is_enum_v<T> && rx::fieldCount<T> > 0)
struct std::formatter<T> {
constexpr std::format_parse_context::iterator
parse(std::format_parse_context &ctx) {
return ctx.begin();
}
std::format_context::iterator format(T value,
std::format_context &ctx) const {
auto getFieldName =
[]<std::size_t... I>(std::underlying_type_t<T> value,
std::index_sequence<I...>) -> std::string {
std::string_view result;
((value == I ? ((result = rx::getNameOf<static_cast<T>(I)>()), 0) : 0),
...);
if (!result.empty()) {
return std::string(result);
}
return std::format("{}", value);
};
auto queryUnknownField =
[]<std::int64_t Offset, std::int64_t... I>(
std::underlying_type_t<T> value,
std::integral_constant<std::int64_t, Offset>,
std::integer_sequence<std::int64_t, I...>) -> std::string {
std::string_view result;
if (value < 0) {
((-value == I + Offset
? ((result = rx::getNameOf<static_cast<T>(-(I + Offset))>()), 0)
: 0),
...);
} else {
((value == I + Offset
? ((result = rx::getNameOf<static_cast<T>(I + Offset)>()), 0)
: 0),
...);
}
if (!result.empty()) {
return std::string(result);
}
return std::format("{}", value);
};
std::string fieldName;
auto underlying = std::to_underlying(value);
if (underlying < 0) {
fieldName = queryUnknownField(
underlying, std::integral_constant<std::int64_t, 0>{},
std::make_integer_sequence<std::int64_t, 128>{});
} else if (underlying >= rx::fieldCount<T>) {
fieldName = queryUnknownField(
underlying, std::integral_constant<std::int64_t, rx::fieldCount<T>>{},
std::make_integer_sequence<std::int64_t, 128>{});
} else {
fieldName = getFieldName(underlying,
std::make_index_sequence<rx::fieldCount<T>>());
}
if (fieldName[0] >= '0' && fieldName[0] <= '9') {
std::format_to(ctx.out(), "({}){}", rx::getNameOf<T>(), fieldName);
} else {
std::format_to(ctx.out(), "{}::{}", rx::getNameOf<T>(), fieldName);
}
return ctx.out();
}
};
template <typename T>
requires requires(T value) { std::begin(value) != std::end(value); }
struct std::formatter<T> {
constexpr std::format_parse_context::iterator
parse(std::format_parse_context &ctx) {
return ctx.begin();
}
std::format_context::iterator format(T &s, std::format_context &ctx) const {
std::format_to(ctx.out(), "[");
for (bool first = true; auto &elem : s) {
if (first) {
first = false;
} else {
std::format_to(ctx.out(), ", ");
}
std::format_to(ctx.out(), "{}", elem);
}
std::format_to(ctx.out(), "]");
return ctx.out();
}
};
template <typename T>
requires(!std::is_same_v<std::remove_cv_t<T>, char> &&
!std::is_same_v<std::remove_cv_t<T>, wchar_t> &&
!std::is_same_v<std::remove_cv_t<T>, char8_t> &&
!std::is_same_v<std::remove_cv_t<T>, char16_t> &&
!std::is_same_v<std::remove_cv_t<T>, char32_t> &&
std::is_default_constructible_v<std::formatter<T>>)
struct std::formatter<T *> {
constexpr std::format_parse_context::iterator
parse(std::format_parse_context &ctx) {
return ctx.begin();
}
std::format_context::iterator format(T *ptr, std::format_context &ctx) const {
auto name = rx::detail::getVariableStorage().getVariableName(ptr);
if (!name.empty()) {
std::format_to(ctx.out(), "*{} = ", name);
} else {
std::format_to(ctx.out(), "*");
}
if (ptr == nullptr) {
std::format_to(ctx.out(), "nullptr");
} else {
std::format_to(ctx.out(), "{}:{}", static_cast<void *>(ptr), *ptr);
}
return ctx.out();
}
};
#define RX_CONCAT(a, b) RX_CONCAT_IMPL(a, b)
#define RX_CONCAT_IMPL(a, b) a##b
#define RX_REGISTER_VARIABLE(x) \
static auto RX_CONCAT(_RX_REGISTER_VARIABLE_, __LINE__) = ([] { \
::rx::registerVariable<x>(); \
return true; \
}())
#define RX_REGISTER_FIELD(x) \
static auto RX_CONCAT(_RX_REGISTER_FIELD_, __LINE__) = ([] { \
::rx::registerField<&x>(); \
return true; \
}())

160
rx/include/rx/refl.hpp Normal file
View file

@ -0,0 +1,160 @@
#pragma once
#include <cstddef>
#include <string_view>
#ifdef _MSC_VER
#define RX_PRETTY_FUNCTION __FUNCSIG__
#elif defined(__GNUC__)
#define RX_PRETTY_FUNCTION __PRETTY_FUNCTION__
#else
#define RX_PRETTY_FUNCTION ""
#endif
namespace rx {
namespace detail {
struct AnyStructFieldQuery {
template <typename T> constexpr operator T &&();
};
template <typename StructT, std::size_t N = 1, std::size_t LastValidCount = 0>
requires std::is_class_v<StructT>
constexpr auto calcFieldCount() {
auto isValidFieldCount = []<std::size_t... I>(std::index_sequence<I...>) {
return requires { StructT(((I, AnyStructFieldQuery{}))...); };
};
if constexpr (isValidFieldCount(std::make_index_sequence<N>())) {
return calcFieldCount<StructT, N + 1, N>();
} else if constexpr (sizeof(StructT) <= N || LastValidCount > 0) {
return LastValidCount;
} else {
return calcFieldCount<StructT, N + 1, LastValidCount>();
}
}
consteval std::string_view unwrapName(std::string_view prefix,
std::string_view pretty,
bool dropNamespace) {
#ifdef _MSC_VER
if (auto pos = pretty.find(prefix); pos != std::string_view::npos) {
pretty.remove_prefix(pos + prefix.size());
} else {
pretty = {};
}
if (auto pos = pretty.rfind('>'); pos != std::string_view::npos) {
pretty.remove_suffix(pretty.size() - pos);
} else {
pretty = {};
}
if (auto pos = pretty.rfind(')'); pos != std::string_view::npos) {
pretty.remove_prefix(pos + 1);
}
if (auto pos = pretty.find(' '); pos != std::string_view::npos) {
pretty.remove_prefix(pos + 1);
}
#else
if (auto pos = pretty.rfind('['); pos != std::string_view::npos) {
pretty.remove_prefix(pos + 1);
} else {
pretty = {};
}
if (auto pos = pretty.rfind(prefix); pos != std::string_view::npos) {
pretty.remove_prefix(pos + prefix.size());
} else {
pretty = {};
}
if (auto pos = pretty.rfind(')'); pos != std::string_view::npos) {
pretty.remove_prefix(pos + 1);
}
if (pretty.ends_with(']')) {
pretty.remove_suffix(1);
} else {
pretty = {};
}
#endif
if (dropNamespace) {
if (auto pos = pretty.rfind(':'); pos != std::string_view::npos) {
pretty.remove_prefix(pos + 1);
}
}
return pretty;
}
template <typename> constexpr bool isField = false;
template <typename BaseT, typename TypeT>
constexpr bool isField<TypeT(BaseT::*)> = true;
} // namespace detail
template <auto &&V> consteval auto getNameOf() {
std::string_view prefix;
#ifdef _MSC_VER
prefix = "getNameOf<";
#else
prefix = "V = ";
#endif
return detail::unwrapName(prefix, RX_PRETTY_FUNCTION, true);
}
template <auto V>
requires(detail::isField<decltype(V)> ||
std::is_enum_v<std::remove_cvref_t<decltype(V)>> ||
std::is_pointer_v<std::remove_cvref_t<decltype(V)>>)
consteval auto getNameOf() {
std::string_view prefix;
#ifdef _MSC_VER
prefix = "getNameOf<";
#else
prefix = "V = ";
#endif
return detail::unwrapName(prefix, RX_PRETTY_FUNCTION, true);
}
template <typename T> consteval auto getNameOf() {
std::string_view prefix;
#ifdef _MSC_VER
prefix = "getNameOf<";
#else
prefix = "T = ";
#endif
return detail::unwrapName(prefix, RX_PRETTY_FUNCTION, false);
}
namespace detail {
template <typename EnumT, std::size_t N = 0>
requires std::is_enum_v<EnumT>
constexpr auto calcFieldCount() {
if constexpr (!requires { getNameOf<EnumT(N)>()[0]; }) {
return N;
} else {
constexpr auto c = getNameOf<EnumT(N)>()[0];
if constexpr (requires { EnumT::Count; }) {
return EnumT::Count;
} else if constexpr (requires { EnumT::_count; }) {
return EnumT::_count;
} else if constexpr (requires { EnumT::count; }) {
return EnumT::count;
} else if constexpr (!requires { getNameOf<EnumT(N)>()[0]; }) {
return N;
} else if constexpr (c >= '0' && c <= '9') {
return N;
} else {
return calcFieldCount<EnumT, N + 1>();
}
}
}
} // namespace detail
template <typename StructT>
inline constexpr std::size_t fieldCount = detail::calcFieldCount<StructT>();
} // namespace rx