Files
Structive/core/include/structive/property/schema.hpp
T
2026-08-12 06:16:00 +08:00

682 lines
35 KiB
C++

#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<std::string_view, sizeof...(Properties)> 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... Attributes>
struct Type_Metadata {
using type_metadata_tag = void;
using attribute_types = Type_List<Attributes...>;
static_assert((Property_Attribute<Attributes> && ...));
static_assert(unique_single_value_categories<Attributes...>());
std::tuple<Attributes...> attributes;
};
template <class Type>
concept Type_Metadata_Type = requires {
typename Type::type_metadata_tag;
typename Type::attribute_types;
};
template <class... Attributes>
constexpr auto type_metadata(Attributes&&... attributes) {
return Type_Metadata<std::decay_t<Attributes>...>{
{std::forward<Attributes>(attributes)...}};
}
using No_Type_Metadata = Type_Metadata<>;
inline constexpr No_Type_Metadata no_type_metadata{};
template <class Object, class Defaults, class Type_Metadata_Value, class... Properties>
class Object_Schema {
[[no_unique_address]] Defaults object_defaults_;
[[no_unique_address]] Type_Metadata_Value type_metadata_;
Synchronization_Plan synchronization_plan_;
std::tuple<Properties...> properties_;
public:
using property_schema_tag = void;
using object_type = Object;
using defaults_type = Defaults;
using type_metadata_type = Type_Metadata_Value;
using type_attribute_types = typename Type_Metadata_Value::attribute_types;
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, Type_Metadata_Value type_metadata_value,
Synchronization_Plan synchronization_plan,
std::tuple<Properties...> properties)
: object_defaults_(std::move(object_defaults)),
type_metadata_(std::move(type_metadata_value)),
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_;
}
const Type_Metadata_Value& metadata() const noexcept {
return type_metadata_;
}
template <class Category>
using type_attribute_type = find_attribute_in_list_t<Category, type_attribute_types>;
template <class Category>
static constexpr std::size_t type_attribute_count =
[]<class... Attributes>(Type_List<Attributes...>) {
return count_attribute_category<Category, Attributes...>();
}(type_attribute_types{});
template <class Category>
static constexpr bool has_type_attribute = type_attribute_count<Category> != 0;
template <class Category>
constexpr decltype(auto) type_attribute() const
requires (type_attribute_count<Category> == 1) {
using target = type_attribute_type<Category>;
return std::get<target>(type_metadata_.attributes);
}
template <class Function>
constexpr void for_each_type_attribute(Function&& function) const {
std::apply([&](const auto&... attributes) {
(function(attributes), ...);
}, type_metadata_.attributes);
}
template <class Category, class Function>
constexpr void for_each_type_attribute(Function&& function) const {
std::apply([&](const auto&... attributes) {
([&] {
using type = std::remove_cvref_t<decltype(attributes)>;
if constexpr (std::same_as<attribute_category_of_t<type>, Category>)
function(attributes);
}(), ...);
}, type_metadata_.attributes);
}
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::type_metadata_type;
typename Schema::type_attribute_types;
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>
concept Schema_Property_Index = Property_Schema<Schema> && Index < Schema::property_count;
template <class Schema, class Dependency>
struct Schema_Dependency_Index;
template <Property_Schema Schema, auto Member>
struct Schema_Dependency_Index<Schema, Member_Property_Dependency<Member>> {
static constexpr std::size_t value = schema_member_property_index_v<Schema, Member>;
static_assert(value < Schema::property_count, "computed dependency member is not registered in this property schema");
static_assert(Schema::template property_type<value>::readable, "computed dependency must be readable");
};
template <Property_Schema Schema, Fixed_String Key>
struct Schema_Dependency_Index<Schema, Key_Property_Dependency<Key>> {
static constexpr std::size_t value = schema_property_index_v<Schema, Key>;
static_assert(value < Schema::property_count, "computed dependency key is not registered in this property schema");
static_assert(Schema::template property_type<value>::readable, "computed dependency must be readable");
};
template <std::size_t Size>
consteval bool unique_dependency_indices(const std::array<std::size_t, Size>& indices) {
for (std::size_t left = 0; left < Size; ++left) {
for (std::size_t right = left + 1; right < Size; ++right) {
if (indices[left] == indices[right]) {
return false;
}
}
}
return true;
}
template <Property_Schema Schema, class Dependencies>
struct Schema_Dependency_Indices;
template <Property_Schema Schema, class... Dependencies>
struct Schema_Dependency_Indices<Schema, Property_Dependencies<Dependencies...>> {
static constexpr std::array<std::size_t, sizeof...(Dependencies)> value{Schema_Dependency_Index<Schema, Dependencies>::value...};
static_assert(unique_dependency_indices(value), "computed property declares the same dependency more than once");
};
template <Property_Schema Schema, std::size_t Index>
consteval auto schema_property_dependency_indices() requires Schema_Property_Index<Schema, Index> {
using dependency_spec = typename Schema::template property_type<Index>::dependency_spec;
return Schema_Dependency_Indices<Schema, dependency_spec>::value;
}
template <class Schema, std::size_t Index>
inline constexpr std::size_t schema_property_dependency_count_v = schema_property_dependency_indices<Schema, Index>().size();
template <class Schema, std::size_t Property_Index, std::size_t Dependency_Index>
inline constexpr bool schema_property_depends_on_index_v = [] {
constexpr auto dependencies = schema_property_dependency_indices<Schema, Property_Index>();
for (auto index : dependencies) {
if (index == Dependency_Index) {
return true;
}
}
return false;
}();
template <Property_Schema Schema>
consteval auto schema_property_dependency_matrix() {
std::array<std::array<bool, Schema::property_count>, Schema::property_count> matrix{};
[&]<std::size_t... Index>(std::index_sequence<Index...>) {
([&] {
constexpr auto dependencies = schema_property_dependency_indices<Schema, Index>();
for (auto dependency : dependencies) {
matrix[Index][dependency] = true;
}
}(), ...);
}(std::make_index_sequence<Schema::property_count>{});
return matrix;
}
template <Property_Schema Schema>
consteval bool schema_dependency_graph_acyclic() {
constexpr auto dependencies = schema_property_dependency_matrix<Schema>();
std::array<std::size_t, Schema::property_count> dependency_counts{};
std::array<std::size_t, Schema::property_count> ready{};
std::size_t ready_begin = 0;
std::size_t ready_end = 0;
for (std::size_t property = 0; property < Schema::property_count; ++property) {
for (std::size_t dependency = 0; dependency < Schema::property_count; ++dependency) {
dependency_counts[property] += dependencies[property][dependency] ? 1u : 0u;
}
if (dependency_counts[property] == 0) {
ready[ready_end++] = property;
}
}
std::size_t resolved_count = 0;
while (ready_begin != ready_end) {
auto resolved = ready[ready_begin++];
++resolved_count;
for (std::size_t property = 0; property < Schema::property_count; ++property) {
if (!dependencies[property][resolved]) {
continue;
}
--dependency_counts[property];
if (dependency_counts[property] == 0) {
ready[ready_end++] = property;
}
}
}
return resolved_count == 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_Sensitive_Attribute = Sensitive_Attribute<false>;
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>
concept Valid_Property_Schema = Property_Schema<Schema> && schema_dependency_graph_acyclic<Schema>();
template <class Schema, std::size_t Index>
inline constexpr bool property_requires_synchronization_v = Schema::template property_type<Index>::writable || Schema::template property_type<Index>::synchronized_view_read;
template <class Schema, std::size_t Index>
inline constexpr bool property_has_derived_synchronization_v = Schema::template property_type<Index>::synchronized_view_read && !Schema::template property_type<Index>::writable;
template <class Schema, std::size_t Index>
inline constexpr bool property_owns_synchronization_domain_v = Schema::template property_type<Index>::writable || (Schema::template property_type<Index>::synchronized_view_read && !property_has_derived_synchronization_v<Schema, Index>);
template <class Schema, std::size_t Index>
concept Schema_Readable_Property_Index = Schema_Property_Index<Schema, Index> && Schema::template property_type<Index>::readable;
template <class Schema, std::size_t Index>
concept Schema_Writable_Property_Index = Schema_Property_Index<Schema, Index> && Schema::template property_type<Index>::writable;
template <class Schema, auto Member>
concept Schema_Readable_Property_Member = Schema_Property_Member<Schema, Member> && Schema::template property_type<schema_member_property_index_v<Schema, Member>>::readable;
template <class Schema, auto Member>
concept Schema_Writable_Property_Member = Schema_Property_Member<Schema, Member> && Schema::template property_type<schema_member_property_index_v<Schema, Member>>::writable;
template <class Schema, Fixed_String Key>
concept Schema_Readable_Property_Key = Schema_Property_Key<Schema, Key> && Schema::template property_type<schema_property_index_v<Schema, Key>>::readable;
template <class Schema, Fixed_String Key>
concept Schema_Writable_Property_Key = Schema_Property_Key<Schema, Key> && Schema::template property_type<schema_property_index_v<Schema, Key>>::writable;
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;
constexpr auto owns_synchronization_domain = []<std::size_t... Index>(std::index_sequence<Index...>) {
return std::array<bool, Schema::property_count>{property_owns_synchronization_domain_v<Schema, Index>...};
}(std::make_index_sequence<Schema::property_count>{});
constexpr auto derived_synchronization = []<std::size_t... Index>(std::index_sequence<Index...>) {
return std::array<bool, Schema::property_count>{property_has_derived_synchronization_v<Schema, Index>...};
}(std::make_index_sequence<Schema::property_count>{});
constexpr auto dependencies = schema_property_dependency_matrix<Schema>();
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 (derived_synchronization[index]) {
throw std::invalid_argument("Synchronization of computed property is derived from dependencies: " + std::string(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;
}
}
for (std::size_t index = 0; index < Schema::property_count; ++index) {
if (!owns_synchronization_domain[index]) {
logical[index] = unsynchronized_slot;
}
}
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;
}
}
if constexpr (Schema::property_count > 0) {
constexpr auto keys = schema_property_keys<Schema>();
std::array<bool, Schema::property_count> dependency_resolved{};
auto resolve_derived_slot = [&](auto&& self, std::size_t index) -> std::size_t {
if (!derived_synchronization[index] || dependency_resolved[index]) {
return resolved.lock_slots[index];
}
bool has_dependency_domain = false;
std::size_t dependency_slot = unsynchronized_slot;
for (std::size_t dependency = 0; dependency < Schema::property_count; ++dependency) {
if (!dependencies[index][dependency] || (!owns_synchronization_domain[dependency] && !derived_synchronization[dependency])) {
continue;
}
auto current_slot = derived_synchronization[dependency] ? self(self, dependency) : resolved.lock_slots[dependency];
if (!has_dependency_domain) {
dependency_slot = current_slot;
has_dependency_domain = true;
} else if (dependency_slot != current_slot) {
throw std::invalid_argument("Computed property dependencies do not share one synchronization domain: " + std::string(keys[index]));
}
}
resolved.lock_slots[index] = has_dependency_domain ? dependency_slot : unsynchronized_slot;
dependency_resolved[index] = true;
return resolved.lock_slots[index];
};
for (std::size_t index = 0; index < Schema::property_count; ++index) {
if (derived_synchronization[index]) {
resolve_derived_slot(resolve_derived_slot, index);
}
}
}
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_at(const Schema& schema, std::size_t index_value, Function&& function) {
bool found = false;
schema.for_each_property([&](auto index, const auto& descriptor) {
if (!found && decltype(index)::value == index_value) {
std::invoke(function, index, descriptor);
found = true;
}
});
return found;
}
template <Valid_Property_Schema Schema, class Function>
bool visit_schema_property(const Schema& schema, std::string_view key_value, Function&& function) {
auto index = schema_property_index<Schema>(key_value);
if (!index) {
return false;
}
return visit_schema_property_at(schema, *index, std::forward<Function>(function));
}
template <class Object, class Defaults, class Metadata, class Source, class... Properties>
auto make_object_schema(Defaults&& object_defaults, Metadata&& metadata,
Source&& synchronization_source, Properties&&... properties)
requires Object_Defaults_Type<std::remove_cvref_t<Defaults>> &&
Type_Metadata_Type<std::remove_cvref_t<Metadata>> &&
Synchronization_Source_Type<std::remove_cvref_t<Source>> {
using schema_type = Object_Schema<Object, std::decay_t<Defaults>,
std::decay_t<Metadata>, std::decay_t<Properties>...>;
constexpr bool valid_schema = Valid_Property_Schema<schema_type>;
static_assert(valid_schema, "property dependency graph must be valid and acyclic");
auto plan = materialize_synchronization_plan<schema_type>(std::forward<Source>(synchronization_source));
schema_type schema{std::forward<Defaults>(object_defaults),
std::forward<Metadata>(metadata), std::move(plan),
std::tuple<std::decay_t<Properties>...>{
std::forward<Properties>(properties)...}};
if constexpr (valid_schema) {
static_cast<void>(resolve_synchronization_plan(schema, schema.synchronization_plan()));
}
return schema;
}
template <class Object, class Defaults, class Metadata, class Source, class... Properties>
auto object_schema(Defaults&& object_defaults, Metadata&& metadata,
Source&& synchronization_source, Properties&&... properties)
requires Object_Defaults_Type<std::remove_cvref_t<Defaults>> &&
Type_Metadata_Type<std::remove_cvref_t<Metadata>> &&
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<Metadata>(metadata),
std::forward<Source>(synchronization_source),
std::forward<Properties>(properties)...);
}
template <class Object, class Metadata, class Source, class... Properties>
auto object_schema(Metadata&& metadata, Source&& synchronization_source,
Properties&&... properties)
requires Type_Metadata_Type<std::remove_cvref_t<Metadata>> &&
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<Metadata>(metadata),
std::forward<Source>(synchronization_source),
std::forward<Properties>(properties)...);
}
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), no_type_metadata, 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, no_type_metadata, std::forward<Source>(synchronization_source), std::forward<Properties>(properties)...);
}
template <class Object, class Defaults, class Metadata, class... Properties>
auto object_schema(Defaults&& object_defaults, Metadata&& metadata,
Properties&&... properties)
requires Object_Defaults_Type<std::remove_cvref_t<Defaults>> &&
Type_Metadata_Type<std::remove_cvref_t<Metadata>> &&
(Property_Descriptor_Type<std::remove_cvref_t<Properties>> && ...) {
return make_object_schema<Object>(std::forward<Defaults>(object_defaults),
std::forward<Metadata>(metadata),
Synchronization_Plan{},
std::forward<Properties>(properties)...);
}
template <class Object, class Metadata, class... Properties>
auto object_schema(Metadata&& metadata, Properties&&... properties)
requires Type_Metadata_Type<std::remove_cvref_t<Metadata>> &&
(Property_Descriptor_Type<std::remove_cvref_t<Properties>> && ...) {
return make_object_schema<Object>(no_defaults, std::forward<Metadata>(metadata),
Synchronization_Plan{},
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), no_type_metadata, 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, no_type_metadata, Synchronization_Plan{}, std::forward<Properties>(properties)...);
}
template <class Object, class... Arguments>
requires requires(Arguments&&... arguments) {
object_schema<Object>(std::forward<Arguments>(arguments)...);
}
auto object(Arguments&&... arguments) {
return object_schema<Object>(std::forward<Arguments>(arguments)...);
}
}