首次提交

This commit is contained in:
2026-08-07 16:21:44 +08:00
parent 396311246c
commit 1e903dfe1e
33 changed files with 5960 additions and 1 deletions
@@ -0,0 +1,125 @@
#pragma once
#include <concepts>
#include <functional>
#include <type_traits>
#include <utility>
namespace structive {
template <class>
struct Member_Pointer_Traits;
template <class Object, class Value>
struct Member_Pointer_Traits<Value Object::*> {
using object_type = Object;
using value_type = Value;
};
template <class>
struct Member_Function_Traits;
template <class Object, class Return, class... Args>
struct Member_Function_Traits<Return (Object::*)(Args...)> {
using object_type = Object;
using return_type = Return;
};
template <class Object, class Return, class... Args>
struct Member_Function_Traits<Return (Object::*)(Args...) const> {
using object_type = Object;
using return_type = Return;
};
template <class Object, class Return, class... Args>
struct Member_Function_Traits<Return (Object::*)(Args...) noexcept> : Member_Function_Traits<Return (Object::*)(Args...)> {};
template <class Object, class Return, class... Args>
struct Member_Function_Traits<Return (Object::*)(Args...) const noexcept> : Member_Function_Traits<Return (Object::*)(Args...) const> {};
template <auto Member>
struct Member_Storage_Identity {};
template <auto Getter>
concept Trusted_Getter_Function = requires {
typename Member_Function_Traits<decltype(Getter)>::object_type;
} && requires(const typename Member_Function_Traits<decltype(Getter)>::object_type& object) {
std::invoke(Getter, object);
} && (!std::is_void_v<std::invoke_result_t<decltype(Getter), const typename Member_Function_Traits<decltype(Getter)>::object_type&>>) && (!std::is_rvalue_reference_v<std::invoke_result_t<decltype(Getter), const typename Member_Function_Traits<decltype(Getter)>::object_type&>>) && (!(std::is_lvalue_reference_v<std::invoke_result_t<decltype(Getter), const typename Member_Function_Traits<decltype(Getter)>::object_type&>> && !std::is_const_v<std::remove_reference_t<std::invoke_result_t<decltype(Getter), const typename Member_Function_Traits<decltype(Getter)>::object_type&>>>));
template <auto Getter>
using trusted_getter_object_t = typename Member_Function_Traits<decltype(Getter)>::object_type;
template <auto Getter>
using trusted_getter_value_t = std::remove_cvref_t<std::invoke_result_t<decltype(Getter), const trusted_getter_object_t<Getter>&>>;
template <auto Setter, class Object, class Value>
concept Trusted_Setter_Function_For = requires {
typename Member_Function_Traits<decltype(Setter)>::object_type;
} && std::same_as<typename Member_Function_Traits<decltype(Setter)>::object_type, Object> && requires(Object& object, Value value) {
{ std::invoke(Setter, object, std::move(value)) } -> std::same_as<void>;
};
template <auto Member>
struct Member_Accessor {
using traits = Member_Pointer_Traits<decltype(Member)>;
using object_type = typename traits::object_type;
using value_type = typename traits::value_type;
using storage_identity = Member_Storage_Identity<Member>;
static constexpr bool readable = true;
static constexpr bool writable = !std::is_const_v<value_type>;
static constexpr bool synchronized_view_read = false;
static constexpr bool trusted_object_access = false;
static constexpr auto member = Member;
constexpr const value_type& read(const object_type& object) const {
return object.*Member;
}
constexpr value_type& read(object_type& object) const {
return object.*Member;
}
template <class Value>
constexpr void write(object_type& object, Value&& value) const requires writable && std::assignable_from<value_type&, Value> {
object.*Member = std::forward<Value>(value);
}
};
template <class Object, class Value, class Function>
struct Synchronized_Computed_Accessor {
using object_type = Object;
using value_type = Value;
using storage_identity = void;
static constexpr bool readable = true;
static constexpr bool writable = false;
static constexpr bool synchronized_view_read = true;
static constexpr bool trusted_object_access = false;
[[no_unique_address]] Function function;
template <class View>
constexpr value_type read(const View& view) const requires std::invocable<const Function&, const View&> && std::constructible_from<value_type, std::invoke_result_t<const Function&, const View&>> {
return value_type(std::invoke(function, view));
}
};
template <auto Getter> requires Trusted_Getter_Function<Getter>
struct Trusted_Computed_Accessor {
using object_type = trusted_getter_object_t<Getter>;
using value_type = trusted_getter_value_t<Getter>;
using storage_identity = void;
static constexpr bool readable = true;
static constexpr bool writable = false;
static constexpr bool synchronized_view_read = false;
static constexpr bool trusted_object_access = true;
constexpr decltype(auto) read(const object_type& object) const noexcept(std::is_nothrow_invocable_v<decltype(Getter), const object_type&>) {
return std::invoke(Getter, object);
}
};
template <auto Getter, auto Setter> requires Trusted_Getter_Function<Getter> && Trusted_Setter_Function_For<Setter, trusted_getter_object_t<Getter>, trusted_getter_value_t<Getter>>
struct Trusted_Getter_Setter_Accessor {
using object_type = trusted_getter_object_t<Getter>;
using value_type = trusted_getter_value_t<Getter>;
using storage_identity = void;
static constexpr bool readable = true;
static constexpr bool writable = true;
static constexpr bool synchronized_view_read = false;
static constexpr bool trusted_object_access = true;
constexpr decltype(auto) read(const object_type& object) const noexcept(std::is_nothrow_invocable_v<decltype(Getter), const object_type&>) {
return std::invoke(Getter, object);
}
template <class Value>
constexpr void write(object_type& object, Value&& value) const requires std::invocable<decltype(Setter), object_type&, Value> && std::same_as<std::invoke_result_t<decltype(Setter), object_type&, Value>, void> {
std::invoke(Setter, object, std::forward<Value>(value));
}
};
template <class Accessor>
concept Property_Accessor = requires {
typename Accessor::object_type;
typename Accessor::value_type;
typename Accessor::storage_identity;
{ Accessor::readable } -> std::convertible_to<bool>;
{ Accessor::writable } -> std::convertible_to<bool>;
{ Accessor::synchronized_view_read } -> std::convertible_to<bool>;
{ Accessor::trusted_object_access } -> std::convertible_to<bool>;
};
}
@@ -0,0 +1,133 @@
#pragma once
#include "fixed_string.hpp"
#include <cmath>
#include <concepts>
#include <string_view>
#include <type_traits>
#include <utility>
namespace structive {
struct Key_Category {};
struct Access_Category {};
struct Persistence_Category {};
struct Unit_Category {};
struct Sensitive_Category {};
enum class External_Access {
none,
read,
write,
read_write
};
enum class Persistence_Access {
none,
load,
store,
load_store
};
template <Fixed_String Value>
struct Key_Attribute {
using attribute_category = Key_Category;
static constexpr bool single_valued = true;
static constexpr bool inheritable = false;
static constexpr auto value = Value;
};
template <External_Access Value>
struct External_Access_Attribute {
using attribute_category = Access_Category;
static constexpr bool single_valued = true;
static constexpr bool inheritable = true;
static constexpr auto value = Value;
};
template <Persistence_Access Value>
struct Persistence_Access_Attribute {
using attribute_category = Persistence_Category;
static constexpr bool single_valued = true;
static constexpr bool inheritable = true;
static constexpr auto value = Value;
};
template <Fixed_String Value>
struct Unit_Attribute {
using attribute_category = Unit_Category;
static constexpr bool single_valued = true;
static constexpr bool inheritable = false;
static constexpr auto value = Value;
};
template <bool Value>
struct Sensitive_Attribute {
using attribute_category = Sensitive_Category;
static constexpr bool single_valued = true;
static constexpr bool inheritable = true;
static constexpr auto value = Value;
};
template <auto Value>
struct Min_Constraint {
using property_constraint_tag = void;
static constexpr bool inheritable = false;
static constexpr auto code = Fixed_String{"min_value"};
template <class T>
constexpr bool validate(const T& candidate) const requires requires { candidate >= Value; } {
return candidate >= Value;
}
};
template <auto Value>
struct Max_Constraint {
using property_constraint_tag = void;
static constexpr bool inheritable = false;
static constexpr auto code = Fixed_String{"max_value"};
template <class T>
constexpr bool validate(const T& candidate) const requires requires { candidate <= Value; } {
return candidate <= Value;
}
};
struct Finite_Constraint {
using property_constraint_tag = void;
static constexpr bool inheritable = false;
static constexpr auto code = Fixed_String{"finite"};
template <std::floating_point T>
bool validate(T candidate) const {
return std::isfinite(candidate);
}
};
template <Fixed_String Code, class Function>
struct Custom_Constraint {
using property_constraint_tag = void;
static constexpr bool inheritable = false;
static constexpr auto code = Code;
[[no_unique_address]] Function function;
template <class T>
constexpr bool validate(const T& candidate) const requires std::predicate<const Function&, const T&> {
return static_cast<bool>(function(candidate));
}
};
template <Fixed_String Value>
inline constexpr Key_Attribute<Value> key{};
template <External_Access Value>
inline constexpr External_Access_Attribute<Value> external_access{};
template <Persistence_Access Value>
inline constexpr Persistence_Access_Attribute<Value> persistence_access{};
template <Fixed_String Value>
inline constexpr Unit_Attribute<Value> unit{};
template <bool Value = true>
inline constexpr Sensitive_Attribute<Value> sensitive{};
template <auto Value>
inline constexpr Min_Constraint<Value> min_value{};
template <auto Value>
inline constexpr Max_Constraint<Value> max_value{};
inline constexpr Finite_Constraint finite{};
template <Fixed_String Code, class Function>
constexpr auto constraint(Function&& function) {
return Custom_Constraint<Code, std::decay_t<Function>>{std::forward<Function>(function)};
}
template <class Attribute>
concept Property_Constraint = requires {
typename Attribute::property_constraint_tag;
{ Attribute::code.view() } -> std::convertible_to<std::string_view>;
};
template <class Attribute, class Value>
concept Property_Constraint_For = Property_Constraint<Attribute> && requires(const Attribute& attribute, const Value& value) {
{ attribute.validate(value) } -> std::convertible_to<bool>;
};
template <class Attribute>
concept Inheritable_Attribute = requires {
{ Attribute::inheritable } -> std::convertible_to<bool>;
} && Attribute::inheritable;
}
@@ -0,0 +1,101 @@
#pragma once
#include "accessor.hpp"
#include "attributes.hpp"
#include "meta.hpp"
#include <cstddef>
#include <tuple>
#include <string_view>
#include <type_traits>
#include <utility>
namespace structive {
template <class Value, class... Attributes>
consteval bool constraints_compatible() {
return (([] {
if constexpr (Property_Constraint<Attributes>) {
return Property_Constraint_For<Attributes, Value>;
}
return true;
}()) && ...);
}
template <Property_Accessor Accessor, class... Attributes>
struct Property_Descriptor {
using property_descriptor_tag = void;
using accessor_type = Accessor;
using object_type = typename Accessor::object_type;
using value_type = typename Accessor::value_type;
using storage_identity = typename Accessor::storage_identity;
using attribute_types = Type_List<Attributes...>;
static constexpr bool readable = Accessor::readable;
static constexpr bool writable = Accessor::writable;
static constexpr bool synchronized_view_read = Accessor::synchronized_view_read;
static constexpr bool trusted_object_access = Accessor::trusted_object_access;
static_assert(unique_single_value_categories<Attributes...>());
using key_type = find_attribute_in_list_t<Key_Category, attribute_types>;
static_assert(!std::same_as<key_type, void>);
static_assert(key_type::value.view().size() > 0);
static_assert(constraints_compatible<value_type, Attributes...>());
[[no_unique_address]] Accessor accessor{};
std::tuple<Attributes...> attributes;
template <class Category>
using attribute_type = find_attribute_in_list_t<Category, attribute_types>;
template <class Category>
static constexpr bool has_attribute = !std::same_as<attribute_type<Category>, void>;
template <class Category>
constexpr decltype(auto) attribute() const requires has_attribute<Category> {
using target = attribute_type<Category>;
return std::get<target>(attributes);
}
constexpr std::string_view key() const noexcept {
return key_type::value.view();
}
template <class Function>
constexpr void for_each_attribute(Function&& function) const {
std::apply([&](const auto&... values) {
(function(values), ...);
}, attributes);
}
template <class Function>
constexpr void for_each_constraint(Function&& function) const {
std::apply([&](const auto&... values) {
([&] {
using type = std::remove_cvref_t<decltype(values)>;
if constexpr (Property_Constraint<type>) {
function(values);
}
}(), ...);
}, attributes);
}
};
template <class Type>
concept Property_Descriptor_Type = requires {
typename Type::property_descriptor_tag;
typename Type::object_type;
typename Type::value_type;
typename Type::accessor_type;
typename Type::storage_identity;
};
template <auto Member, class... Attributes>
constexpr auto property(Attributes&&... attributes) {
using accessor = Member_Accessor<Member>;
return Property_Descriptor<accessor, std::decay_t<Attributes>...>{{}, {std::forward<Attributes>(attributes)...}};
}
template <auto Member, class... Attributes>
constexpr auto field(Attributes&&... attributes) {
return property<Member>(std::forward<Attributes>(attributes)...);
}
template <class Object, class Value, class Function, class... Attributes>
constexpr auto computed_property(Function&& function, Attributes&&... attributes) {
using accessor = Synchronized_Computed_Accessor<Object, Value, std::decay_t<Function>>;
return Property_Descriptor<accessor, std::decay_t<Attributes>...>{accessor{std::forward<Function>(function)}, {std::forward<Attributes>(attributes)...}};
}
template <auto Getter, class... Attributes> requires Trusted_Getter_Function<Getter>
constexpr auto trusted_computed_property(Attributes&&... attributes) {
using accessor = Trusted_Computed_Accessor<Getter>;
return Property_Descriptor<accessor, std::decay_t<Attributes>...>{{}, {std::forward<Attributes>(attributes)...}};
}
template <auto Getter, auto Setter, class... Attributes> requires Trusted_Getter_Function<Getter> && Trusted_Setter_Function_For<Setter, trusted_getter_object_t<Getter>, trusted_getter_value_t<Getter>>
constexpr auto trusted_accessor_property(Attributes&&... attributes) {
using accessor = Trusted_Getter_Setter_Accessor<Getter, Setter>;
return Property_Descriptor<accessor, std::decay_t<Attributes>...>{{}, {std::forward<Attributes>(attributes)...}};
}
}
@@ -0,0 +1,18 @@
#pragma once
#include <cstddef>
#include <string_view>
namespace structive {
template <std::size_t N>
struct Fixed_String {
char value[N]{};
consteval Fixed_String(const char (&text)[N]) {
for (std::size_t i = 0; i < N; ++i) {
value[i] = text[i];
}
}
constexpr std::string_view view() const {
return {value, N - 1};
}
constexpr auto operator<=>(const Fixed_String&) const = default;
};
}
+85
View File
@@ -0,0 +1,85 @@
#pragma once
#include <concepts>
#include <cstddef>
#include <tuple>
#include <type_traits>
namespace structive {
template <class... Types>
struct Type_List {};
template <class Type, class List>
struct Type_List_Contains;
template <class Type, class... Types>
struct Type_List_Contains<Type, Type_List<Types...>> : std::bool_constant<(std::same_as<Type, Types> || ...)> {};
template <class Type, class List>
inline constexpr bool type_list_contains_v = Type_List_Contains<Type, List>::value;
template <class List, class Type>
struct Type_List_Push_Unique;
template <class... Types, class Type>
struct Type_List_Push_Unique<Type_List<Types...>, Type> {
using type = std::conditional_t<(std::same_as<Type, Types> || ...), Type_List<Types...>, Type_List<Types..., Type>>;
};
template <class Input, class Output = Type_List<>>
struct Type_List_Unique;
template <class Output>
struct Type_List_Unique<Type_List<>, Output> {
using type = Output;
};
template <class Head, class... Tail, class Output>
struct Type_List_Unique<Type_List<Head, Tail...>, Output> {
using next = typename Type_List_Push_Unique<Output, Head>::type;
using type = typename Type_List_Unique<Type_List<Tail...>, next>::type;
};
template <class Type, class List>
struct Type_List_Index;
template <class Type, class... Tail>
struct Type_List_Index<Type, Type_List<Type, Tail...>> : std::integral_constant<std::size_t, 0> {};
template <class Type, class Head, class... Tail>
struct Type_List_Index<Type, Type_List<Head, Tail...>> : std::integral_constant<std::size_t, 1 + Type_List_Index<Type, Type_List<Tail...>>::value> {};
template <class Type, class List>
inline constexpr std::size_t type_list_index_v = Type_List_Index<Type, List>::value;
template <class Attribute, class = void>
struct Attribute_Category_Of {
using type = void;
};
template <class Attribute>
struct Attribute_Category_Of<Attribute, std::void_t<typename Attribute::attribute_category>> {
using type = typename Attribute::attribute_category;
};
template <class Attribute>
using attribute_category_of_t = typename Attribute_Category_Of<Attribute>::type;
template <class Category, class... Attributes>
struct Find_Attribute_By_Category;
template <class Category>
struct Find_Attribute_By_Category<Category> {
using type = void;
};
template <class Category, class Head, class... Tail>
struct Find_Attribute_By_Category<Category, Head, Tail...> {
using type = std::conditional_t<std::same_as<Category, attribute_category_of_t<Head>>, Head, typename Find_Attribute_By_Category<Category, Tail...>::type>;
};
template <class Category, class List>
struct Find_Attribute_In_List;
template <class Category, class... Attributes>
struct Find_Attribute_In_List<Category, Type_List<Attributes...>> {
using type = typename Find_Attribute_By_Category<Category, Attributes...>::type;
};
template <class Category, class List>
using find_attribute_in_list_t = typename Find_Attribute_In_List<Category, List>::type;
template <class Category, class... Attributes>
consteval std::size_t count_attribute_category() {
return (std::size_t{0} + ... + (std::same_as<attribute_category_of_t<Attributes>, Category> ? 1u : 0u));
}
template <class Attribute, class... Attributes>
consteval bool unique_single_value_category_for() {
if constexpr (requires { Attribute::single_valued; }) {
if constexpr (Attribute::single_valued && !std::same_as<attribute_category_of_t<Attribute>, void>) {
return count_attribute_category<attribute_category_of_t<Attribute>, Attributes...>() == 1;
}
}
return true;
}
template <class... Attributes>
consteval bool unique_single_value_categories() {
return (unique_single_value_category_for<Attributes, Attributes...>() && ...);
}
}
@@ -0,0 +1,11 @@
#pragma once
#include "accessor.hpp"
#include "attributes.hpp"
#include "descriptor.hpp"
#include "fixed_string.hpp"
#include "meta.hpp"
#include "schema.hpp"
#include "synchronization.hpp"
#include "type_descriptor.hpp"
#include "property_object.hpp"
#include "validation.hpp"
@@ -0,0 +1,963 @@
#pragma once
#include "type_descriptor.hpp"
#include <algorithm>
#include <array>
#include <concepts>
#include <cstddef>
#include <functional>
#include <initializer_list>
#include <memory>
#include <limits>
#include <mutex>
#include <shared_mutex>
#include <span>
#include <stdexcept>
#include <string>
#include <string_view>
#include <typeinfo>
#include <type_traits>
#include <utility>
#include <vector>
namespace structive {
struct Null_Shared_Mutex {
void lock() {}
void unlock() {}
void lock_shared() {}
void unlock_shared() {}
};
struct Shared_Mutex_Policy {
using mutex_type = std::shared_mutex;
};
struct No_Lock_Policy {
using mutex_type = Null_Shared_Mutex;
};
template <class Mutex>
concept Shared_Lockable = requires(Mutex& mutex) {
mutex.lock();
mutex.unlock();
mutex.lock_shared();
mutex.unlock_shared();
};
template <class Policy>
concept Synchronization_Policy = requires {
typename Policy::mutex_type;
} && Shared_Lockable<typename Policy::mutex_type>;
enum class Managed_Access_Mode {
internal,
external,
persistence
};
enum class Runtime_Access_Result {
ok,
unknown_property,
not_readable,
not_writable,
type_mismatch
};
using Runtime_Read_Callback = void (*)(void*, std::size_t, std::string_view, const std::type_info&, const void*);
class Property_Object_Base {
struct Runtime_Interface {
const std::type_info& (*object_type)() noexcept;
std::size_t (*property_count)() noexcept;
Runtime_Access_Result (*read)(const Property_Object_Base&, Managed_Access_Mode, std::string_view, void*, Runtime_Read_Callback);
Runtime_Access_Result (*write)(Property_Object_Base&, Managed_Access_Mode, std::string_view, const std::type_info&, const void*);
};
const Runtime_Interface* runtime_interface_{};
protected:
explicit Property_Object_Base(const Runtime_Interface* runtime_interface) noexcept : runtime_interface_(runtime_interface) {}
Property_Object_Base(const Property_Object_Base&) noexcept = default;
Property_Object_Base(Property_Object_Base&&) noexcept = default;
Property_Object_Base& operator=(const Property_Object_Base&) noexcept = default;
Property_Object_Base& operator=(Property_Object_Base&&) noexcept = default;
~Property_Object_Base() = default;
template <class Object, Synchronization_Policy Policy>
friend class Property_Object;
public:
const std::type_info& runtime_object_type() const noexcept {
return runtime_interface_->object_type();
}
std::size_t runtime_property_count() const noexcept {
return runtime_interface_->property_count();
}
Runtime_Access_Result runtime_read(Managed_Access_Mode mode, std::string_view key, void* context, Runtime_Read_Callback callback) const {
return runtime_interface_->read(*this, mode, key, context, callback);
}
Runtime_Access_Result runtime_write(Managed_Access_Mode mode, std::string_view key, const std::type_info& value_type, const void* value) {
return runtime_interface_->write(*this, mode, key, value_type, value);
}
};
template <Managed_Access_Mode Mode, class Schema, std::size_t Index>
inline constexpr bool property_read_allowed_v = Mode == Managed_Access_Mode::internal ? Schema::template property_type<Index>::readable : Mode == Managed_Access_Mode::external ? external_readable_v<Schema, Index> : persistence_storable_v<Schema, Index>;
template <Managed_Access_Mode Mode, class Schema, std::size_t Index>
inline constexpr bool property_write_allowed_v = Mode == Managed_Access_Mode::internal ? Schema::template property_type<Index>::writable : Mode == Managed_Access_Mode::external ? external_writable_v<Schema, Index> : persistence_loadable_v<Schema, Index>;
template <Managed_Access_Mode Mode, class Schema, std::size_t Index>
inline constexpr bool property_visible_v = property_read_allowed_v<Mode, Schema, Index> || property_write_allowed_v<Mode, Schema, Index>;
struct Property_Synchronization {
Synchronization_Plan plan;
};
inline Property_Synchronization property_synchronization(Synchronization_Plan plan) {
return {std::move(plan)};
}
template <class Object, Synchronization_Source_Type Source>
Property_Synchronization property_synchronization(Source&& source) requires Property_Described_Object<Object> {
using Schema = type_descriptor_schema_t<Object>;
return {materialize_synchronization_plan<Schema>(std::forward<Source>(source))};
}
struct Resolved_Synchronization_View {
static constexpr std::size_t unsynchronized_slot = std::numeric_limits<std::size_t>::max();
std::span<const std::size_t> lock_slots;
std::size_t lock_count{};
std::size_t slot(std::size_t index) const noexcept {
return lock_slots[index];
}
bool uses_lock(std::size_t index) const noexcept {
return slot(index) != unsynchronized_slot;
}
};
template <class Derived, Synchronization_Policy Lock_Policy = Shared_Mutex_Policy>
class Property_Object : public Property_Object_Base {
public:
using object_type = Derived;
using mutex_type = typename Lock_Policy::mutex_type;
private:
struct Dynamic_Lock_Targets {
std::vector<std::size_t> slots;
std::vector<std::size_t> unsynchronized_properties;
};
template <std::size_t Capacity>
struct Static_Lock_Targets {
std::array<std::size_t, Capacity> slots{};
std::size_t slot_count{};
std::array<std::size_t, Capacity> unsynchronized_properties{};
std::size_t unsynchronized_count{};
};
std::vector<std::size_t> lock_slots_;
std::size_t lock_count_{};
std::unique_ptr<mutex_type[]> locks_;
template <class Schema>
void initialize(const Schema& schema, const Synchronization_Plan& plan) {
auto resolved = resolve_synchronization_plan(schema, plan);
lock_slots_.assign(resolved.lock_slots.begin(), resolved.lock_slots.end());
lock_count_ = resolved.lock_count;
locks_ = lock_count_ ? std::make_unique<mutex_type[]>(lock_count_) : nullptr;
}
void copy_synchronization_from(const Property_Object& other) {
lock_slots_ = other.lock_slots_;
lock_count_ = other.lock_count_;
locks_ = lock_count_ ? std::make_unique<mutex_type[]>(lock_count_) : nullptr;
}
std::size_t slot(std::size_t index) const noexcept {
return lock_slots_[index];
}
template <std::size_t Capacity>
static void sort_unique_prefix(std::array<std::size_t, Capacity>& values, std::size_t& count) {
for (std::size_t index = 1; index < count; ++index) {
auto value = values[index];
auto position = index;
while (position > 0 && value < values[position - 1]) {
values[position] = values[position - 1];
--position;
}
values[position] = value;
}
if (count == 0) {
return;
}
std::size_t write = 1;
for (std::size_t read = 1; read < count; ++read) {
if (values[read] != values[write - 1]) {
values[write++] = values[read];
}
}
count = write;
}
template <Managed_Access_Mode Mode>
static bool runtime_property_readable(std::size_t index) {
using Schema = type_descriptor_schema_t<Derived>;
static const auto table = []<std::size_t... Index>(std::index_sequence<Index...>) {
return std::array<bool, Schema::property_count>{property_read_allowed_v<Mode, Schema, Index>...};
}(std::make_index_sequence<Schema::property_count>{});
return table[index];
}
template <Managed_Access_Mode Mode>
static bool runtime_property_visible(std::size_t index) {
using Schema = type_descriptor_schema_t<Derived>;
static const auto table = []<std::size_t... Index>(std::index_sequence<Index...>) {
return std::array<bool, Schema::property_count>{property_visible_v<Mode, Schema, Index>...};
}(std::make_index_sequence<Schema::property_count>{});
return table[index];
}
template <Managed_Access_Mode Mode>
Dynamic_Lock_Targets collect_dynamic_lock_targets(std::span<const std::string_view> keys, bool shared_access) const {
using Schema = type_descriptor_schema_t<Derived>;
Dynamic_Lock_Targets targets;
targets.slots.reserve(keys.size());
targets.unsynchronized_properties.reserve(keys.size());
for (auto key : keys) {
auto index = schema_property_index<Schema>(key);
if (!index) {
throw std::invalid_argument("Unknown property: " + std::string(key));
}
bool allowed = shared_access ? runtime_property_readable<Mode>(*index) : runtime_property_visible<Mode>(*index);
if (!allowed) {
throw std::invalid_argument("Property is not accessible through this view: " + std::string(key));
}
auto lock_slot = slot(*index);
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
targets.unsynchronized_properties.push_back(*index);
} else {
targets.slots.push_back(lock_slot);
}
}
std::sort(targets.slots.begin(), targets.slots.end());
targets.slots.erase(std::unique(targets.slots.begin(), targets.slots.end()), targets.slots.end());
std::sort(targets.unsynchronized_properties.begin(), targets.unsynchronized_properties.end());
targets.unsynchronized_properties.erase(std::unique(targets.unsynchronized_properties.begin(), targets.unsynchronized_properties.end()), targets.unsynchronized_properties.end());
return targets;
}
template <Managed_Access_Mode Mode, bool Shared_Access, std::size_t... Index>
Static_Lock_Targets<sizeof...(Index)> collect_static_lock_targets() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(((Shared_Access ? property_read_allowed_v<Mode, Schema, Index> : property_visible_v<Mode, Schema, Index>) && ...));
Static_Lock_Targets<sizeof...(Index)> targets;
auto collect = [&]<std::size_t Property_Index>() {
auto lock_slot = slot(Property_Index);
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
targets.unsynchronized_properties[targets.unsynchronized_count++] = Property_Index;
} else {
targets.slots[targets.slot_count++] = lock_slot;
}
};
(collect.template operator()<Index>(), ...);
sort_unique_prefix(targets.slots, targets.slot_count);
sort_unique_prefix(targets.unsynchronized_properties, targets.unsynchronized_count);
return targets;
}
template <Managed_Access_Mode Mode, bool Shared_Access>
auto collect_all_static_lock_targets() const {
using Schema = type_descriptor_schema_t<Derived>;
Static_Lock_Targets<Schema::property_count> targets;
auto collect = [&]<std::size_t Index>() {
constexpr bool allowed = Shared_Access ? property_read_allowed_v<Mode, Schema, Index> : property_write_allowed_v<Mode, Schema, Index>;
if constexpr (allowed) {
auto lock_slot = slot(Index);
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
targets.unsynchronized_properties[targets.unsynchronized_count++] = Index;
} else {
targets.slots[targets.slot_count++] = lock_slot;
}
}
};
[&]<std::size_t... Index>(std::index_sequence<Index...>) {
(collect.template operator()<Index>(), ...);
}(std::make_index_sequence<Schema::property_count>{});
sort_unique_prefix(targets.slots, targets.slot_count);
sort_unique_prefix(targets.unsynchronized_properties, targets.unsynchronized_count);
return targets;
}
template <std::size_t Index, class View>
decltype(auto) read_unlocked(const View& view) const {
using Schema = type_descriptor_schema_t<Derived>;
const auto& descriptor = type_descriptor<Derived>().template property<Index>();
using Accessor = typename Schema::template property_type<Index>::accessor_type;
if constexpr (Accessor::synchronized_view_read) {
return descriptor.accessor.read(view);
} else {
return descriptor.accessor.read(static_cast<const Derived&>(*this));
}
}
template <std::size_t Index, class Value>
void write_unlocked(Value&& value) {
const auto& descriptor = type_descriptor<Derived>().template property<Index>();
descriptor.accessor.write(static_cast<Derived&>(*this), std::forward<Value>(value));
}
template <Managed_Access_Mode Mode>
class Single_Read_View {
const Property_Object* owner_{};
std::size_t property_index_{};
std::size_t lock_slot_{};
public:
Single_Read_View(const Property_Object& owner, std::size_t property_index, std::size_t lock_slot) : owner_(&owner), property_index_(property_index), lock_slot_(lock_slot) {}
template <auto Member>
decltype(auto) get() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
if (owner_->slot(index) != lock_slot_ || (lock_slot_ == Resolved_Synchronization_View::unsynchronized_slot && index != property_index_)) {
throw std::logic_error("Computed property reads outside its synchronization slot");
}
return owner_->template read_unlocked<index>(*this);
}
template <Fixed_String Key>
decltype(auto) get_key() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
if (owner_->slot(index) != lock_slot_ || (lock_slot_ == Resolved_Synchronization_View::unsynchronized_slot && index != property_index_)) {
throw std::logic_error("Computed property reads outside its synchronization slot");
}
return owner_->template read_unlocked<index>(*this);
}
};
template <Managed_Access_Mode Mode, std::size_t Index>
auto read_one() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(property_read_allowed_v<Mode, Schema, Index>);
using Value = typename Schema::template property_type<Index>::value_type;
auto lock_slot = slot(Index);
Single_Read_View<Mode> view{*this, Index, lock_slot};
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
return Value(read_unlocked<Index>(view));
}
std::shared_lock lock{locks_[lock_slot]};
return Value(read_unlocked<Index>(view));
}
template <Managed_Access_Mode Mode, std::size_t Index, class Value>
void write_one(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(property_write_allowed_v<Mode, Schema, Index>);
auto lock_slot = slot(Index);
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
write_unlocked<Index>(std::forward<Value>(value));
return;
}
std::unique_lock lock{locks_[lock_slot]};
write_unlocked<Index>(std::forward<Value>(value));
}
public:
template <Managed_Access_Mode Mode>
class Read_Guard {
const Property_Object* owner_{};
std::vector<std::size_t> slots_;
std::vector<std::size_t> unsynchronized_properties_;
std::vector<std::shared_lock<mutex_type>> locks_;
bool holds(std::size_t index) const {
auto lock_slot = owner_->slot(index);
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
return std::binary_search(unsynchronized_properties_.begin(), unsynchronized_properties_.end(), index);
}
return std::binary_search(slots_.begin(), slots_.end(), lock_slot);
}
friend class Property_Object;
Read_Guard(const Property_Object& owner, Dynamic_Lock_Targets targets) : owner_(&owner), slots_(std::move(targets.slots)), unsynchronized_properties_(std::move(targets.unsynchronized_properties)) {
locks_.reserve(slots_.size());
for (auto lock_slot : slots_) {
locks_.emplace_back(owner_->locks_[lock_slot]);
}
}
public:
template <std::size_t Index>
decltype(auto) get_index() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(property_read_allowed_v<Mode, Schema, Index>);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
return owner_->template read_unlocked<Index>(*this);
}
template <auto Member>
decltype(auto) get() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
template <Fixed_String Key>
decltype(auto) get_key() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
};
template <Managed_Access_Mode Mode>
class Write_Guard {
Property_Object* owner_{};
std::vector<std::size_t> slots_;
std::vector<std::size_t> unsynchronized_properties_;
std::vector<std::unique_lock<mutex_type>> locks_;
bool holds(std::size_t index) const {
auto lock_slot = owner_->slot(index);
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
return std::binary_search(unsynchronized_properties_.begin(), unsynchronized_properties_.end(), index);
}
return std::binary_search(slots_.begin(), slots_.end(), lock_slot);
}
friend class Property_Object;
Write_Guard(Property_Object& owner, Dynamic_Lock_Targets targets) : owner_(&owner), slots_(std::move(targets.slots)), unsynchronized_properties_(std::move(targets.unsynchronized_properties)) {
locks_.reserve(slots_.size());
for (auto lock_slot : slots_) {
locks_.emplace_back(owner_->locks_[lock_slot]);
}
}
public:
template <std::size_t Index>
decltype(auto) get_index() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(property_read_allowed_v<Mode, Schema, Index>);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
return owner_->template read_unlocked<Index>(*this);
}
template <auto Member>
decltype(auto) get() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
template <Fixed_String Key>
decltype(auto) get_key() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
template <std::size_t Index, class Value>
void set_index(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(property_write_allowed_v<Mode, Schema, Index>);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
owner_->template write_unlocked<Index>(std::forward<Value>(value));
}
template <auto Member, class Value>
void set(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
set_index<index>(std::forward<Value>(value));
}
template <Fixed_String Key, class Value>
void set_key(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
set_index<index>(std::forward<Value>(value));
}
};
template <Managed_Access_Mode Mode, std::size_t Capacity>
class Static_Read_Guard {
const Property_Object* owner_{};
Static_Lock_Targets<Capacity> targets_;
std::array<std::shared_lock<mutex_type>, Capacity> locks_{};
bool holds(std::size_t index) const {
auto lock_slot = owner_->slot(index);
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
return std::binary_search(targets_.unsynchronized_properties.begin(), targets_.unsynchronized_properties.begin() + static_cast<std::ptrdiff_t>(targets_.unsynchronized_count), index);
}
return std::binary_search(targets_.slots.begin(), targets_.slots.begin() + static_cast<std::ptrdiff_t>(targets_.slot_count), lock_slot);
}
friend class Property_Object;
Static_Read_Guard(const Property_Object& owner, Static_Lock_Targets<Capacity> targets) : owner_(&owner), targets_(std::move(targets)) {
for (std::size_t index = 0; index < targets_.slot_count; ++index) {
locks_[index] = std::shared_lock<mutex_type>{owner_->locks_[targets_.slots[index]]};
}
}
public:
template <std::size_t Index>
decltype(auto) get_index() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(property_read_allowed_v<Mode, Schema, Index>);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
return owner_->template read_unlocked<Index>(*this);
}
template <auto Member>
decltype(auto) get() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
template <Fixed_String Key>
decltype(auto) get_key() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
};
template <Managed_Access_Mode Mode, std::size_t Capacity>
class Static_Write_Guard {
Property_Object* owner_{};
Static_Lock_Targets<Capacity> targets_;
std::array<std::unique_lock<mutex_type>, Capacity> locks_{};
bool holds(std::size_t index) const {
auto lock_slot = owner_->slot(index);
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
return std::binary_search(targets_.unsynchronized_properties.begin(), targets_.unsynchronized_properties.begin() + static_cast<std::ptrdiff_t>(targets_.unsynchronized_count), index);
}
return std::binary_search(targets_.slots.begin(), targets_.slots.begin() + static_cast<std::ptrdiff_t>(targets_.slot_count), lock_slot);
}
friend class Property_Object;
Static_Write_Guard(Property_Object& owner, Static_Lock_Targets<Capacity> targets) : owner_(&owner), targets_(std::move(targets)) {
for (std::size_t index = 0; index < targets_.slot_count; ++index) {
locks_[index] = std::unique_lock<mutex_type>{owner_->locks_[targets_.slots[index]]};
}
}
public:
template <std::size_t Index>
decltype(auto) get_index() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(property_read_allowed_v<Mode, Schema, Index>);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
return owner_->template read_unlocked<Index>(*this);
}
template <auto Member>
decltype(auto) get() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
template <Fixed_String Key>
decltype(auto) get_key() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
return get_index<index>();
}
template <std::size_t Index, class Value>
void set_index(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(property_write_allowed_v<Mode, Schema, Index>);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
owner_->template write_unlocked<Index>(std::forward<Value>(value));
}
template <auto Member, class Value>
void set(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
set_index<index>(std::forward<Value>(value));
}
template <Fixed_String Key, class Value>
void set_key(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
set_index<index>(std::forward<Value>(value));
}
};
private:
template <Managed_Access_Mode Mode, class Function>
void for_each_readable_impl(Function&& function) const {
using Schema = type_descriptor_schema_t<Derived>;
const auto& schema = type_descriptor<Derived>();
schema.for_each_property([&](auto index, const auto& descriptor) {
constexpr std::size_t property_index = decltype(index)::value;
if constexpr (property_read_allowed_v<Mode, Schema, property_index>) {
auto value = read_one<Mode, property_index>();
std::invoke(function, index, descriptor, value);
}
});
}
template <Managed_Access_Mode Mode, class Function>
void for_each_readable_locked_impl(Function&& function) const {
using Schema = type_descriptor_schema_t<Derived>;
auto targets = collect_all_static_lock_targets<Mode, true>();
Static_Read_Guard<Mode, Schema::property_count> guard{*this, std::move(targets)};
type_descriptor<Derived>().for_each_property([&](auto index, const auto& descriptor) {
constexpr std::size_t property_index = decltype(index)::value;
if constexpr (property_read_allowed_v<Mode, Schema, property_index>) {
std::invoke(function, index, descriptor, guard.template get_index<property_index>());
}
});
}
template <Managed_Access_Mode Mode, class Function>
void with_all_writable_locked_impl(Function&& function) {
using Schema = type_descriptor_schema_t<Derived>;
auto targets = collect_all_static_lock_targets<Mode, false>();
Static_Write_Guard<Mode, Schema::property_count> guard{*this, std::move(targets)};
std::invoke(std::forward<Function>(function), guard);
}
static const std::type_info& runtime_object_type_impl() noexcept {
return typeid(Derived);
}
static std::size_t runtime_property_count_impl() noexcept {
using Schema = type_descriptor_schema_t<Derived>;
return Schema::property_count;
}
template <Managed_Access_Mode Mode>
Runtime_Access_Result runtime_read_mode(std::string_view key, void* context, Runtime_Read_Callback callback) const {
using Schema = type_descriptor_schema_t<Derived>;
auto index = schema_property_index<Schema>(key);
if (!index) {
return Runtime_Access_Result::unknown_property;
}
if (!runtime_property_readable<Mode>(*index)) {
return Runtime_Access_Result::not_readable;
}
Runtime_Access_Result result = Runtime_Access_Result::unknown_property;
visit_schema_property(type_descriptor<Derived>(), key, [&](auto property_index_constant, const auto&) {
constexpr std::size_t property_index = decltype(property_index_constant)::value;
using Value = typename Schema::template property_type<property_index>::value_type;
auto lock_slot = slot(property_index);
Single_Read_View<Mode> view{*this, property_index, lock_slot};
auto emit = [&] {
if constexpr (std::is_reference_v<decltype(read_unlocked<property_index>(view))>) {
auto&& value = read_unlocked<property_index>(view);
callback(context, property_index, key, typeid(Value), std::addressof(value));
} else {
Value value = read_unlocked<property_index>(view);
callback(context, property_index, key, typeid(Value), std::addressof(value));
}
};
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
emit();
} else {
std::shared_lock lock{locks_[lock_slot]};
emit();
}
result = Runtime_Access_Result::ok;
});
return result;
}
static Runtime_Access_Result runtime_read_impl(const Property_Object_Base& base, Managed_Access_Mode mode, std::string_view key, void* context, Runtime_Read_Callback callback) {
const auto& self = static_cast<const Property_Object&>(base);
switch (mode) {
case Managed_Access_Mode::internal:
return self.template runtime_read_mode<Managed_Access_Mode::internal>(key, context, callback);
case Managed_Access_Mode::external:
return self.template runtime_read_mode<Managed_Access_Mode::external>(key, context, callback);
case Managed_Access_Mode::persistence:
return self.template runtime_read_mode<Managed_Access_Mode::persistence>(key, context, callback);
}
return Runtime_Access_Result::not_readable;
}
template <Managed_Access_Mode Mode>
Runtime_Access_Result runtime_write_mode(std::string_view key, const std::type_info& value_type, const void* value) {
using Schema = type_descriptor_schema_t<Derived>;
auto index = schema_property_index<Schema>(key);
if (!index) {
return Runtime_Access_Result::unknown_property;
}
if (!runtime_property_visible<Mode>(*index)) {
return Runtime_Access_Result::not_writable;
}
Runtime_Access_Result result = Runtime_Access_Result::unknown_property;
visit_schema_property(type_descriptor<Derived>(), key, [&](auto property_index_constant, const auto&) {
constexpr std::size_t property_index = decltype(property_index_constant)::value;
using Property = typename Schema::template property_type<property_index>;
using Accessor = typename Property::accessor_type;
using Value = typename Property::value_type;
if constexpr (!property_write_allowed_v<Mode, Schema, property_index> || !requires(const Accessor& accessor, Derived& object, const Value& candidate) { accessor.write(object, candidate); }) {
result = Runtime_Access_Result::not_writable;
} else if (value_type != typeid(Value)) {
result = Runtime_Access_Result::type_mismatch;
} else {
write_one<Mode, property_index>(*static_cast<const Value*>(value));
result = Runtime_Access_Result::ok;
}
});
return result;
}
static Runtime_Access_Result runtime_write_impl(Property_Object_Base& base, Managed_Access_Mode mode, std::string_view key, const std::type_info& value_type, const void* value) {
auto& self = static_cast<Property_Object&>(base);
switch (mode) {
case Managed_Access_Mode::internal:
return self.template runtime_write_mode<Managed_Access_Mode::internal>(key, value_type, value);
case Managed_Access_Mode::external:
return self.template runtime_write_mode<Managed_Access_Mode::external>(key, value_type, value);
case Managed_Access_Mode::persistence:
return self.template runtime_write_mode<Managed_Access_Mode::persistence>(key, value_type, value);
}
return Runtime_Access_Result::not_writable;
}
static const Property_Object_Base::Runtime_Interface* runtime_interface() noexcept {
static const Property_Object_Base::Runtime_Interface value{
&runtime_object_type_impl,
&runtime_property_count_impl,
&runtime_read_impl,
&runtime_write_impl
};
return &value;
}
protected:
Property_Object() : Property_Object_Base(runtime_interface()) {
const auto& schema = type_descriptor<Derived>();
initialize(schema, schema.synchronization_plan());
}
explicit Property_Object(Property_Synchronization synchronization) : Property_Object_Base(runtime_interface()) {
initialize(type_descriptor<Derived>(), synchronization.plan);
}
Property_Object(const Property_Object& other) : Property_Object_Base(runtime_interface()) {
copy_synchronization_from(other);
}
Property_Object(Property_Object&& other) : Property_Object_Base(runtime_interface()) {
copy_synchronization_from(other);
}
Property_Object& operator=(const Property_Object&) noexcept {
return *this;
}
Property_Object& operator=(Property_Object&&) noexcept {
return *this;
}
~Property_Object() = default;
public:
const auto& schema() const noexcept {
return type_descriptor<Derived>();
}
Resolved_Synchronization_View resolved_synchronization() const noexcept {
return {lock_slots_, lock_count_};
}
Derived& unsafe_object() noexcept {
return static_cast<Derived&>(*this);
}
const Derived& unsafe_object() const noexcept {
return static_cast<const Derived&>(*this);
}
template <auto Member>
auto read() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return read_one<Managed_Access_Mode::internal, index>();
}
template <Fixed_String Key>
auto read_key() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
return read_one<Managed_Access_Mode::internal, index>();
}
template <auto Member, class Value>
void write(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
write_one<Managed_Access_Mode::internal, index>(std::forward<Value>(value));
}
template <Fixed_String Key, class Value>
void write_key(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
write_one<Managed_Access_Mode::internal, index>(std::forward<Value>(value));
}
template <auto Member>
std::size_t lock_slot() const noexcept {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return slot(index);
}
template <Managed_Access_Mode Mode>
Read_Guard<Mode> lock_shared(std::span<const std::string_view> keys) const {
return Read_Guard<Mode>{*this, collect_dynamic_lock_targets<Mode>(keys, true)};
}
template <Managed_Access_Mode Mode>
Read_Guard<Mode> lock_shared(std::initializer_list<std::string_view> keys) const {
return lock_shared<Mode>(std::span<const std::string_view>{keys.begin(), keys.size()});
}
Read_Guard<Managed_Access_Mode::internal> lock_shared(std::span<const std::string_view> keys) const {
return lock_shared<Managed_Access_Mode::internal>(keys);
}
Read_Guard<Managed_Access_Mode::internal> lock_shared(std::initializer_list<std::string_view> keys) const {
return lock_shared<Managed_Access_Mode::internal>(keys);
}
template <Managed_Access_Mode Mode>
Write_Guard<Mode> lock_unique(std::span<const std::string_view> keys) {
return Write_Guard<Mode>{*this, collect_dynamic_lock_targets<Mode>(keys, false)};
}
template <Managed_Access_Mode Mode>
Write_Guard<Mode> lock_unique(std::initializer_list<std::string_view> keys) {
return lock_unique<Mode>(std::span<const std::string_view>{keys.begin(), keys.size()});
}
Write_Guard<Managed_Access_Mode::internal> lock_unique(std::span<const std::string_view> keys) {
return lock_unique<Managed_Access_Mode::internal>(keys);
}
Write_Guard<Managed_Access_Mode::internal> lock_unique(std::initializer_list<std::string_view> keys) {
return lock_unique<Managed_Access_Mode::internal>(keys);
}
template <Managed_Access_Mode Mode, auto... Members>
auto lock_shared_mode() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert((Schema_Property_Member<Schema, Members> && ...));
auto targets = collect_static_lock_targets<Mode, true, schema_member_property_index_v<Schema, Members>...>();
return Static_Read_Guard<Mode, sizeof...(Members)>{*this, std::move(targets)};
}
template <auto... Members>
auto lock_shared() const {
return lock_shared_mode<Managed_Access_Mode::internal, Members...>();
}
template <Managed_Access_Mode Mode, auto... Members>
auto lock_unique_mode() {
using Schema = type_descriptor_schema_t<Derived>;
static_assert((Schema_Property_Member<Schema, Members> && ...));
auto targets = collect_static_lock_targets<Mode, false, schema_member_property_index_v<Schema, Members>...>();
return Static_Write_Guard<Mode, sizeof...(Members)>{*this, std::move(targets)};
}
template <auto... Members>
auto lock_unique() {
return lock_unique_mode<Managed_Access_Mode::internal, Members...>();
}
template <class Function>
void for_each_readable(Function&& function) const {
for_each_readable_impl<Managed_Access_Mode::internal>(std::forward<Function>(function));
}
template <class Function>
void for_each_readable_locked(Function&& function) const {
for_each_readable_locked_impl<Managed_Access_Mode::internal>(std::forward<Function>(function));
}
template <class Function>
void with_all_writable_locked(Function&& function) {
with_all_writable_locked_impl<Managed_Access_Mode::internal>(std::forward<Function>(function));
}
template <Managed_Access_Mode Mode>
class Capability_View {
Property_Object* owner_{};
public:
explicit Capability_View(Property_Object& owner) : owner_(&owner) {}
template <auto Member>
auto read() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return owner_->template read_one<Mode, index>();
}
template <Fixed_String Key>
auto read_key() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
return owner_->template read_one<Mode, index>();
}
template <auto Member, class Value>
void write(Value&& value) requires (Mode != Managed_Access_Mode::persistence) {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
owner_->template write_one<Mode, index>(std::forward<Value>(value));
}
template <Fixed_String Key, class Value>
void write_key(Value&& value) requires (Mode != Managed_Access_Mode::persistence) {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
owner_->template write_one<Mode, index>(std::forward<Value>(value));
}
template <auto Member, class Value>
void load(Value&& value) requires (Mode == Managed_Access_Mode::persistence) {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
owner_->template write_one<Mode, index>(std::forward<Value>(value));
}
template <Fixed_String Key, class Value>
void load_key(Value&& value) requires (Mode == Managed_Access_Mode::persistence) {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
owner_->template write_one<Mode, index>(std::forward<Value>(value));
}
template <auto Member>
auto store() const requires (Mode == Managed_Access_Mode::persistence) {
return read<Member>();
}
template <Fixed_String Key>
auto store_key() const requires (Mode == Managed_Access_Mode::persistence) {
return read_key<Key>();
}
template <auto... Members>
auto lock_shared() const {
return owner_->template lock_shared_mode<Mode, Members...>();
}
template <auto... Members>
auto lock_unique() {
return owner_->template lock_unique_mode<Mode, Members...>();
}
Read_Guard<Mode> lock_shared(std::span<const std::string_view> keys) const {
return owner_->template lock_shared<Mode>(keys);
}
Write_Guard<Mode> lock_unique(std::span<const std::string_view> keys) {
return owner_->template lock_unique<Mode>(keys);
}
template <class Function>
void for_each_readable(Function&& function) const {
owner_->template for_each_readable_impl<Mode>(std::forward<Function>(function));
}
template <class Function>
void for_each_readable_locked(Function&& function) const {
owner_->template for_each_readable_locked_impl<Mode>(std::forward<Function>(function));
}
template <class Function>
void with_all_writable_locked(Function&& function) requires (Mode != Managed_Access_Mode::persistence) {
owner_->template with_all_writable_locked_impl<Mode>(std::forward<Function>(function));
}
template <class Function>
void with_all_loadable_locked(Function&& function) requires (Mode == Managed_Access_Mode::persistence) {
owner_->template with_all_writable_locked_impl<Mode>(std::forward<Function>(function));
}
};
template <Managed_Access_Mode Mode>
class Const_Capability_View {
const Property_Object* owner_{};
public:
explicit Const_Capability_View(const Property_Object& owner) : owner_(&owner) {}
template <auto Member>
auto read() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return owner_->template read_one<Mode, index>();
}
template <Fixed_String Key>
auto read_key() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
return owner_->template read_one<Mode, index>();
}
template <auto Member>
auto store() const requires (Mode == Managed_Access_Mode::persistence) {
return read<Member>();
}
template <Fixed_String Key>
auto store_key() const requires (Mode == Managed_Access_Mode::persistence) {
return read_key<Key>();
}
template <auto... Members>
auto lock_shared() const {
return owner_->template lock_shared_mode<Mode, Members...>();
}
Read_Guard<Mode> lock_shared(std::span<const std::string_view> keys) const {
return owner_->template lock_shared<Mode>(keys);
}
template <class Function>
void for_each_readable(Function&& function) const {
owner_->template for_each_readable_impl<Mode>(std::forward<Function>(function));
}
template <class Function>
void for_each_readable_locked(Function&& function) const {
owner_->template for_each_readable_locked_impl<Mode>(std::forward<Function>(function));
}
};
Capability_View<Managed_Access_Mode::external> external() {
return Capability_View<Managed_Access_Mode::external>{*this};
}
Capability_View<Managed_Access_Mode::persistence> persistence() {
return Capability_View<Managed_Access_Mode::persistence>{*this};
}
Const_Capability_View<Managed_Access_Mode::external> external() const {
return Const_Capability_View<Managed_Access_Mode::external>{*this};
}
Const_Capability_View<Managed_Access_Mode::persistence> persistence() const {
return Const_Capability_View<Managed_Access_Mode::persistence>{*this};
}
};
}
+430
View File
@@ -0,0 +1,430 @@
#pragma once
#include "descriptor.hpp"
#include "meta.hpp"
#include "synchronization.hpp"
#include <algorithm>
#include <array>
#include <concepts>
#include <cstddef>
#include <functional>
#include <optional>
#include <span>
#include <stdexcept>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
namespace structive {
template <class Property>
consteval std::string_view declared_property_key() {
using key_type = typename Property::template attribute_type<Key_Category>;
return key_type::value.view();
}
template <class... Properties>
consteval bool unique_property_keys() {
constexpr std::array keys{declared_property_key<Properties>()...};
for (std::size_t i = 0; i < keys.size(); ++i) {
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (keys[i] == keys[j]) {
return false;
}
}
}
return true;
}
template <class Left, class Right>
consteval bool distinct_storage_identity() {
using left = typename Left::storage_identity;
using right = typename Right::storage_identity;
if constexpr (std::same_as<left, void> || std::same_as<right, void>) {
return true;
}
return !std::same_as<left, right>;
}
template <class Tuple, std::size_t Left, std::size_t... Right>
consteval bool distinct_storage_row(std::index_sequence<Right...>) {
using left = std::tuple_element_t<Left, Tuple>;
return (distinct_storage_identity<left, std::tuple_element_t<Left + 1 + Right, Tuple>>() && ...);
}
template <class... Properties>
consteval bool unique_property_storage() {
using tuple = std::tuple<Properties...>;
if constexpr (sizeof...(Properties) < 2) {
return true;
} else {
return []<std::size_t... Left>(std::index_sequence<Left...>) {
return (distinct_storage_row<tuple, Left>(std::make_index_sequence<sizeof...(Properties) - Left - 1>{}) && ...);
}(std::make_index_sequence<sizeof...(Properties) - 1>{});
}
}
template <class... Attributes>
struct Object_Defaults {
using object_defaults_tag = void;
using attribute_types = Type_List<Attributes...>;
static_assert(unique_single_value_categories<Attributes...>());
static_assert((Inheritable_Attribute<Attributes> && ...));
std::tuple<Attributes...> attributes;
};
template <class Type>
concept Object_Defaults_Type = requires {
typename Type::object_defaults_tag;
typename Type::attribute_types;
};
template <class... Attributes>
constexpr auto defaults(Attributes&&... attributes) {
return Object_Defaults<std::decay_t<Attributes>...>{{std::forward<Attributes>(attributes)...}};
}
struct No_Defaults {
using object_defaults_tag = void;
using attribute_types = Type_List<>;
std::tuple<> attributes;
};
inline constexpr No_Defaults no_defaults{};
template <class Object, class Defaults, class... Properties>
class Object_Schema {
[[no_unique_address]] Defaults object_defaults_;
Synchronization_Plan synchronization_plan_;
std::tuple<Properties...> properties_;
public:
using property_schema_tag = void;
using object_type = Object;
using defaults_type = Defaults;
using property_types = Type_List<Properties...>;
static constexpr std::size_t property_count = sizeof...(Properties);
static_assert((std::same_as<typename Properties::object_type, Object> && ...));
static_assert(unique_property_keys<Properties...>());
static_assert(unique_property_storage<Properties...>());
Object_Schema(Defaults object_defaults, Synchronization_Plan synchronization_plan, std::tuple<Properties...> properties) : object_defaults_(std::move(object_defaults)), synchronization_plan_(std::move(synchronization_plan)), properties_(std::move(properties)) {}
Object_Schema(const Object_Schema&) = default;
Object_Schema(Object_Schema&&) noexcept = default;
Object_Schema& operator=(const Object_Schema&) = delete;
Object_Schema& operator=(Object_Schema&&) = delete;
template <std::size_t Index>
using property_type = std::tuple_element_t<Index, std::tuple<Properties...>>;
const Defaults& object_defaults() const noexcept {
return object_defaults_;
}
template <auto Selector>
constexpr const auto& property() const {
if constexpr (std::integral<decltype(Selector)>) {
static_assert(Selector >= 0);
constexpr std::size_t index = static_cast<std::size_t>(Selector);
static_assert(index < property_count);
return std::get<index>(properties_);
} else {
static_assert(std::is_member_object_pointer_v<decltype(Selector)>);
using member_traits = Member_Pointer_Traits<decltype(Selector)>;
static_assert(std::same_as<typename member_traits::object_type, Object>);
using identity = Member_Storage_Identity<Selector>;
constexpr auto matches = []<std::size_t... Index>(std::index_sequence<Index...>) {
return std::array<bool, property_count>{std::same_as<typename std::tuple_element_t<Index, std::tuple<Properties...>>::storage_identity, identity>...};
}(std::make_index_sequence<property_count>{});
constexpr std::size_t index = [matches] {
for (std::size_t value = 0; value < matches.size(); ++value) {
if (matches[value]) {
return value;
}
}
return property_count;
}();
static_assert(index < property_count, "member is not registered in this property schema");
return std::get<index>(properties_);
}
}
template <class Function>
constexpr void for_each_property(Function&& function) const {
[&]<std::size_t... Index>(std::index_sequence<Index...>) {
(function(std::integral_constant<std::size_t, Index>{}, std::get<Index>(properties_)), ...);
}(std::make_index_sequence<property_count>{});
}
const Synchronization_Plan& synchronization_plan() const noexcept {
return synchronization_plan_;
}
};
template <class Schema>
concept Property_Schema = requires {
typename Schema::property_schema_tag;
typename Schema::object_type;
typename Schema::defaults_type;
typename Schema::property_types;
{ Schema::property_count } -> std::convertible_to<std::size_t>;
};
template <Property_Schema Schema>
consteval auto schema_property_keys() {
return []<std::size_t... Index>(std::index_sequence<Index...>) {
return std::array<std::string_view, Schema::property_count>{declared_property_key<typename Schema::template property_type<Index>>()...};
}(std::make_index_sequence<Schema::property_count>{});
}
template <Property_Schema Schema>
constexpr std::optional<std::size_t> schema_property_index(std::string_view key_value) {
constexpr auto keys = schema_property_keys<Schema>();
for (std::size_t index = 0; index < keys.size(); ++index) {
if (keys[index] == key_value) {
return index;
}
}
return std::nullopt;
}
template <Property_Schema Schema, Fixed_String Key>
consteval std::size_t schema_property_index() {
constexpr auto keys = schema_property_keys<Schema>();
for (std::size_t index = 0; index < keys.size(); ++index) {
if (keys[index] == Key.view()) {
return index;
}
}
return Schema::property_count;
}
template <class Schema, Fixed_String Key>
inline constexpr std::size_t schema_property_index_v = schema_property_index<Schema, Key>();
template <class Schema, Fixed_String Key>
concept Schema_Property_Key = Property_Schema<Schema> && schema_property_index_v<Schema, Key> < Schema::property_count;
template <Property_Schema Schema, auto Member>
consteval std::size_t schema_member_property_index() {
static_assert(std::is_member_object_pointer_v<decltype(Member)>);
using member_traits = Member_Pointer_Traits<decltype(Member)>;
static_assert(std::same_as<typename member_traits::object_type, typename Schema::object_type>);
using identity = Member_Storage_Identity<Member>;
constexpr auto matches = []<std::size_t... Index>(std::index_sequence<Index...>) {
return std::array<bool, Schema::property_count>{std::same_as<typename Schema::template property_type<Index>::storage_identity, identity>...};
}(std::make_index_sequence<Schema::property_count>{});
for (std::size_t index = 0; index < matches.size(); ++index) {
if (matches[index]) {
return index;
}
}
return Schema::property_count;
}
template <class Schema, auto Member>
inline constexpr std::size_t schema_member_property_index_v = schema_member_property_index<Schema, Member>();
template <class Schema, auto Member>
concept Schema_Property_Member = Property_Schema<Schema> && std::is_member_object_pointer_v<decltype(Member)> && schema_member_property_index_v<Schema, Member> < Schema::property_count;
template <class Schema, std::size_t Index, class Category, class Fallback>
struct Effective_Attribute {
private:
using property_attribute = typename Schema::template property_type<Index>::template attribute_type<Category>;
using default_attribute = find_attribute_in_list_t<Category, typename Schema::defaults_type::attribute_types>;
public:
using type = std::conditional_t<!std::same_as<property_attribute, void>, property_attribute, std::conditional_t<!std::same_as<default_attribute, void>, default_attribute, Fallback>>;
};
template <class Schema, std::size_t Index, class Category, class Fallback>
using effective_attribute_t = typename Effective_Attribute<Schema, Index, Category, Fallback>::type;
using Default_Access_Attribute = External_Access_Attribute<External_Access::none>;
using Default_Persistence_Attribute = Persistence_Access_Attribute<Persistence_Access::none>;
using Default_Sensitive_Attribute = Sensitive_Attribute<false>;
template <class Schema, std::size_t Index>
inline constexpr External_Access effective_external_access_v = effective_attribute_t<Schema, Index, Access_Category, Default_Access_Attribute>::value;
template <class Schema, std::size_t Index>
inline constexpr Persistence_Access effective_persistence_access_v = effective_attribute_t<Schema, Index, Persistence_Category, Default_Persistence_Attribute>::value;
template <class Schema, std::size_t Index>
inline constexpr bool effective_sensitive_v = effective_attribute_t<Schema, Index, Sensitive_Category, Default_Sensitive_Attribute>::value;
template <class Schema, std::size_t Index, class Category>
using property_declared_attribute_t = typename Schema::template property_type<Index>::template attribute_type<Category>;
template <class Schema, class Category>
using object_default_attribute_t = find_attribute_in_list_t<Category, typename Schema::defaults_type::attribute_types>;
template <class Schema, std::size_t Index, class Category>
inline constexpr bool has_declared_effective_attribute_v = !std::same_as<property_declared_attribute_t<Schema, Index, Category>, void> || !std::same_as<object_default_attribute_t<Schema, Category>, void>;
template <std::size_t Index, class Category, Property_Schema Schema>
constexpr decltype(auto) declared_effective_attribute(const Schema& schema) requires has_declared_effective_attribute_v<Schema, Index, Category> {
using property_attribute = property_declared_attribute_t<Schema, Index, Category>;
if constexpr (!std::same_as<property_attribute, void>) {
return schema.template property<Index>().template attribute<Category>();
} else {
using default_attribute = object_default_attribute_t<Schema, Category>;
return std::get<default_attribute>(schema.object_defaults().attributes);
}
}
template <class Schema, std::size_t Index>
inline constexpr bool external_readable_v = effective_external_access_v<Schema, Index> == External_Access::read || effective_external_access_v<Schema, Index> == External_Access::read_write;
template <class Schema, std::size_t Index>
inline constexpr bool external_writable_v = effective_external_access_v<Schema, Index> == External_Access::write || effective_external_access_v<Schema, Index> == External_Access::read_write;
template <class Schema, std::size_t Index>
inline constexpr bool persistence_loadable_v = effective_persistence_access_v<Schema, Index> == Persistence_Access::load || effective_persistence_access_v<Schema, Index> == Persistence_Access::load_store;
template <class Schema, std::size_t Index>
inline constexpr bool persistence_storable_v = effective_persistence_access_v<Schema, Index> == Persistence_Access::store || effective_persistence_access_v<Schema, Index> == Persistence_Access::load_store;
template <class Schema, std::size_t Index>
consteval bool property_capability_semantics_valid() {
using property_type = typename Schema::template property_type<Index>;
if constexpr ((external_readable_v<Schema, Index> || persistence_storable_v<Schema, Index>) && !property_type::readable) {
return false;
}
if constexpr ((external_writable_v<Schema, Index> || persistence_loadable_v<Schema, Index>) && !property_type::writable) {
return false;
}
return true;
}
template <class Schema, std::size_t... Index>
consteval bool schema_capability_semantics_valid_impl(std::index_sequence<Index...>) {
return (property_capability_semantics_valid<Schema, Index>() && ...);
}
template <class Schema>
consteval bool schema_capability_semantics_valid() {
return schema_capability_semantics_valid_impl<Schema>(std::make_index_sequence<Schema::property_count>{});
}
template <class Schema>
concept Valid_Property_Schema = Property_Schema<Schema> && requires {
requires schema_capability_semantics_valid<Schema>();
};
template <Valid_Property_Schema Schema>
Resolved_Synchronization_Plan<Schema::property_count> resolve_synchronization_plan(const Schema&, const Synchronization_Plan& plan) {
using resolved_type = Resolved_Synchronization_Plan<Schema::property_count>;
constexpr auto unsynchronized_slot = resolved_type::unsynchronized_slot;
std::array<std::size_t, Schema::property_count> logical{};
std::array<bool, Schema::property_count> explicitly_configured{};
std::size_t next_token = Schema::property_count + 1;
if (plan.default_mode() == Synchronization_Default::independent) {
for (std::size_t index = 0; index < Schema::property_count; ++index) {
logical[index] = index;
}
} else if (plan.default_mode() == Synchronization_Default::shared) {
logical.fill(Schema::property_count);
} else {
logical.fill(unsynchronized_slot);
}
auto require_index = [](std::string_view key_value) {
auto index = schema_property_index<Schema>(key_value);
if (!index) {
throw std::invalid_argument("Synchronization plan references unknown property: " + std::string(key_value));
}
return *index;
};
auto mark = [&](std::size_t index, std::string_view key_value) {
if (explicitly_configured[index]) {
throw std::invalid_argument("Synchronization plan configures property more than once: " + std::string(key_value));
}
explicitly_configured[index] = true;
};
for (const auto& rule : plan.property_rules()) {
auto index = require_index(rule.property);
mark(index, rule.property);
logical[index] = rule.mode == Sync_Property_Rule::Mode::unsynchronized ? unsynchronized_slot : next_token++;
}
std::vector<std::string_view> group_names;
group_names.reserve(plan.group_rules().size());
for (const auto& rule : plan.group_rules()) {
if (rule.properties.empty()) {
throw std::invalid_argument("Synchronization group cannot be empty: " + rule.name);
}
if (std::find(group_names.begin(), group_names.end(), rule.name) != group_names.end()) {
throw std::invalid_argument("Synchronization group name is duplicated: " + rule.name);
}
group_names.push_back(rule.name);
auto token = next_token++;
for (const auto& key_value : rule.properties) {
auto index = require_index(key_value);
mark(index, key_value);
logical[index] = token;
}
}
resolved_type resolved;
resolved.lock_slots.fill(unsynchronized_slot);
std::vector<std::pair<std::size_t, std::size_t>> token_slots;
token_slots.reserve(Schema::property_count);
for (std::size_t index = 0; index < Schema::property_count; ++index) {
if (logical[index] == unsynchronized_slot) {
continue;
}
auto it = std::find_if(token_slots.begin(), token_slots.end(), [&](const auto& item) {
return item.first == logical[index];
});
if (it == token_slots.end()) {
auto slot = resolved.lock_count++;
token_slots.emplace_back(logical[index], slot);
resolved.lock_slots[index] = slot;
} else {
resolved.lock_slots[index] = it->second;
}
}
return resolved;
}
template <Property_Schema Schema, auto Member>
consteval std::string_view schema_member_property_key() requires Schema_Property_Member<Schema, Member> {
constexpr auto index = schema_member_property_index_v<Schema, Member>;
return declared_property_key<typename Schema::template property_type<index>>();
}
template <class Schema>
void apply_synchronization_rule(Synchronization_Plan& plan, Sync_Default_Rule rule) {
plan.apply(rule);
}
template <class Schema>
void apply_synchronization_rule(Synchronization_Plan& plan, Sync_Property_Rule rule) {
plan.apply(std::move(rule));
}
template <class Schema>
void apply_synchronization_rule(Synchronization_Plan& plan, Sync_Group_Rule rule) {
plan.apply(std::move(rule));
}
template <class Schema, auto Member, Sync_Property_Rule::Mode Mode>
void apply_synchronization_rule(Synchronization_Plan& plan, Sync_Member_Rule<Member, Mode>) {
static_assert(Schema_Property_Member<Schema, Member>);
constexpr auto key_value = schema_member_property_key<Schema, Member>();
if constexpr (Mode == Sync_Property_Rule::Mode::independent) {
plan.independent(key_value);
} else {
plan.unsynchronized(key_value);
}
}
template <class Schema, auto... Members>
void apply_synchronization_rule(Synchronization_Plan& plan, const Sync_Member_Group_Rule<Members...>& rule) {
static_assert((Schema_Property_Member<Schema, Members> && ...));
constexpr std::array<std::string_view, sizeof...(Members)> keys{schema_member_property_key<Schema, Members>()...};
plan.group(rule.name, std::span<const std::string_view>{keys});
}
template <class Schema, Synchronization_Source_Type Source>
Synchronization_Plan materialize_synchronization_plan(Source&& source) {
if constexpr (Synchronization_Plan_Type<std::remove_cvref_t<Source>>) {
return std::forward<Source>(source);
} else {
Synchronization_Plan plan;
std::apply([&](auto&&... rules) {
(apply_synchronization_rule<Schema>(plan, std::forward<decltype(rules)>(rules)), ...);
}, std::forward<Source>(source).rules);
return plan;
}
}
template <Valid_Property_Schema Schema, class Function>
bool visit_schema_property(const Schema& schema, std::string_view key_value, Function&& function) {
bool found = false;
schema.for_each_property([&](auto index, const auto& descriptor) {
if (!found) {
using property_type = std::remove_cvref_t<decltype(descriptor)>;
if (declared_property_key<property_type>() == key_value) {
std::invoke(function, index, descriptor);
found = true;
}
}
});
return found;
}
template <class Object, class Defaults, class Source, class... Properties>
auto make_object_schema(Defaults&& object_defaults, Source&& synchronization_source, Properties&&... properties) requires Synchronization_Source_Type<std::remove_cvref_t<Source>> {
using schema_type = Object_Schema<Object, std::decay_t<Defaults>, std::decay_t<Properties>...>;
static_assert(Valid_Property_Schema<schema_type>);
auto plan = materialize_synchronization_plan<schema_type>(std::forward<Source>(synchronization_source));
schema_type schema{std::forward<Defaults>(object_defaults), std::move(plan), std::tuple<std::decay_t<Properties>...>{std::forward<Properties>(properties)...}};
static_cast<void>(resolve_synchronization_plan(schema, schema.synchronization_plan()));
return schema;
}
template <class Object, class Defaults, class Source, class... Properties>
auto object_schema(Defaults&& object_defaults, Source&& synchronization_source, Properties&&... properties) requires Object_Defaults_Type<std::remove_cvref_t<Defaults>> && Synchronization_Source_Type<std::remove_cvref_t<Source>> && (Property_Descriptor_Type<std::remove_cvref_t<Properties>> && ...) {
return make_object_schema<Object>(std::forward<Defaults>(object_defaults), std::forward<Source>(synchronization_source), std::forward<Properties>(properties)...);
}
template <class Object, class Source, class... Properties>
auto object_schema(Source&& synchronization_source, Properties&&... properties) requires Synchronization_Source_Type<std::remove_cvref_t<Source>> && (Property_Descriptor_Type<std::remove_cvref_t<Properties>> && ...) {
return make_object_schema<Object>(no_defaults, std::forward<Source>(synchronization_source), std::forward<Properties>(properties)...);
}
template <class Object, class Defaults, class... Properties>
auto object_schema(Defaults&& object_defaults, Properties&&... properties) requires Object_Defaults_Type<std::remove_cvref_t<Defaults>> && (Property_Descriptor_Type<std::remove_cvref_t<Properties>> && ...) {
return make_object_schema<Object>(std::forward<Defaults>(object_defaults), Synchronization_Plan{}, std::forward<Properties>(properties)...);
}
template <class Object, class... Properties>
auto object_schema(Properties&&... properties) requires (Property_Descriptor_Type<std::remove_cvref_t<Properties>> && ...) {
return make_object_schema<Object>(no_defaults, Synchronization_Plan{}, std::forward<Properties>(properties)...);
}
template <class Object, class... Arguments>
auto object(Arguments&&... arguments) {
return object_schema<Object>(std::forward<Arguments>(arguments)...);
}
}
@@ -0,0 +1,168 @@
#pragma once
#include <array>
#include <cstddef>
#include <concepts>
#include <initializer_list>
#include <limits>
#include <span>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
namespace structive {
enum class Synchronization_Default {
independent,
shared,
unsynchronized
};
struct Sync_Default_Rule {
Synchronization_Default value{Synchronization_Default::independent};
};
struct Sync_Property_Rule {
enum class Mode {
independent,
unsynchronized
};
Mode mode{Mode::independent};
std::string property;
};
struct Sync_Group_Rule {
std::string name;
std::vector<std::string> properties;
};
template <auto Member, Sync_Property_Rule::Mode Mode>
struct Sync_Member_Rule {
static constexpr auto member = Member;
static constexpr auto mode = Mode;
};
template <auto... Members>
struct Sync_Member_Group_Rule {
static_assert(sizeof...(Members) > 0);
std::string name;
};
inline constexpr Sync_Default_Rule sync_all_independent{Synchronization_Default::independent};
inline constexpr Sync_Default_Rule sync_all_shared{Synchronization_Default::shared};
inline constexpr Sync_Default_Rule sync_all_unsynchronized{Synchronization_Default::unsynchronized};
inline Sync_Property_Rule sync_independent(std::string_view property) {
return {Sync_Property_Rule::Mode::independent, std::string(property)};
}
inline Sync_Property_Rule sync_unsynchronized(std::string_view property) {
return {Sync_Property_Rule::Mode::unsynchronized, std::string(property)};
}
template <auto Member>
constexpr auto sync_independent() {
return Sync_Member_Rule<Member, Sync_Property_Rule::Mode::independent>{};
}
template <auto Member>
constexpr auto sync_unsynchronized() {
return Sync_Member_Rule<Member, Sync_Property_Rule::Mode::unsynchronized>{};
}
template <auto... Members>
Sync_Member_Group_Rule<Members...> sync_group(std::string_view name) {
return {std::string(name)};
}
template <class... Properties>
Sync_Group_Rule sync_group(std::string_view name, Properties&&... properties) requires (std::convertible_to<Properties, std::string_view> && ...) {
Sync_Group_Rule rule;
rule.name = name;
rule.properties.reserve(sizeof...(Properties));
(rule.properties.emplace_back(std::string_view(std::forward<Properties>(properties))), ...);
return rule;
}
class Synchronization_Plan {
Synchronization_Default default_mode_{Synchronization_Default::independent};
std::vector<Sync_Property_Rule> property_rules_;
std::vector<Sync_Group_Rule> group_rules_;
public:
using synchronization_plan_tag = void;
Synchronization_Plan() = default;
explicit Synchronization_Plan(Synchronization_Default mode) : default_mode_(mode) {}
Synchronization_Default default_mode() const noexcept {
return default_mode_;
}
const auto& property_rules() const noexcept {
return property_rules_;
}
const auto& group_rules() const noexcept {
return group_rules_;
}
Synchronization_Plan& set_default(Synchronization_Default mode) {
default_mode_ = mode;
return *this;
}
Synchronization_Plan& independent(std::string_view property) {
property_rules_.push_back(sync_independent(property));
return *this;
}
Synchronization_Plan& unsynchronized(std::string_view property) {
property_rules_.push_back(sync_unsynchronized(property));
return *this;
}
Synchronization_Plan& group(std::string_view name, std::span<const std::string_view> properties) {
Sync_Group_Rule rule;
rule.name = name;
rule.properties.reserve(properties.size());
for (auto property : properties) {
rule.properties.emplace_back(property);
}
group_rules_.push_back(std::move(rule));
return *this;
}
Synchronization_Plan& group(std::string_view name, std::initializer_list<std::string_view> properties) {
return group(name, std::span<const std::string_view>{properties.begin(), properties.size()});
}
Synchronization_Plan& apply(Sync_Default_Rule rule) {
default_mode_ = rule.value;
return *this;
}
Synchronization_Plan& apply(Sync_Property_Rule rule) {
property_rules_.push_back(std::move(rule));
return *this;
}
Synchronization_Plan& apply(Sync_Group_Rule rule) {
group_rules_.push_back(std::move(rule));
return *this;
}
};
template <class Type>
concept Synchronization_Plan_Type = requires {
typename Type::synchronization_plan_tag;
};
template <class Type>
concept Runtime_Synchronization_Rule = std::same_as<std::remove_cvref_t<Type>, Sync_Default_Rule> || std::same_as<std::remove_cvref_t<Type>, Sync_Property_Rule> || std::same_as<std::remove_cvref_t<Type>, Sync_Group_Rule>;
template <class... Rules>
struct Synchronization_Spec {
using synchronization_spec_tag = void;
std::tuple<Rules...> rules;
};
template <class Type>
concept Synchronization_Spec_Type = requires {
typename Type::synchronization_spec_tag;
};
template <class Type>
concept Synchronization_Source_Type = Synchronization_Plan_Type<Type> || Synchronization_Spec_Type<Type>;
template <class... Rules>
auto synchronization(Rules&&... rules) {
if constexpr ((Runtime_Synchronization_Rule<Rules> && ...)) {
Synchronization_Plan plan;
(plan.apply(std::forward<Rules>(rules)), ...);
return plan;
} else {
return Synchronization_Spec<std::decay_t<Rules>...>{{std::forward<Rules>(rules)...}};
}
}
template <std::size_t Property_Count>
struct Resolved_Synchronization_Plan {
static constexpr std::size_t unsynchronized_slot = std::numeric_limits<std::size_t>::max();
std::array<std::size_t, Property_Count> lock_slots{};
std::size_t lock_count{};
constexpr std::size_t slot(std::size_t property_index) const noexcept {
return lock_slots[property_index];
}
constexpr bool uses_lock(std::size_t property_index) const noexcept {
return slot(property_index) != unsynchronized_slot;
}
};
}
@@ -0,0 +1,21 @@
#pragma once
#include "schema.hpp"
#include <concepts>
#include <type_traits>
namespace structive {
template <class Object>
struct Type_Descriptor;
template <class Object>
using type_descriptor_schema_t = std::remove_cvref_t<decltype(Type_Descriptor<Object>::get())>;
template <class Object>
concept Property_Described_Object = requires {
{ Type_Descriptor<Object>::get() };
requires Valid_Property_Schema<type_descriptor_schema_t<Object>>;
requires std::same_as<typename type_descriptor_schema_t<Object>::object_type, Object>;
};
template <Property_Described_Object Object>
const type_descriptor_schema_t<Object>& type_descriptor() {
static const auto value = Type_Descriptor<Object>::get();
return value;
}
}
@@ -0,0 +1,30 @@
#pragma once
#include "schema.hpp"
#include <optional>
#include <string_view>
#include <type_traits>
namespace structive {
struct Validation_Error {
std::string_view property_key;
std::string_view code;
};
template <Valid_Property_Schema Schema, std::size_t Index, class Value>
std::optional<Validation_Error> validate_property_value(const Schema& schema, const Value& value) {
std::optional<Validation_Error> error;
schema.template property<Index>().for_each_constraint([&](const auto& constraint_value) {
if (!error && !static_cast<bool>(constraint_value.validate(value))) {
using constraint_type = std::remove_cvref_t<decltype(constraint_value)>;
error = Validation_Error{declared_property_key<typename Schema::template property_type<Index>>(), constraint_type::code.view()};
}
});
return error;
}
template <auto Member, Valid_Property_Schema Schema, class Value>
std::optional<Validation_Error> validate_property_value(const Schema& schema, const Value& value) requires Schema_Property_Member<Schema, Member> {
return validate_property_value<Schema, schema_member_property_index_v<Schema, Member>>(schema, value);
}
template <Fixed_String Key, Valid_Property_Schema Schema, class Value>
std::optional<Validation_Error> validate_property_key_value(const Schema& schema, const Value& value) requires Schema_Property_Key<Schema, Key> {
return validate_property_value<Schema, schema_property_index_v<Schema, Key>>(schema, value);
}
}