From b7b0195841a22f5b8801c69fa8d61c8b0a07ab06 Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Sat, 19 Nov 2022 17:47:47 +0100 Subject: entity: add the rest of erased accessors --- entity/accessor.hpp | 77 +++++++++++++++++++++++++++++++++---------- entity/constraints.hpp | 3 +- entity/erased-constraints.hpp | 21 +++++++++++- entity/metadata.hpp | 72 +++++++++++++++++++++++++--------------- 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 +#include #include +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 @@ -39,6 +65,9 @@ struct erased_accessor final { template constexpr bool check_name() const noexcept; + template + constexpr void do_asserts() const; + 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; @@ -46,8 +75,12 @@ struct erased_accessor final { 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 field_status is_enabled(const Obj& x) const noexcept; constexpr bool can_write() const noexcept { return writer != nullptr; } + template erased_constraints::range get_range(const Obj& x) const noexcept; + template erased_constraints::max_length get_max_length(const Obj& x) const noexcept; + template erased_constraints::group get_group(const Obj& x) const noexcept; }; template @@ -57,6 +90,14 @@ constexpr bool erased_accessor::check_name_static() !std::is_pointer_v && !std::is_reference_v; } +template +constexpr void erased_accessor::do_asserts() const +{ + static_assert(!std::is_pointer_v && !std::is_reference_v); + constexpr auto obj = name_of; + fm_assert(obj.data() == object_type.data() || obj == object_type); +} + template constexpr bool erased_accessor::check_name() const noexcept { @@ -120,10 +161,12 @@ void erased_accessor::write(Obj& x, move_qualified value) const noexc template field_status erased_accessor::is_enabled(const Obj& x) const noexcept { - static_assert(!std::is_pointer_v && !std::is_reference_v); - constexpr auto obj = name_of; - fm_assert(obj.data() == object_type.data() || obj == object_type); + do_asserts(); return predicate_fun(&x, predicate); } +template erased_constraints::range erased_accessor::get_range(const T& x) const noexcept { do_asserts();return range_fun(&x,range); } +template erased_constraints::max_length erased_accessor::get_max_length(const T& x) const noexcept { do_asserts();return length_fun(&x,length); } +template erased_constraints::group erased_accessor::get_group(const T& x) const noexcept { do_asserts();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 struct range constexpr operator erased_constraints::range() const noexcept; constexpr operator std::pair() const noexcept; + constexpr bool operator==(const range&) const noexcept = default; }; template @@ -32,7 +33,7 @@ constexpr range::operator erased_constraints::range() const noexcept template constexpr range::operator std::pair() 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 constexpr std::pair 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::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 { 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::length{std::size_t(-1)}); + static constexpr auto default_c_length = constantly(constraints::max_length{std::size_t(-1)}); static constexpr auto default_c_group = constantly(StringView{}); using default_predicate_t = std::decay_t; using default_c_range_t = std::decay_t; @@ -87,7 +87,7 @@ private: using default_c_group_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; + using c_length = detail::find_reader; using c_group = detail::find_reader; 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 get_range(const Range& r, const Obj& x); - constexpr std::pair 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 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); } + 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::write(const W& writer, Obj& template R, FieldWriter W, typename... Ts> constexpr erased_accessor entity_field::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, field_name = name_of; - using P = Predicate; constexpr auto reader_fn = [](const void* obj, const reader_t* reader, void* value) { const auto& obj_ = *reinterpret_cast(obj); @@ -171,17 +173,32 @@ constexpr erased_accessor entity_field::erased() const }; constexpr auto predicate_fn = [](const void* obj, const predicate_t* predicate) { const auto& obj_ = *reinterpret_cast(obj); - const auto& predicate_ = *reinterpret_cast(predicate); + const auto& predicate_ = *reinterpret_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(*reinterpret_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(*reinterpret_cast(reader), *reinterpret_cast(obj)); + }; + constexpr auto c_group_fn = [](const void* obj, const c_group_t* reader) -> erased_constraints::group { + return get_group(*reinterpret_cast(reader), *reinterpret_cast(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::is_enabled(const Pr { return detail::read_field::read(x, p); } template R, FieldWriter W, typename... Ts> -constexpr std::pair entity_field::get_range(const Range& r, const Obj& x) +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 std::size_t entity_field::get_max_length(const Length& l, const Obj& x) -{ return detail::read_field::read(x, l); } +constexpr constraints::max_length entity_field::get_max_length(const Length& l, const Obj& x) +{ return detail::read_field::read(x, l); } template R, FieldWriter W, typename... Ts> -constexpr StringView entity_field::get_group(const Group& g, const Obj& x) +constexpr constraints::group entity_field::get_group(const Group& g, const Obj& x) { return detail::read_field::read(x, g); } template @@ -244,14 +261,9 @@ constexpr bool find_in_tuple(F&& fun, Tuple&& tuple) namespace floormat { template -requires std::is_same_v> class entity_metadata final { - template - static consteval auto erased_helper(const std::tuple& tuple) - { - std::array array { std::get(tuple).erased()..., }; - return array; - } + static_assert(std::is_same_v>); + template static consteval auto erased_helper(const std::tuple& tuple); public: static constexpr StringView class_name = name_of; static constexpr std::size_t size = std::tuple_size_v>; @@ -259,4 +271,12 @@ public: static constexpr auto erased_accessors = erased_helper(accessors); }; +template +template +consteval auto entity_metadata::erased_helper(const std::tuple& tuple) +{ + std::array array { std::get(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::field{ "foo"_s, &TestAccessors::foo, &TestAccessors::foo, - constantly(constraints::length{42}), + constantly(constraints::max_length{42}), constantly(constraints::range{37, 42}), constantly(constraints::group{"foo"_s}) }; - using limits = std::numeric_limits; - - static_assert(foo.get_range(x) == std::pair{37, 42}); + static_assert(foo.get_range(x) == constraints::range{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{limits::min(), limits::max()}); + static_assert(m_foo.get_range(x) == constraints::range{}); 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::field{ + constexpr auto foo2 = entity::type::field { "foo"_s, &TestAccessors::foo, &TestAccessors::foo, - constantly(constraints::length{123}), + constantly(constraints::max_length {123}), }; - static_assert(foo2.get_range(x) == std::pair{limits::min(), limits::max()}); + static_assert(foo2.get_range(x) == constraints::range{}); 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::field{ + "foo"_s, &TestAccessors::foo, &TestAccessors::foo, + constantly(constraints::max_length{42}), + constantly(constraints::range{37, 42}), + constantly(constraints::group{"foo"_s}) + }; + const auto x = TestAccessors{}; + const auto erased = foo.erased(); + + fm_assert(erased.get_range(x) == constraints::range{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 -- cgit v1.2.3