diff --git a/rx/include/rx/format.hpp b/rx/include/rx/format.hpp new file mode 100644 index 000000000..682999ce3 --- /dev/null +++ b/rx/include/rx/format.hpp @@ -0,0 +1,315 @@ +#pragma once +#include "refl.hpp" +#include +#include +#include +#include +#include +#include +#include + +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 constexpr operator T() { + info.size = sizeof(T); + info.align = alignof(T); + if constexpr (std::is_default_constructible_v>) { + info.format = + [](void *object, + std::format_context &ctx) -> std::format_context::iterator { + std::formatter formatter; + return formatter.format(*static_cast(object), ctx); + }; + } + + return {}; + } +}; + +template +std::size_t getFieldOffset(T StructT::*ptr) { + StructT queryStruct; + return std::bit_cast(&(queryStruct.*ptr)) - + std::bit_cast(&queryStruct); +} + +template struct StructRuntimeInfo { + std::unordered_map fieldInfo; + + template void registerField() { + fieldInfo[getFieldOffset(Field)] = getNameOf(); + } + + 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 infos; + + std::string_view getVariableName(void *pointer) { + if (auto it = infos.find(pointer); it != infos.end()) { + return it->second; + } + + return {}; + } +}; + +template struct UnwrapFieldInfo; + +template struct UnwrapFieldInfo { + using struct_type = StructT; + using field_type = T; +}; + +template auto &getStructStorage() { + static StructRuntimeInfo structStorage; + return structStorage; +} + +inline auto &getVariableStorage() { + static VariableRuntimeInfo storage; + return storage; +} + +template > +constexpr auto getConstStructInfo() { + auto genInfo = + []( + std::index_sequence) -> std::array { + std::array queries; + static_cast(StructT{queries[I]...}); + + auto result = std::array{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()); +} + +template constexpr auto getStructInfo() { + auto structInfo = getConstStructInfo(); + auto &runtimeInfo = getStructStorage(); + for (auto &field : structInfo) { + field.name = runtimeInfo.getFieldName(field.offset); + } + return structInfo; +} +} // namespace detail + +template void registerField() { + using Info = detail::UnwrapFieldInfo; + auto &storage = detail::getStructStorage(); + storage.template registerField(); +} + +template void registerVariable() { + auto &storage = detail::getVariableStorage(); + storage.infos[&Variable] = rx::getNameOf(); +} +} // namespace rx + +template + requires(std::is_standard_layout_v && std::is_class_v && + rx::fieldCount > 0) && + (!requires(T value) { std::begin(value) != std::end(value); }) +struct std::formatter { + 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()); + std::format_to(ctx.out(), "{{"); + + auto structInfo = rx::detail::getStructInfo(); + auto bytes = reinterpret_cast(&s); + for (std::size_t i = 0; i < rx::fieldCount; ++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 + requires(std::is_enum_v && rx::fieldCount > 0) +struct std::formatter { + 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::underlying_type_t value, + std::index_sequence) -> std::string { + std::string_view result; + ((value == I ? ((result = rx::getNameOf(I)>()), 0) : 0), + ...); + + if (!result.empty()) { + return std::string(result); + } + + return std::format("{}", value); + }; + + auto queryUnknownField = + []( + std::underlying_type_t value, + std::integral_constant, + std::integer_sequence) -> std::string { + std::string_view result; + if (value < 0) { + ((-value == I + Offset + ? ((result = rx::getNameOf(-(I + Offset))>()), 0) + : 0), + ...); + } else { + ((value == I + Offset + ? ((result = rx::getNameOf(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::make_integer_sequence{}); + } else if (underlying >= rx::fieldCount) { + fieldName = queryUnknownField( + underlying, std::integral_constant>{}, + std::make_integer_sequence{}); + } else { + fieldName = getFieldName(underlying, + std::make_index_sequence>()); + } + + if (fieldName[0] >= '0' && fieldName[0] <= '9') { + std::format_to(ctx.out(), "({}){}", rx::getNameOf(), fieldName); + } else { + std::format_to(ctx.out(), "{}::{}", rx::getNameOf(), fieldName); + } + + return ctx.out(); + } +}; + +template + requires requires(T value) { std::begin(value) != std::end(value); } +struct std::formatter { + 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 + requires(!std::is_same_v, char> && + !std::is_same_v, wchar_t> && + !std::is_same_v, char8_t> && + !std::is_same_v, char16_t> && + !std::is_same_v, char32_t> && + std::is_default_constructible_v>) +struct std::formatter { + 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(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(); \ + return true; \ + }()) + +#define RX_REGISTER_FIELD(x) \ + static auto RX_CONCAT(_RX_REGISTER_FIELD_, __LINE__) = ([] { \ + ::rx::registerField<&x>(); \ + return true; \ + }()) diff --git a/rx/include/rx/refl.hpp b/rx/include/rx/refl.hpp new file mode 100644 index 000000000..b2c38a86c --- /dev/null +++ b/rx/include/rx/refl.hpp @@ -0,0 +1,160 @@ +#pragma once + +#include +#include + +#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 constexpr operator T &&(); +}; + +template + requires std::is_class_v +constexpr auto calcFieldCount() { + + auto isValidFieldCount = [](std::index_sequence) { + return requires { StructT(((I, AnyStructFieldQuery{}))...); }; + }; + + if constexpr (isValidFieldCount(std::make_index_sequence())) { + return calcFieldCount(); + } else if constexpr (sizeof(StructT) <= N || LastValidCount > 0) { + return LastValidCount; + } else { + return calcFieldCount(); + } +} + +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 constexpr bool isField = false; + +template +constexpr bool isField = true; + +} // namespace detail + +template 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 + requires(detail::isField || + std::is_enum_v> || + std::is_pointer_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 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 + requires std::is_enum_v +constexpr auto calcFieldCount() { + + if constexpr (!requires { getNameOf()[0]; }) { + return N; + } else { + constexpr auto c = getNameOf()[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()[0]; }) { + return N; + } else if constexpr (c >= '0' && c <= '9') { + return N; + } else { + return calcFieldCount(); + } + } +} +} // namespace detail + +template +inline constexpr std::size_t fieldCount = detail::calcFieldCount(); +} // namespace rx