summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/entity.hpp119
-rw-r--r--test/entity.cpp35
2 files changed, 154 insertions, 0 deletions
diff --git a/src/entity.hpp b/src/entity.hpp
new file mode 100644
index 00000000..b63d13e8
--- /dev/null
+++ b/src/entity.hpp
@@ -0,0 +1,119 @@
+#pragma once
+#include "compat/integer-types.hpp"
+#include <type_traits>
+#include <concepts>
+#include <functional>
+
+#include <Corrade/Containers/StringView.h>
+
+namespace floormat {}
+
+namespace floormat::entities {
+
+template<typename T> using const_qualified = std::conditional_t<std::is_fundamental_v<T>, T, const T&>;
+template<typename T> using ref_qualified = std::conditional_t<std::is_fundamental_v<T>, T, T&>;
+template<typename T> using move_qualified = std::conditional_t<std::is_fundamental_v<T>, T, T&&>;
+
+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>;
+};
+
+template<typename Obj, typename Type, typename R>
+struct read_field {
+ static Type read(const Obj& x, R r) { return r(x); }
+};
+
+template<typename Obj, typename Type>
+struct read_field<Obj, Type, Type (Obj::*)() const> {
+ static 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 Type read(const Obj& x, Type Obj::*r) { return x.*r; }
+};
+
+template<typename Obj, typename FieldType, typename Writer> struct write_field {
+ static FieldType write(const Obj& x, Writer w, move_qualified<FieldType> value) { return w(x, value); }
+};
+
+template<typename Obj, typename Type>
+struct write_field<Obj, Type, void(Type::*)(move_qualified<Type>)> {
+ static Type write(const Obj& x, void(Type::*w)(Type&&), move_qualified<Type> value) { return (x.*w)(value); }
+};
+
+template<typename Obj, typename Type>
+struct write_field<Obj, Type, Type Obj::*> {
+ static Type write(const Obj& x, Type Obj::* w, move_qualified<Type> value) { return x.*w = value; }
+};
+
+template<typename Obj, typename FieldType, FieldReader<Obj, FieldType> R, FieldWriter<Obj, FieldType> W>
+struct field {
+ using Object = Obj;
+ using Type = FieldType;
+ using Reader = R;
+ using Writer = W;
+
+ StringView name;
+ Reader reader;
+ Writer writer;
+
+ constexpr field(StringView name, Reader r, Writer w) noexcept : name{name}, reader{r}, writer{w} {}
+ decltype(auto) read(const Obj& x) const { return read_field<Obj, FieldType, R>::read(x, reader); }
+ void write(Obj& x, move_qualified<FieldType> v) const { return write_field<Obj, FieldType, R>::write(x, v); }
+};
+
+template<typename Obj>
+struct entity {
+ template<typename FieldType>
+ struct Field {
+ template<FieldReader<Obj, FieldType> R, FieldWriter<Obj, FieldType> W>
+ struct make final : field<Obj, FieldType, R, W> {
+ consteval make(StringView name_, R r, W w) noexcept : field<Obj, FieldType, R, W>{name_, r, w} {}
+ };
+ template<FieldReader<Obj, FieldType> R, FieldWriter<Obj, FieldType> W>
+ make(StringView name, R r, W w) -> make<R, W>;
+ };
+};
+
+} // namespace floormat::entities
diff --git a/test/entity.cpp b/test/entity.cpp
new file mode 100644
index 00000000..9cf0e617
--- /dev/null
+++ b/test/entity.cpp
@@ -0,0 +1,35 @@
+#include "app.hpp"
+#include "compat/assert.hpp"
+#include "src/entity.hpp"
+
+namespace floormat {
+
+using namespace floormat::entities;
+
+static void test()
+{
+ struct Test {
+ int foo = 111;
+ int bar() const { return _bar; }
+ void set_bar(int value) { _bar = value; }
+ int _baz = 222;
+ private:
+ int _bar = 333;
+ };
+
+ using Entity = entity<Test>;
+ constexpr auto m_foo = Entity::Field<int>::make{ "foo"_s, &Test::foo, &Test::foo, };
+ constexpr auto m_bar = Entity::Field<int>::make{ "bar"_s, &Test::bar, &Test::set_bar, };
+ constexpr auto r_baz = [](const Test& x) { return x._baz; };
+ constexpr auto w_baz = [](Test& x, int v) { x._baz = v; };
+ constexpr auto m_baz = Entity::Field<int>::make{ "baz"_s, r_baz, w_baz };
+
+ auto x = Test{};
+ fm_assert(m_foo.read(x) == 111);
+ fm_assert(m_bar.read(x) == 222);
+ fm_assert(m_baz.read(x) == 333);
+
+ return true;
+}
+
+} // namespace floormat