summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--entity/accessor.hpp77
-rw-r--r--entity/constraints.hpp3
-rw-r--r--entity/erased-constraints.hpp21
-rw-r--r--entity/metadata.hpp72
-rw-r--r--test/entity.cpp37
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