diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2022-11-18 15:56:05 +0100 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2022-11-18 15:56:05 +0100 |
commit | 4408dbdef53a35ca6402732a187fbcd9043505c7 (patch) | |
tree | 7a1734055e83246cb76a116430a8784fabc0a949 /entity/metadata.hpp | |
parent | 28449ce459900bd88527ed424c4297b4cd936fe6 (diff) |
entity: rename field -> metadata
Diffstat (limited to 'entity/metadata.hpp')
-rw-r--r-- | entity/metadata.hpp | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/entity/metadata.hpp b/entity/metadata.hpp new file mode 100644 index 00000000..4e18493d --- /dev/null +++ b/entity/metadata.hpp @@ -0,0 +1,261 @@ +#pragma once +#include "name-of.hpp" +#include "accessor.hpp" +#include "util.hpp" +#include <cstddef> +#include <concepts> +#include <type_traits> +#include <utility> +#include <tuple> +#include <array> +#include <compat/function2.hpp> +#include <Corrade/Containers/StringView.h> + +namespace floormat::entities { + +template<typename F, typename T, typename FieldType> +concept FieldReader_memfn = requires(const T x, F f) { + { (x.*f)() } -> std::convertible_to<FieldType>; +}; + +template<typename F, typename T, typename FieldType> +concept FieldReader_ptr = requires(const T x, F f) { + { x.*f } -> std::convertible_to<FieldType>; +}; + +template<typename F, typename T, typename FieldType> +concept FieldReader_function = requires(const T x, F f) { + { f(x) } -> std::convertible_to<FieldType>; +}; + +template<typename F, typename T, typename FieldType> +concept FieldReader = requires { + requires FieldReader_memfn<F, T, FieldType> || + FieldReader_ptr<F, T, FieldType> || + FieldReader_function<F, T, FieldType>; +}; + +template<typename F, typename T, typename FieldType> +concept FieldWriter_memfn = requires(T x, move_qualified<FieldType> value, F f) { + { (x.*f)(value) } -> std::same_as<void>; +}; + +template<typename F, typename T, typename FieldType> +concept FieldWriter_ptr = requires(T x, move_qualified<FieldType> value, F f) { + { x.*f = value }; +}; + +template<typename F, typename T, typename FieldType> +concept FieldWriter_function = requires(T x, move_qualified<FieldType> value, F f) { + { f(x, value) } -> std::same_as<void>; +}; + +template<typename F, typename T, typename FieldType> +concept FieldWriter = requires { + requires FieldWriter_memfn<F, T, FieldType> || + FieldWriter_ptr<F, T, FieldType> || + FieldWriter_function<F, T, FieldType> || + std::same_as<F, std::nullptr_t>; +}; + +namespace detail { + +template<typename Obj, typename Type, FieldReader<Obj, Type> R> +struct read_field { + static constexpr Type read(const Obj& x, R r) { return r(x); } +}; + +template<typename Obj, typename Type> +struct read_field<Obj, Type, Type (Obj::*)() const> { + static constexpr Type read(const Obj& x, Type (Obj::*r)() const) { return (x.*r)(); } +}; + +template<typename Obj, typename Type> +struct read_field<Obj, Type, Type Obj::*> { + static constexpr Type read(const Obj& x, Type Obj::*r) { return x.*r; } +}; + +template<typename Obj, typename Type, bool Owning, bool Copyable, typename Capacity, bool Throwing, bool ExcGuarantee> +struct read_field<Obj, Type, fu2::function_base<Owning, Copyable, Capacity, Throwing, ExcGuarantee, void(const Obj&, move_qualified<Type>) const>> +{ + template<typename F> static constexpr Type read(const Obj& x, F&& fun) { return fun(x); } +}; + +template<typename Obj, typename FieldType, FieldWriter<Obj, FieldType> W> struct write_field { + static constexpr void write(Obj& x, W w, move_qualified<FieldType> value) { w(x, value); } +}; + +template<typename Obj, typename FieldType> +struct write_field<Obj, FieldType, void(Obj::*)(move_qualified<FieldType>)> { + static constexpr void write(Obj& x, void(Obj::*w)(move_qualified<FieldType>), move_qualified<FieldType> value) { (x.*w)(value); } +}; + +template<typename Obj, typename FieldType> +struct write_field<Obj, FieldType, FieldType Obj::*> { + static constexpr void write(Obj& x, FieldType Obj::* w, move_qualified<FieldType> value) { x.*w = value; } +}; + +template<typename Obj, typename Type, bool Owning, bool Copyable, typename Capacity, bool Throwing, bool ExcGuarantee> +struct write_field<Obj, Type, fu2::function_base<Owning, Copyable, Capacity, Throwing, ExcGuarantee, void(Obj&, move_qualified<Type>) const>> { + template<typename F> static constexpr void write(Obj& x, F&& fun, move_qualified<Type> value) { fun(x, value); } +}; + +template<typename Obj, typename FieldType> +struct write_field<Obj, FieldType, std::nullptr_t> { + static constexpr void write(Obj&, std::nullptr_t, move_qualified<FieldType>) { fm_abort("no writing for this accessor"); } +}; + +template<typename F, typename Tuple, std::size_t N> +requires std::invocable<F, decltype(std::get<N>(std::declval<Tuple>()))> +constexpr CORRADE_ALWAYS_INLINE void visit_tuple(F&& fun, Tuple&& tuple) +{ + using Size = std::tuple_size<std::remove_cvref_t<Tuple>>; + static_assert(N < Size()); + + fun(std::get<N>(tuple)); + if constexpr(N+1 < Size()) + visit_tuple<F, Tuple, N+1>(std::forward<F>(fun), std::forward<Tuple>(tuple)); +} + +template<typename F, typename Tuple, std::size_t N> +requires std::is_invocable_r_v<bool, F, decltype(std::get<N>(std::declval<Tuple>()))> +constexpr CORRADE_ALWAYS_INLINE bool find_in_tuple(F&& fun, Tuple&& tuple) +{ + using Size = std::tuple_size<std::remove_cvref_t<Tuple>>; + static_assert(N < Size()); + + if (fun(std::get<N>(tuple))) + return true; + if constexpr(N+1 < Size()) + return find_in_tuple<F, Tuple, N+1>(std::forward<F>(fun), std::forward<Tuple>(tuple)); + return false; +} + +template<typename T> struct decay_tuple_; +template<typename... Ts> struct decay_tuple_<std::tuple<Ts...>> { using type = std::tuple<std::decay_t<Ts>...>; }; +template<typename T> using decay_tuple = typename decay_tuple_<T>::type; +template<typename T> struct accessors_for_ { using type = decay_tuple<std::decay_t<decltype(T::accessors())>>; }; +template<typename T> using accessors_for = typename accessors_for_<T>::type; + +} // namespace detail + +template<typename Obj, typename Type> struct entity_field_base {}; + +template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W> +struct entity_field : entity_field_base<Obj, Type> { + 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<Obj, Type, R>::read(x, reader); } + static constexpr void write(const W& writer, Obj& x, move_qualified<Type> v); + constexpr decltype(auto) read(const Obj& x) const { return read(reader, x); } + constexpr void write(Obj& x, move_qualified<Type> value) const { write(writer, x, value); } + static constexpr bool can_write = !std::is_same_v<std::nullptr_t, decltype(entity_field<Obj, Type, R, W>::writer)>; + constexpr entity_field(StringView name, R r, W w) noexcept : name{name}, reader{r}, writer{w} {} + constexpr erased_accessor erased() const; +}; + +template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W> +constexpr void entity_field<Obj, Type, R, W>::write(const W& writer, Obj& x, move_qualified<Type> v) +{ + static_assert(can_write); detail::write_field<Obj, Type, W>::write(x, writer, v); +} + +template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W> +constexpr erased_accessor entity_field<Obj, Type, R, W>::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<Obj>, field_name = name_of<Type>; + + constexpr auto reader_fn = [](const void* obj, const reader_t* reader, void* value) { + const auto& obj_ = *reinterpret_cast<const Obj*>(obj); + const auto& reader_ = *reinterpret_cast<const R*>(reader); + auto& value_ = *reinterpret_cast<Type*>(value); + value_ = read(reader_, obj_); + }; + constexpr auto writer_fn = [](void* obj, const writer_t* writer, void* value) { + auto& obj_ = *reinterpret_cast<Obj*>(obj); + const auto& writer_ = *reinterpret_cast<const W*>(writer); + move_qualified<Type> value_ = std::move(*reinterpret_cast<Type*>(value)); + write(writer_, obj_, value_); + }; + constexpr auto writer_stub_fn = [](void*, const writer_t*, void*) { + fm_abort("no writer for this accessor"); + }; + + return erased_accessor { + (void*)&reader, writer ? (void*)&writer : nullptr, + name, + obj_name, field_name, + reader_fn, writer ? writer_fn : writer_stub_fn, + }; +} + +template<typename Obj> +struct Entity final { + static_assert(std::is_same_v<Obj, std::decay_t<Obj>>); + + template<typename Type> + struct type final + { + template<FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W> + struct field final : entity_field<Obj, Type, R, W> + { + constexpr field(StringView field_name, R r, W w) noexcept : + entity_field<Obj, Type, R, W>{field_name, r, w} + {} + }; + + template<FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W> + field(StringView name, R r, W w) -> field<R, W>; + }; +}; + +template<typename F, typename Tuple> +constexpr void visit_tuple(F&& fun, Tuple&& tuple) +{ + using Size = std::tuple_size<std::decay_t<Tuple>>; + if constexpr(Size() > 0) + detail::visit_tuple<F, Tuple, 0>(std::forward<F>(fun), std::forward<Tuple>(tuple)); +} + +template<typename F, typename Tuple> +constexpr bool find_in_tuple(F&& fun, Tuple&& tuple) +{ + using Size = std::tuple_size<std::decay_t<Tuple>>; + if constexpr(Size() > 0) + return detail::find_in_tuple<F, Tuple, 0>(std::forward<F>(fun), std::forward<Tuple>(tuple)); + else + return false; +} + +} // namespace floormat::entities + +namespace floormat { + +template<typename T> +requires std::is_same_v<T, std::decay_t<T>> +class entity_metadata final { + template<typename... Ts> + static consteval auto erased_helper(const std::tuple<Ts...>& tuple) + { + std::array<entities::erased_accessor, sizeof...(Ts)> array { std::get<Ts>(tuple).erased()..., }; + return array; + } +public: + static constexpr StringView class_name = name_of<T>; + static constexpr std::size_t size = std::tuple_size_v<entities::detail::accessors_for<T>>; + static constexpr entities::detail::accessors_for<T> accessors = T::accessors(); + static constexpr auto erased_accessors = erased_helper(accessors); +}; + +} // namespace floormat |