diff options
-rw-r--r-- | src/entity.hpp | 119 | ||||
-rw-r--r-- | test/entity.cpp | 35 |
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 |