#pragma once #include "name-of.hpp" #include "accessor.hpp" #include "constraints.hpp" #include "util.hpp" #include "concepts.hpp" #include "compat/defs.hpp" #include #include #include #include #include #include #include namespace floormat::entities { struct inspect_intent_t {}; struct serialize_intent_t {}; struct report_intent_t {}; template struct entity_accessors; } // namespace floormat::entities namespace floormat::entities::detail { 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(floormat::forward(fun), floormat::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(floormat::forward(fun), floormat::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 find_reader; template struct find_reader { using type = Default; static constexpr size_t index = I; }; template struct find_reader { using type = typename find_reader::type; static constexpr size_t index = find_reader::index; }; template requires FieldReader struct find_reader { using type = F; static constexpr size_t index = I; }; } // namespace floormat::entities::detail namespace floormat::entities { constexpr auto constantly(const auto& x) noexcept { return [x] (const Ts&...) constexpr -> const auto& { return x; }; } template struct entity_field_base {}; template R, FieldWriter W, typename... Ts> struct entity_field : entity_field_base { private: static constexpr auto default_predicate = constantly(field_status::enabled); static constexpr auto default_c_range = constantly(constraints::range{}); static constexpr auto default_c_length = constantly(constraints::max_length{size_t(-1)}); using default_predicate_t = std::decay_t; using default_c_range_t = std::decay_t; using default_c_length_t = std::decay_t; using c_predicate = detail::find_reader; using c_range = detail::find_reader, default_c_range_t, 0, Ts...>; using c_length = detail::find_reader; static constexpr size_t good_arguments = unsigned(c_predicate::index != sizeof...(Ts)) + unsigned(c_range::index != sizeof...(Ts)) + unsigned(c_length::index != sizeof...(Ts)); static_assert(sizeof...(Ts) == good_arguments, "ignored arguments"); public: using ObjectType = Obj; using FieldType = Type; using Reader = R; using Writer = W; using Predicate = typename c_predicate::type; using Range = typename c_range::type; using Length = typename c_length::type; StringView name; [[fm_no_unique_address]] R reader; [[fm_no_unique_address]] W writer; [[fm_no_unique_address]] Predicate predicate; [[fm_no_unique_address]] Range range; [[fm_no_unique_address]] Length length; fm_DECLARE_DEFAULT_MOVE_COPY_ASSIGNMENTS(entity_field); 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, Type v); constexpr decltype(auto) read(const Obj& x) const { return read(reader, x); } constexpr void write(Obj& x, Type value) const { write(writer, x, move(value)); } static constexpr bool can_write = !std::is_same_v::writer)>; static constexpr field_status is_enabled(const Predicate & p, const Obj& x); constexpr field_status is_enabled(const Obj& x) const { return is_enabled(predicate, x); } static constexpr constraints::range get_range(const Range& r, const Obj& x); constexpr constraints::range get_range(const Obj& x) const { return get_range(range, x); } static constexpr constraints::max_length get_max_length(const Length& l, const Obj& x); constexpr constraints::max_length get_max_length(const Obj& x) const { return get_max_length(length, x); } constexpr entity_field(StringView name, R r, W w, Ts&&... ts) noexcept : name{name}, reader{r}, writer{w}, predicate { std::get(std::forward_as_tuple(ts..., default_predicate)) }, range { std::get (std::forward_as_tuple(ts..., default_c_range)) }, length { std::get (std::forward_as_tuple(ts..., default_c_length)) } {} constexpr erased_accessor erased() const; }; template R, FieldWriter W, typename... Ts> constexpr void entity_field::write(const W& writer, Obj& x, Type v) { static_assert(can_write); detail::write_field::write(x, writer, move(v)); } template R, FieldWriter W, typename... Ts> constexpr erased_accessor entity_field::erased() const { using reader_t = typename erased_accessor::reader_t; using writer_t = typename erased_accessor::writer_t; using predicate_t = typename erased_accessor::predicate_t; using c_range_t = typename erased_accessor::c_range_t; using c_length_t = typename erased_accessor::c_length_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_ = *static_cast(obj); const auto& reader_ = *static_cast(reader); auto& value_ = *static_cast(value); value_ = read(reader_, obj_); }; constexpr auto writer_fn = [](void* obj, const writer_t* writer, void* value) { auto& obj_ = *static_cast(obj); const auto& writer_ = *static_cast(writer); Type value_ = move(*static_cast(value)); write(writer_, obj_, move(value_)); }; constexpr auto predicate_fn = [](const void* obj, const predicate_t* predicate) { const auto& obj_ = *static_cast(obj); const auto& predicate_ = *static_cast(predicate); return is_enabled(predicate_, obj_); }; constexpr auto writer_stub_fn = [](void*, const writer_t*, void*) { fm_abort("no writer for this accessor"); }; constexpr bool has_writer = !std::is_same_v, std::nullptr_t>; constexpr auto c_range_fn = [](const void* obj, const c_range_t* reader) -> erased_constraints::range { return get_range(*static_cast(reader), *reinterpret_cast(obj)); }; constexpr auto c_length_fn = [](const void* obj, const c_length_t* reader) -> erased_constraints::max_length { return get_max_length(*static_cast(reader), *reinterpret_cast(obj)); }; return erased_accessor { (void*)&reader, has_writer ? (void*)&writer : nullptr, (void*)&predicate, (void*)&range, (void*)&length, name, obj_name, field_name, reader_fn, has_writer ? writer_fn : writer_stub_fn, predicate_fn, c_range_fn, c_length_fn, }; } template R, FieldWriter W, typename... Ts> constexpr field_status entity_field::is_enabled(const Predicate& p, const Obj& x) { return detail::read_field::read(x, p); } template R, FieldWriter W, typename... Ts> constexpr constraints::range entity_field::get_range(const Range& r, const Obj& x) { return detail::read_field, Range>::read(x, r); } template R, FieldWriter W, typename... Ts> constexpr constraints::max_length entity_field::get_max_length(const Length& l, const Obj& x) { return detail::read_field::read(x, l); } template struct Entity final { static_assert(std::is_same_v>); template struct type final { template R, FieldWriter W, typename... Ts> struct field final : entity_field { constexpr field(StringView field_name, R r, W w, Ts&&... ts) noexcept : entity_field{field_name, r, w, floormat::forward(ts)...} {} }; template R, FieldWriter W, typename... Ts> field(StringView name, R r, W w, Ts&&... ts) -> field; }; }; template constexpr void visit_tuple(F&& fun, Tuple&& tuple) { using Size = std::tuple_size>; if constexpr(Size() > 0) detail::visit_tuple(floormat::forward(fun), floormat::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(floormat::forward(fun), floormat::forward(tuple)); else return false; } constexpr inline auto ignored_write = [](O&, T) {}; } // namespace floormat::entities namespace floormat { template class entity_metadata final { static_assert(std::is_same_v>); template static consteval auto erased_helper(const Tuple& tuple, std::index_sequence); public: static constexpr StringView class_name = name_of; static constexpr auto accessors = entities::entity_accessors::accessors(); static constexpr size_t size = std::tuple_size_v>; static constexpr auto erased_accessors = erased_helper(accessors, std::make_index_sequence{}); }; template template consteval auto entity_metadata::erased_helper(const Tuple& tuple, std::index_sequence) { std::array array { std::get(tuple).erased()..., }; return array; } } // namespace floormat