diff options
-rw-r--r-- | entity/accessor.hpp | 77 | ||||
-rw-r--r-- | entity/constraints.hpp | 3 | ||||
-rw-r--r-- | entity/erased-constraints.hpp | 21 | ||||
-rw-r--r-- | entity/metadata.hpp | 72 | ||||
-rw-r--r-- | test/entity.cpp | 37 |
5 files changed, 154 insertions, 56 deletions
diff --git a/entity/accessor.hpp b/entity/accessor.hpp index f069cb5b..5e6a60ce 100644 --- a/entity/accessor.hpp +++ b/entity/accessor.hpp @@ -1,36 +1,62 @@ #pragma once #include "util.hpp" +#include "erased-constraints.hpp" #include <type_traits> +#include <utility> #include <Corrade/Containers/StringView.h> +namespace floormat::erased_constraints { + +struct range; +struct max_length; +struct group; + +} // namespace floormat::erased_constraints + namespace floormat::entities { enum class field_status : unsigned char { enabled, hidden, readonly, }; struct erased_accessor final { - using erased_reader_t = void; - using erased_writer_t = void; - using erased_predicate_t = void; + using reader_t = void; + using writer_t = void; + using predicate_t = void; + using c_range_t = void; + using c_length_t = void; + using c_group_t = void; using Object = void; using Value = void; - const erased_reader_t* reader; - const erased_writer_t* writer; - const erased_predicate_t* predicate; + const reader_t* reader; + const writer_t* writer; + const predicate_t* predicate; + const c_range_t* range; + const c_length_t* length; + const c_group_t* group; + StringView field_name, object_type, field_type; - void(*read_fun)(const Object*, const erased_reader_t*, Value*); - void(*write_fun)(Object*, const erased_writer_t*, Value*); - field_status(*predicate_fun)(const Object*, const erased_predicate_t*); + void(*read_fun)(const Object*, const reader_t*, Value*); + void(*write_fun)(Object*, const writer_t*, Value*); + field_status(*predicate_fun)(const Object*, const predicate_t*); + erased_constraints::range(*range_fun)(const Object*, const c_range_t*); + erased_constraints::max_length(*length_fun)(const Object*, const c_length_t*); + erased_constraints::group(*group_fun)(const Object*, const c_group_t*); constexpr erased_accessor(const erased_accessor&) = default; - constexpr erased_accessor(const erased_reader_t* reader, const erased_writer_t* writer, const erased_predicate_t* predicate, + constexpr erased_accessor(const reader_t* reader, const writer_t* writer, const predicate_t* predicate, + const c_range_t* range, const c_length_t* length, const c_group_t* group, StringView field_name, 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*), - field_status(*predicate_fun)(const Object*, const erased_predicate_t*)) : + void(*read_fun)(const Object*, const reader_t*, Value*), + void(*write_fun)(Object*, const writer_t*, Value*), + field_status(*predicate_fun)(const Object*, const predicate_t*), + erased_constraints::range(*range_fun)(const Object*, const c_range_t*), + erased_constraints::max_length(*length_fun)(const Object*, const c_length_t*), + erased_constraints::group(*group_fun)(const Object*, const c_group_t*)) : reader{reader}, writer{writer}, predicate{predicate}, + range{range}, length{length}, group{group}, field_name{field_name}, object_type{object_name}, field_type{field_type_name}, - read_fun{read_fun}, write_fun{write_fun}, predicate_fun{predicate_fun} + read_fun{read_fun}, write_fun{write_fun}, predicate_fun{predicate_fun}, + range_fun{range_fun}, length_fun{length_fun}, group_fun{group_fun} {} template<typename T, typename FieldType> @@ -39,6 +65,9 @@ struct erased_accessor final { template<typename T, typename FieldType> constexpr bool check_name() const noexcept; + template<typename Obj> + constexpr void do_asserts() const; + template<typename Obj, typename FieldType> constexpr void assert_name() const noexcept; template<typename Obj, typename FieldType> void read_unchecked(const Obj& x, FieldType& value) const noexcept; template<typename Obj, typename FieldType> requires std::is_default_constructible_v<FieldType> FieldType read_unchecked(const Obj& x) const noexcept; @@ -46,8 +75,12 @@ struct erased_accessor final { template<typename Obj, typename FieldType> requires std::is_default_constructible_v<FieldType> FieldType read(const Obj& x) const noexcept; template<typename Obj, typename FieldType> void read(const Obj& x, FieldType& value) const noexcept; template<typename Obj, typename FieldType> void write(Obj& x, move_qualified<FieldType> value) const noexcept; + template<typename Obj> field_status is_enabled(const Obj& x) const noexcept; constexpr bool can_write() const noexcept { return writer != nullptr; } + template<typename Obj> erased_constraints::range get_range(const Obj& x) const noexcept; + template<typename Obj> erased_constraints::max_length get_max_length(const Obj& x) const noexcept; + template<typename Obj> erased_constraints::group get_group(const Obj& x) const noexcept; }; template<typename T, typename FieldType> @@ -57,6 +90,14 @@ constexpr bool erased_accessor::check_name_static() !std::is_pointer_v<FieldType> && !std::is_reference_v<T>; } +template<typename Obj> +constexpr void erased_accessor::do_asserts() const +{ + static_assert(!std::is_pointer_v<Obj> && !std::is_reference_v<Obj>); + constexpr auto obj = name_of<Obj>; + fm_assert(obj.data() == object_type.data() || obj == object_type); +} + template<typename T, typename FieldType> constexpr bool erased_accessor::check_name() const noexcept { @@ -120,10 +161,12 @@ void erased_accessor::write(Obj& x, move_qualified<FieldType> value) const noexc template<typename Obj> field_status erased_accessor::is_enabled(const Obj& x) const noexcept { - static_assert(!std::is_pointer_v<Obj> && !std::is_reference_v<Obj>); - constexpr auto obj = name_of<Obj>; - fm_assert(obj.data() == object_type.data() || obj == object_type); + do_asserts<Obj>(); return predicate_fun(&x, predicate); } +template<typename T> erased_constraints::range erased_accessor::get_range(const T& x) const noexcept { do_asserts<T>();return range_fun(&x,range); } +template<typename T> erased_constraints::max_length erased_accessor::get_max_length(const T& x) const noexcept { do_asserts<T>();return length_fun(&x,length); } +template<typename T> erased_constraints::group erased_accessor::get_group(const T& x) const noexcept { do_asserts<T>();return group_fun(&x, group); } + } // namespace floormat::entities diff --git a/entity/constraints.hpp b/entity/constraints.hpp index bc991dfc..99705047 100644 --- a/entity/constraints.hpp +++ b/entity/constraints.hpp @@ -15,6 +15,7 @@ template<typename T> struct range constexpr operator erased_constraints::range() const noexcept; constexpr operator std::pair<T, T>() const noexcept; + constexpr bool operator==(const range&) const noexcept = default; }; template<typename T> @@ -32,7 +33,7 @@ constexpr range<T>::operator erased_constraints::range() const noexcept template<typename T> constexpr range<T>::operator std::pair<T, T>() const noexcept { return { min, max }; } -using length = erased_constraints::length; +using max_length = erased_constraints::max_length; using group = erased_constraints::group; } // namespace floormat::entities::constraints diff --git a/entity/erased-constraints.hpp b/entity/erased-constraints.hpp index f462472a..2d4b47ae 100644 --- a/entity/erased-constraints.hpp +++ b/entity/erased-constraints.hpp @@ -50,9 +50,27 @@ template<typename T> constexpr std::pair<T, T> range::convert() const } } -struct length final { +constexpr bool operator==(const range& a, const range& b) +{ + if (a.type != b.type) + return false; + + static constexpr float eps = 1e-6f; + + switch (a.type) + { + default: return false; + case range::type_none: return true; + case range::type_float: return std::fabs(a.min.f - b.min.f) < eps && std::fabs(a.max.f - b.max.f) < eps; + case range::type_uint: return a.min.u == b.min.u && a.max.u == b.max.u; + case range::type_int: return a.min.i == b.min.i && a.max.i == b.max.i; + } +} + +struct max_length final { std::size_t value = std::numeric_limits<std::size_t>::max(); constexpr operator std::size_t() const { return value; } + //constexpr bool operator==(const max_length&) const noexcept = default; }; struct group final { @@ -60,6 +78,7 @@ struct group final { constexpr operator StringView() const { return group_name; } constexpr group() = default; constexpr group(StringView name) : group_name{name} {} + //constexpr bool operator==(const group&) const noexcept = default; }; } // namespace floormat::entities::erased_constraints diff --git a/entity/metadata.hpp b/entity/metadata.hpp index c9b9a1d5..1067a006 100644 --- a/entity/metadata.hpp +++ b/entity/metadata.hpp @@ -79,7 +79,7 @@ struct entity_field : entity_field_base<Obj, Type> { private: static constexpr auto default_predicate = constantly<Obj>(field_status::enabled); static constexpr auto default_c_range = constantly<Obj>(constraints::range<Type>{}); - static constexpr auto default_c_length = constantly<Obj>(constraints::length{std::size_t(-1)}); + static constexpr auto default_c_length = constantly<Obj>(constraints::max_length{std::size_t(-1)}); static constexpr auto default_c_group = constantly<Obj>(StringView{}); using default_predicate_t = std::decay_t<decltype(default_predicate)>; using default_c_range_t = std::decay_t<decltype(default_c_range)>; @@ -87,7 +87,7 @@ private: using default_c_group_t = std::decay_t<decltype(default_c_group)>; using c_predicate = detail::find_reader<Obj, field_status, default_predicate_t, 0, Ts...>; using c_range = detail::find_reader<Obj, constraints::range<Type>, default_c_range_t, 0, Ts...>; - using c_length = detail::find_reader<Obj, constraints::length, default_c_length_t, 0, Ts...>; + using c_length = detail::find_reader<Obj, constraints::max_length, default_c_length_t, 0, Ts...>; using c_group = detail::find_reader<Obj, constraints::group, default_c_group_t, 0, Ts...>; static constexpr std::size_t good_arguments = unsigned(c_predicate::index != sizeof...(Ts)) + @@ -125,12 +125,12 @@ public: 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 std::pair<Type, Type> get_range(const Range& r, const Obj& x); - constexpr std::pair<Type, Type> get_range(const Obj& x) const { return get_range(range, x); } - static constexpr std::size_t get_max_length(const Length& l, const Obj& x); - constexpr std::size_t get_max_length(const Obj& x) const { return get_max_length(length, x); } - static constexpr StringView get_group(const Group& g, const Obj& x); - constexpr StringView get_group(const Obj& x) const { return get_group(group, x); } + static constexpr constraints::range<Type> get_range(const Range& r, const Obj& x); + constexpr constraints::range<Type> 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); } + static constexpr constraints::group get_group(const Group& g, const Obj& x); + constexpr constraints::group get_group(const Obj& x) const { return get_group(group, x); } constexpr entity_field(StringView name, R r, W w, Ts&&... ts) noexcept : name{name}, reader{r}, writer{w}, @@ -151,11 +151,13 @@ constexpr void entity_field<Obj, Type, R, W, Ts...>::write(const W& writer, Obj& template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> constexpr erased_accessor entity_field<Obj, Type, R, W, Ts...>::erased() const { - using reader_t = typename erased_accessor::erased_reader_t; - using writer_t = typename erased_accessor::erased_writer_t; - using predicate_t = typename erased_accessor::erased_predicate_t ; + 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; + using c_group_t = typename erased_accessor::c_group_t; constexpr auto obj_name = name_of<Obj>, field_name = name_of<Type>; - using P = Predicate; constexpr auto reader_fn = [](const void* obj, const reader_t* reader, void* value) { const auto& obj_ = *reinterpret_cast<const Obj*>(obj); @@ -171,17 +173,32 @@ constexpr erased_accessor entity_field<Obj, Type, R, W, Ts...>::erased() const }; constexpr auto predicate_fn = [](const void* obj, const predicate_t* predicate) { const auto& obj_ = *reinterpret_cast<const Obj*>(obj); - const auto& predicate_ = *reinterpret_cast<const P*>(predicate); + const auto& predicate_ = *reinterpret_cast<const Predicate*>(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::decay_t<decltype(writer)>, std::nullptr_t>; + + constexpr auto c_range_fn = [](const void* obj, const c_range_t* reader) -> erased_constraints::range { + return get_range(*reinterpret_cast<const Range*>(reader), *reinterpret_cast<const Obj*>(obj)); + }; + constexpr auto c_length_fn = [](const void* obj, const c_length_t* reader) -> erased_constraints::max_length { + return get_max_length(*reinterpret_cast<const Length*>(reader), *reinterpret_cast<const Obj*>(obj)); + }; + constexpr auto c_group_fn = [](const void* obj, const c_group_t* reader) -> erased_constraints::group { + return get_group(*reinterpret_cast<const Group*>(reader), *reinterpret_cast<const Obj*>(obj)); + }; return erased_accessor { - (void*)&reader, writer ? (void*)&writer : nullptr, (void*)&predicate, + (void*)&reader, has_writer ? (void*)&writer : nullptr, + (void*)&predicate, + (void*)&range, (void*)&length, (void*)&group, name, obj_name, field_name, - reader_fn, writer ? writer_fn : writer_stub_fn, predicate_fn, + reader_fn, has_writer ? writer_fn : writer_stub_fn, + predicate_fn, + c_range_fn, c_length_fn, c_group_fn, }; } @@ -190,15 +207,15 @@ constexpr field_status entity_field<Obj, Type, R, W, Ts...>::is_enabled(const Pr { return detail::read_field<Obj, field_status, Predicate>::read(x, p); } template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> -constexpr std::pair<Type, Type> entity_field<Obj, Type, R, W, Ts...>::get_range(const Range& r, const Obj& x) +constexpr constraints::range<Type> entity_field<Obj, Type, R, W, Ts...>::get_range(const Range& r, const Obj& x) { return detail::read_field<Obj, constraints::range<Type>, Range>::read(x, r); } template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> -constexpr std::size_t entity_field<Obj, Type, R, W, Ts...>::get_max_length(const Length& l, const Obj& x) -{ return detail::read_field<Obj, constraints::length, Length>::read(x, l); } +constexpr constraints::max_length entity_field<Obj, Type, R, W, Ts...>::get_max_length(const Length& l, const Obj& x) +{ return detail::read_field<Obj, constraints::max_length , Length>::read(x, l); } template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> -constexpr StringView entity_field<Obj, Type, R, W, Ts...>::get_group(const Group& g, const Obj& x) +constexpr constraints::group entity_field<Obj, Type, R, W, Ts...>::get_group(const Group& g, const Obj& x) { return detail::read_field<Obj, constraints::group, Group>::read(x, g); } template<typename Obj> @@ -244,14 +261,9 @@ constexpr bool find_in_tuple(F&& fun, Tuple&& tuple) 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; - } + static_assert(std::is_same_v<T, std::decay_t<T>>); + template<typename... Ts> static consteval auto erased_helper(const std::tuple<Ts...>& tuple); public: static constexpr StringView class_name = name_of<T>; static constexpr std::size_t size = std::tuple_size_v<entities::detail::accessors_for<T>>; @@ -259,4 +271,12 @@ public: static constexpr auto erased_accessors = erased_helper(accessors); }; +template<typename T> +template<typename... Ts> +consteval auto entity_metadata<T>::erased_helper(const std::tuple<Ts...>& tuple) +{ + std::array<entities::erased_accessor, sizeof...(Ts)> array { std::get<Ts>(tuple).erased()..., }; + return array; +} + } // namespace floormat diff --git a/test/entity.cpp b/test/entity.cpp index 34b89d1f..188b3b19 100644 --- a/test/entity.cpp +++ b/test/entity.cpp @@ -68,7 +68,7 @@ constexpr bool test_accessors() constexpr bool test_visitor() { { - constexpr auto tuple = std::make_tuple((unsigned char)1, (unsigned short)2, (int)3, (long)4); + auto tuple = std::make_tuple((unsigned char)1, (unsigned short)2, (int)3, (long)4); long ret = 0; visit_tuple([&](auto x) { ret += (long)x; }, tuple); fm_assert(ret == 1 + 2 + 3 + 4); @@ -186,35 +186,49 @@ constexpr bool test_names() return true; } -constexpr void test_range() +constexpr void test_constraints() { constexpr auto x = TestAccessors{}; constexpr auto foo = entity::type<int>::field{ "foo"_s, &TestAccessors::foo, &TestAccessors::foo, - constantly<TestAccessors>(constraints::length{42}), + constantly<TestAccessors>(constraints::max_length{42}), constantly<TestAccessors>(constraints::range<int>{37, 42}), constantly<TestAccessors>(constraints::group{"foo"_s}) }; - using limits = std::numeric_limits<int>; - - static_assert(foo.get_range(x) == std::pair<int, int>{37, 42}); + static_assert(foo.get_range(x) == constraints::range<int>{37, 42}); static_assert(foo.get_max_length(x) == 42); static_assert(foo.get_group(x) == "foo"_s); - static_assert(m_foo.get_range(x) == std::pair<int, int>{limits::min(), limits::max()}); + static_assert(m_foo.get_range(x) == constraints::range<int>{}); static_assert(m_foo.get_max_length(x) == (std::size_t)-1); static_assert(m_foo.get_group(x) == ""_s); - constexpr auto foo2 = entity::type<int>::field{ + constexpr auto foo2 = entity::type<int>::field { "foo"_s, &TestAccessors::foo, &TestAccessors::foo, - constantly<TestAccessors>(constraints::length{123}), + constantly<TestAccessors>(constraints::max_length {123}), }; - static_assert(foo2.get_range(x) == std::pair<int, int>{limits::min(), limits::max()}); + static_assert(foo2.get_range(x) == constraints::range<int>{}); static_assert(foo2.get_max_length(x) == 123); static_assert(foo2.get_group(x) == ""_s); } +void test_erased_constraints() +{ + static constexpr auto foo = entity::type<int>::field{ + "foo"_s, &TestAccessors::foo, &TestAccessors::foo, + constantly<TestAccessors>(constraints::max_length{42}), + constantly<TestAccessors>(constraints::range<int>{37, 42}), + constantly<TestAccessors>(constraints::group{"foo"_s}) + }; + const auto x = TestAccessors{}; + const auto erased = foo.erased(); + + fm_assert(erased.get_range(x) == constraints::range<int>{37, 42}); + fm_assert(erased.get_max_length(x) == 42); + fm_assert(erased.get_group(x) == "foo"_s); +} + } // namespace void test_app::test_entity() @@ -228,7 +242,8 @@ void test_app::test_entity() test_erasure(); test_type_name(); test_metadata(); - test_range(); + test_constraints(); + test_erased_constraints(); } } // namespace floormat |