#pragma once #include "compat/integer-types.hpp" #include #include #include #include #include #include #include #include #include #if defined _MSC_VER #define FM_PRETTY_FUNCTION __FUNCSIG__ #else #define FM_PRETTY_FUNCTION __PRETTY_FUNCTION__ #endif namespace floormat::entities::detail { template static constexpr StringView type_name(); template struct type_name_helper; } namespace floormat { template constexpr inline StringView name_of = entities::detail::type_name_helper::name; } // namespace floormat namespace floormat::entities { template struct pass_by_value : std::bool_constant> {}; template<> struct pass_by_value : std::true_type {}; template struct pass_by_value && sizeof(T) <= sizeof(void*)>> : std::true_type {}; template constexpr inline bool pass_by_value_v = pass_by_value::value; template using const_qualified = std::conditional_t, T, const T&>; template using ref_qualified = std::conditional_t, T, T&>; template using move_qualified = std::conditional_t, T, T&&>; template concept FieldReader_memfn = requires(const T x, F f) { { (x.*f)() } -> std::convertible_to; }; template concept FieldReader_ptr = requires(const T x, F f) { { x.*f } -> std::convertible_to; }; template concept FieldReader_function = requires(const T x, F f) { { f(x) } -> std::convertible_to; }; template concept FieldReader = requires { requires FieldReader_memfn || FieldReader_ptr || FieldReader_function; }; template concept FieldWriter_memfn = requires(T x, move_qualified value, F f) { { (x.*f)(value) } -> std::same_as; }; template concept FieldWriter_ptr = requires(T x, move_qualified value, F f) { { x.*f = value }; }; template concept FieldWriter_function = requires(T x, move_qualified value, F f) { { f(x, value) } -> std::same_as; }; template concept FieldWriter = requires { requires FieldWriter_memfn || FieldWriter_ptr || FieldWriter_function; }; namespace detail { template static constexpr StringView type_name() { using namespace Corrade::Containers; using SVF = StringViewFlag; constexpr const char* str = FM_PRETTY_FUNCTION; return StringView { str, Implementation::strlen_(str), SVF::Global|SVF::NullTerminated }; } template struct type_name_helper final { static constexpr const StringView name = type_name(); }; template R> struct read_field { static constexpr Type read(const Obj& x, R r) { return r(x); } }; template struct read_field { static constexpr Type read(const Obj& x, Type (Obj::*r)() const) { return (x.*r)(); } }; template struct read_field { static constexpr Type read(const Obj& x, Type Obj::*r) { return x.*r; } }; template struct read_field) const>> { template static constexpr Type read(const Obj& x, F&& fun) { return fun(x); } }; template W> struct write_field { static constexpr void write(Obj& x, W w, move_qualified value) { w(x, value); } }; template struct write_field)> { static constexpr void write(Obj& x, void(Obj::*w)(move_qualified), move_qualified value) { (x.*w)(value); } }; template struct write_field { static constexpr void write(Obj& x, FieldType Obj::* w, move_qualified value) { x.*w = value; } }; template struct write_field) const>> { template static constexpr void write(Obj& x, F&& fun, move_qualified value) { fun(x, value); } }; template requires std::invocable(std::declval()))> constexpr CORRADE_ALWAYS_INLINE void visit_tuple(F&& fun, Tuple&& tuple) { using Size = std::tuple_size>; static_assert(N < Size()); fun(std::get(tuple)); if constexpr(N+1 < Size()) visit_tuple(std::forward(fun), std::forward(tuple)); } template requires std::is_invocable_r_v(std::declval()))> constexpr CORRADE_ALWAYS_INLINE bool find_in_tuple(F&& fun, Tuple&& tuple) { using Size = std::tuple_size>; static_assert(N < Size()); if (fun(std::get(tuple))) return true; if constexpr(N+1 < Size()) return find_in_tuple(std::forward(fun), std::forward(tuple)); return false; } template struct decay_tuple_; template struct decay_tuple_> { using type = std::tuple...>; }; template using decay_tuple = typename decay_tuple_::type; template struct accessors_for_ { using type = decay_tuple>; }; template using accessors_for = typename accessors_for_::type; } // namespace detail struct erased_accessor final { using erased_reader_t = void; using erased_writer_t = void; using Object = void; using Value = void; const erased_reader_t* reader; const erased_writer_t* writer; StringView object_name, field_type_name; void(*read_fun)(const Object*, const erased_reader_t*, Value*); void(*write_fun)(Object*, const erased_writer_t*, Value*); constexpr erased_accessor(const erased_accessor&) = default; constexpr erased_accessor(erased_reader_t* reader, erased_writer_t * writer, StringView object_name, StringView field_type_name, void(*read_fun)(const Object*, const erased_reader_t*, Value*), void(*write_fun)(Object*, const erased_writer_t*, Value*)) : reader{reader}, writer{writer}, object_name{object_name}, field_type_name{field_type_name}, read_fun{read_fun}, write_fun{write_fun} {} template static constexpr bool check_name_static() { return !std::is_pointer_v && !std::is_reference_v && !std::is_pointer_v && !std::is_reference_v; } template constexpr bool check_name() const noexcept { static_assert(check_name_static()); constexpr auto obj = name_of, field = name_of; return (obj.data() == object_name.data() && field.data() == field_type_name.data()) || obj == object_name && field == field_type_name; } template constexpr void assert_name() const noexcept { fm_assert(check_name()); } template void read_unchecked(const Obj& x, FieldType& value) const noexcept { static_assert(check_name_static()); read_fun(&x, reader, &value); } template requires std::is_default_constructible_v FieldType read_unchecked(const Obj& x) const noexcept { static_assert(check_name_static()); FieldType value; read_unchecked(x, value); return value; } template void write_unchecked(Obj& x, move_qualified value) const noexcept { static_assert(check_name_static()); write_fun(&x, writer, &value); } template requires std::is_default_constructible_v FieldType read(const Obj& x) const noexcept { assert_name(); return read_unchecked(x); } template void read(const Obj& x, FieldType& value) const noexcept { assert_name(); read_unchecked(x, value); } template void write(Obj& x, move_qualified value) const noexcept { assert_name(); write_unchecked(x, value); } }; template struct entity_field_base {}; template R, FieldWriter W> struct entity_field : entity_field_base { using ObjectType = Obj; using FieldType = Type; using Reader = R; using Writer = W; StringView name; [[no_unique_address]] R reader; [[no_unique_address]] W writer; constexpr entity_field(const entity_field&) = default; constexpr entity_field& operator=(const entity_field&) = default; static constexpr decltype(auto) read(const R& reader, const Obj& x) { return detail::read_field::read(x, reader); } static constexpr void write(const W& writer, Obj& x, move_qualified v) { detail::write_field::write(x, writer, v); } constexpr decltype(auto) read(const Obj& x) const { return read(reader, x); } constexpr void write(Obj& x, move_qualified value) const { write(writer, x, value); } constexpr entity_field(StringView name, R r, W w) noexcept : name{name}, reader{r}, writer{w} {} constexpr erased_accessor erased() const { using reader_t = typename erased_accessor::erased_reader_t; using writer_t = typename erased_accessor::erased_writer_t; constexpr auto obj_name = name_of, field_name = name_of; constexpr auto reader_fn = [](const void* obj, const reader_t* reader, void* value) { const auto& obj_ = *reinterpret_cast(obj); const auto& reader_ = *reinterpret_cast(reader); auto& value_ = *reinterpret_cast(value); value_ = read(reader_, obj_); }; constexpr auto writer_fn = [](void* obj, const writer_t* writer, void* value) { auto& obj_ = *reinterpret_cast(obj); const auto& writer_ = *reinterpret_cast(writer); move_qualified value_ = std::move(*reinterpret_cast(value)); write(writer_, obj_, value_); }; return erased_accessor{ (void*)&reader, (void*)&writer, obj_name, field_name, reader_fn, writer_fn, }; } }; template struct Entity final { static_assert(std::is_same_v>); template struct type final { template R, FieldWriter W> struct field final : entity_field { constexpr field(StringView field_name, R r, W w) noexcept : entity_field{field_name, r, w} {} }; template R, FieldWriter W> field(StringView name, R r, W w) -> field; }; }; template constexpr void visit_tuple(F&& fun, Tuple&& tuple) { using Size = std::tuple_size>; if constexpr(Size() > 0) detail::visit_tuple(std::forward(fun), std::forward(tuple)); } template constexpr bool find_in_tuple(F&& fun, Tuple&& tuple) { using Size = std::tuple_size>; if constexpr(Size() > 0) return detail::find_in_tuple(std::forward(fun), std::forward(tuple)); else return false; } enum class erased_field_type : std::uint32_t { none, string, u8, u16, u32, u64, s8, s16, s32, s64, user_type_start, MAX = (1u << 31) - 1u, DYNAMIC = (std::uint32_t)-1, }; template struct type_of_erased_field; template struct erased_field_type_v_ : std::integral_constant {}; #define FM_ERASED_FIELD_TYPE(TYPE, ENUM) \ template<> struct erased_field_type_v_ : std::integral_constant {}; \ template<> struct type_of_erased_field { using type = TYPE; } FM_ERASED_FIELD_TYPE(std::uint8_t, u8); FM_ERASED_FIELD_TYPE(std::uint16_t, u16); FM_ERASED_FIELD_TYPE(std::uint32_t, u32); FM_ERASED_FIELD_TYPE(std::uint64_t, u64); FM_ERASED_FIELD_TYPE(std::int8_t, s8); FM_ERASED_FIELD_TYPE(std::int16_t, s16); FM_ERASED_FIELD_TYPE(std::int32_t, s32); FM_ERASED_FIELD_TYPE(std::int64_t, s64); FM_ERASED_FIELD_TYPE(StringView, string); #undef FM_ERASED_FIELD_TYPE } // namespace floormat::entities namespace floormat { template requires std::is_same_v> class entity_metadata final { template static constexpr auto erased_helper(const std::tuple& tuple) { std::array array { std::get(tuple).erased()..., }; return array; } public: static constexpr StringView class_name = name_of; static constexpr std::size_t size = std::tuple_size_v>; static constexpr entities::detail::accessors_for accessors = T::accessors(); static constexpr auto erased_accessors = erased_helper(accessors); }; } // namespace floormat