From 3393e34574e1a30ffc4f7a8d24ec081d0cea6170 Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Sat, 19 Nov 2022 12:38:40 +0100 Subject: entity: wip optional arguments --- entity/accessor.hpp | 10 +++--- entity/constraints.hpp | 78 ++++++++++++++++++++++++++++++++++++++++++ entity/metadata.hpp | 92 +++++++++++++++++++++++++++++++------------------- test/entity.cpp | 18 +++++----- 4 files changed, 151 insertions(+), 47 deletions(-) create mode 100644 entity/constraints.hpp diff --git a/entity/accessor.hpp b/entity/accessor.hpp index 08f5ddaf..f069cb5b 100644 --- a/entity/accessor.hpp +++ b/entity/accessor.hpp @@ -5,6 +5,8 @@ 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; @@ -18,14 +20,14 @@ struct erased_accessor final { 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*); - bool(*predicate_fun)(const Object*, const erased_predicate_t*); + field_status(*predicate_fun)(const Object*, const erased_predicate_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, 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*), - bool(*predicate_fun)(const Object*, const erased_predicate_t*)) : + field_status(*predicate_fun)(const Object*, const erased_predicate_t*)) : reader{reader}, writer{writer}, predicate{predicate}, 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} @@ -44,7 +46,7 @@ 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 bool is_enabled(const Obj& x) const noexcept; + template field_status is_enabled(const Obj& x) const noexcept; constexpr bool can_write() const noexcept { return writer != nullptr; } }; @@ -116,7 +118,7 @@ void erased_accessor::write(Obj& x, move_qualified value) const noexc } template -bool erased_accessor::is_enabled(const Obj& x) const noexcept +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; diff --git a/entity/constraints.hpp b/entity/constraints.hpp new file mode 100644 index 00000000..21d549c5 --- /dev/null +++ b/entity/constraints.hpp @@ -0,0 +1,78 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace floormat::entities::erased_constraints { + +static_assert(sizeof(std::size_t) == sizeof(std::uintptr_t)); +static_assert(sizeof(std::size_t) == sizeof(std::ptrdiff_t)); + +struct range final { + using U = std::size_t; + using I = std::make_signed_t; + enum type_ : unsigned char { type_none, type_float, type_uint, type_int, }; + union element { + float f; + U u; + I i; + }; + + element min {.i = 0}, max {.i = 0}; + type_ type = type_none; + + template constexpr std::pair convert() const; +}; + +template constexpr std::pair range::convert() const +{ + using limits = std::numeric_limits; + switch (type) { + case type_float: + if constexpr (limits::is_integer()) + return { T(std::floor(min.f)), T(std::ceil(max.f)) }; + else + return { min.f, max.f }; + case type_uint: return { std::max(T(min.u), limits::min()), std::min(T(max.u), limits::max()) }; + case type_int: return { std::max(T(min.i), limits::min()), std::min(T(max.i), limits::max()) }; + default: case type_none: return { limits::min(), limits::max() }; + } +} + +struct length final { + std::size_t value = (std::size_t)-1; +}; + +struct group final { + StringView group_name; +}; + +} // namespace floormat::entities::erased_constraints + +namespace floormat::entities::constraints { + +template struct range { + using limits = std::numeric_limits; + T min = limits::min(), max = limits::max(); + + constexpr operator erased_constraints::range() const; +}; + +template constexpr range::operator erased_constraints::range() const +{ + using enum erased_constraints::range::type_; + if constexpr (std::is_floating_point_v) + return { { .f = min }, { .f = max }, type_float }; + if constexpr (std::is_integral_v && std::is_unsigned_v) + return { {.u = min}, {.u = max}, type_uint }; + if constexpr (std::is_integral_v && std::is_signed_v) + return { {.i = min}, {.i = max}, type_int }; + return { {}, {}, type_none }; +} + +using length = erased_constraints::length; +using group = erased_constraints::group; + +} // namespace floormat::entities::constraints diff --git a/entity/metadata.hpp b/entity/metadata.hpp index dc7d9f1d..cbfcd38a 100644 --- a/entity/metadata.hpp +++ b/entity/metadata.hpp @@ -1,10 +1,12 @@ #pragma once #include "name-of.hpp" #include "accessor.hpp" +#include "constraints.hpp" #include "util.hpp" #include #include #include +#include #include #include #include @@ -58,11 +60,6 @@ concept FieldWriter = requires { std::same_as; }; -template -concept FieldPredicate = requires { - requires FieldReader || std::same_as; -}; - namespace detail { template R> @@ -142,22 +139,53 @@ template using decay_tuple = typename decay_tuple_::type; template struct accessors_for_ { using type = decay_tuple>; }; template using accessors_for = typename accessors_for_::type; +template struct find_reader_; + +template struct find_reader_ { + using type = Default; + static constexpr std::size_t index = I; +}; + +template +struct find_reader_ { + using type = typename find_reader_::type; + static constexpr std::size_t index = find_reader_::index; +}; + +template +requires FieldReader +struct find_reader_ { using type = F; static constexpr std::size_t index = I; }; + +template +using find_reader = typename find_reader_...>::type; + +template +constexpr std::size_t find_reader_index = find_reader_...>::index; + +template +constexpr auto constantly = [](const Obj&) constexpr { return constant; }; + + } // namespace detail template struct entity_field_base {}; -template R, FieldWriter W, FieldPredicate P = std::nullptr_t> +template R, FieldWriter W, typename... Ts> struct entity_field : entity_field_base { +private: + static constexpr auto default_predicate = detail::constantly; + using default_predicate_t = std::decay_t; +public: using ObjectType = Obj; using FieldType = Type; using Reader = R; using Writer = W; - using Predicate = P; + using Predicate = std::decay_t>; StringView name; [[no_unique_address]] R reader; [[no_unique_address]] W writer; - [[no_unique_address]] P predicate; + [[no_unique_address]] Predicate predicate; constexpr entity_field(const entity_field&) = default; constexpr entity_field& operator=(const entity_field&) = default; @@ -165,25 +193,29 @@ struct entity_field : entity_field_base { static constexpr void write(const W& writer, Obj& x, move_qualified 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); } - static constexpr bool can_write = !std::is_same_v::writer)>; - static constexpr bool is_enabled(const P& p, const Obj& x); - constexpr entity_field(StringView name, R r, W w, P p = nullptr) noexcept : name{name}, reader{r}, writer{w}, predicate{p} {} + static constexpr bool can_write = !std::is_same_v::writer)>; + static constexpr field_status is_enabled(const Predicate & p, const Obj& 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))} + {} constexpr erased_accessor erased() const; }; -template R, FieldWriter W, FieldPredicate P> -constexpr void entity_field::write(const W& writer, Obj& x, move_qualified v) +template R, FieldWriter W, typename... Ts> +constexpr void entity_field::write(const W& writer, Obj& x, move_qualified v) { static_assert(can_write); detail::write_field::write(x, writer, v); } -template R, FieldWriter W, FieldPredicate P> -constexpr erased_accessor entity_field::erased() const +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 ; 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); @@ -202,29 +234,21 @@ constexpr erased_accessor entity_field::erased() const const auto& predicate_ = *reinterpret_cast(predicate); return is_enabled(predicate_, obj_); }; - constexpr auto predicate_stub_fn = [](const void*, const predicate_t*) { - return true; - }; 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, - !std::is_same_v ? (const void*)&predicate : nullptr, + (void*)&reader, writer ? (void*)&writer : nullptr, (void*)&predicate, name, obj_name, field_name, - reader_fn, writer ? writer_fn : writer_stub_fn, - !std::is_same_v ? predicate_fn : predicate_stub_fn, + reader_fn, writer ? writer_fn : writer_stub_fn, predicate_fn, }; } -template R, FieldWriter W, FieldPredicate P> -constexpr bool entity_field::is_enabled(const P& p, const Obj& x) +template R, FieldWriter W, typename... Ts> +constexpr field_status entity_field::is_enabled(const Predicate& p, const Obj& x) { - if constexpr(std::is_same_v) - return true; - else - return detail::read_field::read(x, p); + return detail::read_field::read(x, p); } template @@ -234,16 +258,16 @@ struct Entity final { template struct type final { - template R, FieldWriter W, FieldPredicate P = std::nullptr_t> - struct field final : entity_field + template R, FieldWriter W, typename... Ts> + struct field final : entity_field { - constexpr field(StringView field_name, R r, W w, P p = nullptr) noexcept : - entity_field{field_name, r, w, p} + constexpr field(StringView field_name, R r, W w, Ts&&... ts) noexcept : + entity_field{field_name, r, w, std::forward(ts)...} {} }; - template R, FieldWriter W, FieldPredicate P = std::nullptr_t> - field(StringView name, R r, W w, P p = nullptr) -> field; + template R, FieldWriter W, typename... Ts> + field(StringView name, R r, W w, Ts&&... ts) -> field; }; }; diff --git a/test/entity.cpp b/test/entity.cpp index a4dd4fcb..ea9e928f 100644 --- a/test/entity.cpp +++ b/test/entity.cpp @@ -28,7 +28,7 @@ constexpr auto TestAccessors::accessors() noexcept constexpr auto tuple = std::make_tuple( entity::type::field{"foo"_s, &TestAccessors::foo, &TestAccessors::foo}, entity::type::field{"bar"_s, &TestAccessors::bar, &TestAccessors::set_bar}, - entity::type::field("baz"_s, r_baz, w_baz) + entity::type::field("baz"_s, r_baz, w_baz, std::tuple<>{}) ); return tuple; } @@ -159,16 +159,16 @@ void test_predicate() { constexpr TestAccessors x{0, 0, 0}; constexpr auto m_foo = entity::type::field{"foo"_s, &TestAccessors::foo, &TestAccessors::foo, - [](const TestAccessors&) { return false; }}; - static_assert(!m_foo.is_enabled(m_foo.predicate, x)); - fm_assert(!m_foo.erased().is_enabled(x)); + [](const TestAccessors&) { return field_status::hidden; }}; + static_assert(m_foo.is_enabled(m_foo.predicate, x) == field_status::hidden); + fm_assert(m_foo.erased().is_enabled(x) == field_status::hidden); constexpr auto m_foo2 = entity::type::field{"foo"_s, &TestAccessors::foo, &TestAccessors::foo, - [](const TestAccessors&) { return true; }}; - static_assert(m_foo2.is_enabled(m_foo2.predicate, x)); - fm_assert(m_foo2.erased().is_enabled(x)); + [](const TestAccessors&) { return field_status::readonly; }}; + static_assert(m_foo2.is_enabled(m_foo2.predicate, x) == field_status::readonly); + fm_assert(m_foo2.erased().is_enabled(x) == field_status::readonly); constexpr auto m_foo3 = entity::type::field{"foo"_s, &TestAccessors::foo, &TestAccessors::foo}; - static_assert(m_foo3.is_enabled(m_foo3.predicate, x)); - fm_assert(m_foo3.erased().is_enabled(x)); + static_assert(m_foo3.is_enabled(m_foo3.predicate, x) == field_status::enabled); + fm_assert(m_foo3.erased().is_enabled(x) == field_status::enabled); } constexpr bool test_names() -- cgit v1.2.3