From b65b34a976b586f4e407f7a89567163c85b94aa6 Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Fri, 18 Nov 2022 10:42:26 +0100 Subject: split erased accessor from main header --- entity/accessor.hpp | 112 +++++++++++++++++++++ entity/entity.hpp | 280 ++++++++++++++++++++++++++++++++++++++++++++++++++++ entity/name-of.hpp | 18 ++++ entity/util.hpp | 22 +++++ 4 files changed, 432 insertions(+) create mode 100644 entity/accessor.hpp create mode 100644 entity/entity.hpp create mode 100644 entity/name-of.hpp create mode 100644 entity/util.hpp (limited to 'entity') diff --git a/entity/accessor.hpp b/entity/accessor.hpp new file mode 100644 index 00000000..9b836420 --- /dev/null +++ b/entity/accessor.hpp @@ -0,0 +1,112 @@ +#pragma once +#include "util.hpp" +#include +#include + +namespace floormat::entities { + +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(); + + template + constexpr bool check_name() const noexcept; + + template constexpr void assert_name() const noexcept; + template void read_unchecked(const Obj& x, FieldType& value) const noexcept; + template requires std::is_default_constructible_v FieldType read_unchecked(const Obj& x) const noexcept; + template void write_unchecked(Obj& x, move_qualified value) const noexcept; + template requires std::is_default_constructible_v FieldType read(const Obj& x) const noexcept; + template void read(const Obj& x, FieldType& value) const noexcept; + template void write(Obj& x, move_qualified value) const noexcept; +}; + +template +constexpr bool erased_accessor::check_name_static() +{ + return !std::is_pointer_v && !std::is_reference_v && + !std::is_pointer_v && !std::is_reference_v; +} + +template +constexpr bool erased_accessor::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 erased_accessor::assert_name() const noexcept +{ + fm_assert(check_name()); +} + +template +void erased_accessor::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 erased_accessor::read_unchecked(const Obj& x) const noexcept +{ + static_assert(check_name_static()); + FieldType value; + read_unchecked(x, value); + return value; +} + +template +void erased_accessor::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 erased_accessor::read(const Obj& x) const noexcept { + assert_name(); + return read_unchecked(x); +} + +template +void erased_accessor::read(const Obj& x, FieldType& value) const noexcept +{ + assert_name(); + read_unchecked(x, value); +} + +template +void erased_accessor::write(Obj& x, move_qualified value) const noexcept +{ + assert_name(); + write_unchecked(x, value); +} + +} // namespace floormat::entities diff --git a/entity/entity.hpp b/entity/entity.hpp new file mode 100644 index 00000000..8f0980ae --- /dev/null +++ b/entity/entity.hpp @@ -0,0 +1,280 @@ +#pragma once +#include "name-of.hpp" +#include "compat/integer-types.hpp" +#include "accessor.hpp" +#include "util.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace floormat::entities { + +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 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 + +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 diff --git a/entity/name-of.hpp b/entity/name-of.hpp new file mode 100644 index 00000000..592f07c1 --- /dev/null +++ b/entity/name-of.hpp @@ -0,0 +1,18 @@ +#pragma once +#include + +#if defined _MSC_VER +#define FM_PRETTY_FUNCTION __FUNCSIG__ +#else +#define FM_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#endif + +template +static constexpr auto _name_of() { + 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 constexpr inline Corrade::Containers::StringView name_of = _name_of(); diff --git a/entity/util.hpp b/entity/util.hpp new file mode 100644 index 00000000..073745e9 --- /dev/null +++ b/entity/util.hpp @@ -0,0 +1,22 @@ +#pragma once +#include + +namespace Corrade::Containers { + +template class BasicStringView; +using StringView = BasicStringView; + +} // namespace Corrade::Containers + +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&&>; + +} // namespace floormat::entities -- cgit v1.2.3