架构优化
This commit is contained in:
@@ -28,20 +28,6 @@ inline constexpr Property_Dependencies<Member_Property_Dependency<Members>...> d
|
||||
template <Fixed_String... Keys>
|
||||
inline constexpr Property_Dependencies<Key_Property_Dependency<Keys>...> depends_on_keys{};
|
||||
using No_Property_Dependencies = Property_Dependencies<>;
|
||||
template <class Accessor, class = void>
|
||||
struct Accessor_Dependency_Model {
|
||||
using type = No_Property_Dependencies;
|
||||
static constexpr bool declared = false;
|
||||
};
|
||||
template <class Accessor>
|
||||
struct Accessor_Dependency_Model<Accessor, std::void_t<typename Accessor::dependency_spec>> {
|
||||
using type = typename Accessor::dependency_spec;
|
||||
static constexpr bool declared = true;
|
||||
};
|
||||
template <class Accessor>
|
||||
using accessor_dependency_spec_t = typename Accessor_Dependency_Model<Accessor>::type;
|
||||
template <class Accessor>
|
||||
inline constexpr bool accessor_declares_dependencies_v = Accessor_Dependency_Model<Accessor>::declared;
|
||||
template <class>
|
||||
struct Member_Pointer_Traits;
|
||||
template <class Object, class Value>
|
||||
@@ -159,6 +145,8 @@ concept Property_Accessor = requires {
|
||||
typename Accessor::object_type;
|
||||
typename Accessor::value_type;
|
||||
typename Accessor::storage_identity;
|
||||
typename Accessor::dependency_spec;
|
||||
requires Property_Dependencies_Type<typename Accessor::dependency_spec>;
|
||||
{ Accessor::readable } -> std::convertible_to<bool>;
|
||||
{ Accessor::writable } -> std::convertible_to<bool>;
|
||||
{ Accessor::synchronized_view_read } -> std::convertible_to<bool>;
|
||||
|
||||
@@ -106,10 +106,18 @@ constexpr auto constraint(Function&& function) {
|
||||
return Custom_Constraint<Code, std::decay_t<Function>>{std::forward<Function>(function)};
|
||||
}
|
||||
template <class Attribute>
|
||||
concept Property_Attribute = requires {
|
||||
typename Attribute::attribute_category;
|
||||
{ Attribute::single_valued } -> std::convertible_to<bool>;
|
||||
{ Attribute::inheritable } -> std::convertible_to<bool>;
|
||||
};
|
||||
template <class Attribute>
|
||||
concept Property_Constraint = requires {
|
||||
typename Attribute::property_constraint_tag;
|
||||
{ Attribute::code.view() } -> std::convertible_to<std::string_view>;
|
||||
};
|
||||
template <class Metadata>
|
||||
concept Property_Metadata = Property_Attribute<Metadata> || Property_Constraint<Metadata>;
|
||||
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>;
|
||||
|
||||
@@ -8,25 +8,25 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
namespace structive {
|
||||
template <class Value, class... Attributes>
|
||||
template <class Value, class... Metadata>
|
||||
consteval bool constraints_compatible() {
|
||||
return (([] {
|
||||
if constexpr (Property_Constraint<Attributes>) {
|
||||
return Property_Constraint_For<Attributes, Value>;
|
||||
if constexpr (Property_Constraint<Metadata>) {
|
||||
return Property_Constraint_For<Metadata, Value>;
|
||||
}
|
||||
return true;
|
||||
}()) && ...);
|
||||
}
|
||||
template <Property_Accessor Accessor, class... Attributes>
|
||||
template <Property_Accessor Accessor, class... Metadata>
|
||||
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 dependency_spec = accessor_dependency_spec_t<Accessor>;
|
||||
using attribute_types = Type_List<Attributes...>;
|
||||
using capability_type = find_attribute_in_list_t<Capability_Category, attribute_types>;
|
||||
using dependency_spec = typename Accessor::dependency_spec;
|
||||
using metadata_types = Type_List<Metadata...>;
|
||||
using capability_type = find_attribute_in_list_t<Capability_Category, metadata_types>;
|
||||
static constexpr Property_Capability intrinsic_capability = [] {
|
||||
if constexpr (!std::same_as<capability_type, void>) {
|
||||
return capability_type::value;
|
||||
@@ -44,48 +44,59 @@ struct Property_Descriptor {
|
||||
static constexpr bool writable = Accessor::writable && (intrinsic_capability == Property_Capability::write || intrinsic_capability == Property_Capability::read_write);
|
||||
static constexpr bool synchronized_view_read = Accessor::synchronized_view_read;
|
||||
static constexpr bool trusted_object_access = Accessor::trusted_object_access;
|
||||
static constexpr bool declares_dependencies = accessor_declares_dependencies_v<Accessor>;
|
||||
static constexpr std::size_t dependency_count = dependency_spec::count;
|
||||
static constexpr bool runtime_copy_writable = writable && requires(const Accessor& accessor, object_type& object, const value_type& candidate) { accessor.write(object, candidate); };
|
||||
static_assert(unique_single_value_categories<Attributes...>());
|
||||
static_assert((Property_Metadata<Metadata> && ...), "property metadata must be an Attribute or Constraint");
|
||||
static_assert(unique_single_value_categories<Metadata...>());
|
||||
static_assert(!((intrinsic_capability == Property_Capability::read || intrinsic_capability == Property_Capability::read_write) && !Accessor::readable), "property capability requests read from a non-readable accessor");
|
||||
static_assert(!((intrinsic_capability == Property_Capability::write || intrinsic_capability == Property_Capability::read_write) && !Accessor::writable), "property capability requests write from a non-writable accessor");
|
||||
using key_type = find_attribute_in_list_t<Key_Category, attribute_types>;
|
||||
using key_type = find_attribute_in_list_t<Key_Category, metadata_types>;
|
||||
static_assert(!std::same_as<key_type, void>);
|
||||
static_assert(key_type::value.view().size() > 0);
|
||||
static_assert(constraints_compatible<value_type, Attributes...>());
|
||||
static_assert(constraints_compatible<value_type, Metadata...>());
|
||||
[[no_unique_address]] Accessor accessor{};
|
||||
std::tuple<Attributes...> attributes;
|
||||
std::tuple<Metadata...> metadata;
|
||||
template <class Category>
|
||||
using attribute_type = find_attribute_in_list_t<Category, attribute_types>;
|
||||
using attribute_type = find_attribute_in_list_t<Category, metadata_types>;
|
||||
template <class Category>
|
||||
static constexpr std::size_t attribute_count = count_attribute_category<Category, Attributes...>();
|
||||
static constexpr std::size_t attribute_count = count_attribute_category<Category, Metadata...>();
|
||||
template <class Category>
|
||||
static constexpr bool has_attribute = attribute_count<Category> != 0;
|
||||
template <class Category>
|
||||
constexpr decltype(auto) attribute() const requires (attribute_count<Category> == 1) {
|
||||
using target = attribute_type<Category>;
|
||||
return std::get<target>(attributes);
|
||||
return std::get<target>(metadata);
|
||||
}
|
||||
constexpr std::string_view key() const noexcept {
|
||||
return key_type::value.view();
|
||||
}
|
||||
template <class Function>
|
||||
constexpr void for_each_attribute(Function&& function) const {
|
||||
constexpr void for_each_metadata(Function&& function) const {
|
||||
std::apply([&](const auto&... values) {
|
||||
(function(values), ...);
|
||||
}, attributes);
|
||||
}, metadata);
|
||||
}
|
||||
template <class Function>
|
||||
constexpr void for_each_attribute(Function&& function) const {
|
||||
std::apply([&](const auto&... values) {
|
||||
([&] {
|
||||
using type = std::remove_cvref_t<decltype(values)>;
|
||||
if constexpr (Property_Attribute<type>) {
|
||||
function(values);
|
||||
}
|
||||
}(), ...);
|
||||
}, metadata);
|
||||
}
|
||||
template <class Category, class Function>
|
||||
constexpr void for_each_attribute(Function&& function) const {
|
||||
std::apply([&](const auto&... values) {
|
||||
([&] {
|
||||
using type = std::remove_cvref_t<decltype(values)>;
|
||||
if constexpr (std::same_as<attribute_category_of_t<type>, Category>) {
|
||||
if constexpr (Property_Attribute<type> && std::same_as<attribute_category_of_t<type>, Category>) {
|
||||
function(values);
|
||||
}
|
||||
}(), ...);
|
||||
}, attributes);
|
||||
}, metadata);
|
||||
}
|
||||
template <class Function>
|
||||
constexpr void for_each_constraint(Function&& function) const {
|
||||
@@ -96,7 +107,7 @@ struct Property_Descriptor {
|
||||
function(values);
|
||||
}
|
||||
}(), ...);
|
||||
}, attributes);
|
||||
}, metadata);
|
||||
}
|
||||
};
|
||||
template <class Type>
|
||||
@@ -107,29 +118,28 @@ concept Property_Descriptor_Type = requires {
|
||||
typename Type::accessor_type;
|
||||
typename Type::storage_identity;
|
||||
};
|
||||
template <auto Member, class... Attributes>
|
||||
constexpr auto property(Attributes&&... attributes) {
|
||||
template <auto Member, class... Metadata>
|
||||
constexpr auto property(Metadata&&... metadata) {
|
||||
using accessor = Member_Accessor<Member>;
|
||||
return Property_Descriptor<accessor, std::decay_t<Attributes>...>{{}, {std::forward<Attributes>(attributes)...}};
|
||||
return Property_Descriptor<accessor, std::decay_t<Metadata>...>{{}, {std::forward<Metadata>(metadata)...}};
|
||||
}
|
||||
template <auto Member, class... Attributes>
|
||||
constexpr auto field(Attributes&&... attributes) {
|
||||
return property<Member>(std::forward<Attributes>(attributes)...);
|
||||
template <auto Member, class... Metadata>
|
||||
constexpr auto field(Metadata&&... metadata) {
|
||||
return property<Member>(std::forward<Metadata>(metadata)...);
|
||||
}
|
||||
template <class Object, class Value, Property_Dependencies_Type Dependencies, class Function, class... Attributes>
|
||||
constexpr auto computed_property(Dependencies, Function&& function, Attributes&&... attributes) {
|
||||
static_assert(Dependencies::count > 0, "computed property must declare at least one dependency");
|
||||
template <class Object, class Value, Property_Dependencies_Type Dependencies, class Function, class... Metadata>
|
||||
constexpr auto computed_property(Dependencies, Function&& function, Metadata&&... metadata) {
|
||||
using accessor = Synchronized_Computed_Accessor<Object, Value, std::decay_t<Function>, Dependencies>;
|
||||
return Property_Descriptor<accessor, std::decay_t<Attributes>...>{accessor{std::forward<Function>(function)}, {std::forward<Attributes>(attributes)...}};
|
||||
return Property_Descriptor<accessor, std::decay_t<Metadata>...>{accessor{std::forward<Function>(function)}, {std::forward<Metadata>(metadata)...}};
|
||||
}
|
||||
template <auto Getter, class... Attributes> requires Trusted_Getter_Function<Getter>
|
||||
constexpr auto trusted_computed_property(Attributes&&... attributes) {
|
||||
template <auto Getter, class... Metadata> requires Trusted_Getter_Function<Getter>
|
||||
constexpr auto trusted_computed_property(Metadata&&... metadata) {
|
||||
using accessor = Trusted_Computed_Accessor<Getter>;
|
||||
return Property_Descriptor<accessor, std::decay_t<Attributes>...>{{}, {std::forward<Attributes>(attributes)...}};
|
||||
return Property_Descriptor<accessor, std::decay_t<Metadata>...>{{}, {std::forward<Metadata>(metadata)...}};
|
||||
}
|
||||
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) {
|
||||
template <auto Getter, auto Setter, class... Metadata> 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(Metadata&&... metadata) {
|
||||
using accessor = Trusted_Getter_Setter_Accessor<Getter, Setter>;
|
||||
return Property_Descriptor<accessor, std::decay_t<Attributes>...>{{}, {std::forward<Attributes>(attributes)...}};
|
||||
return Property_Descriptor<accessor, std::decay_t<Metadata>...>{{}, {std::forward<Metadata>(metadata)...}};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ struct Find_Attribute_By_Category<Category> {
|
||||
};
|
||||
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>;
|
||||
using type = std::conditional_t<!std::same_as<attribute_category_of_t<Head>, void> && 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;
|
||||
@@ -67,7 +67,7 @@ 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));
|
||||
return (std::size_t{0} + ... + (!std::same_as<attribute_category_of_t<Attributes>, void> && std::same_as<attribute_category_of_t<Attributes>, Category> ? 1u : 0u));
|
||||
}
|
||||
template <class Attribute, class... Attributes>
|
||||
consteval bool unique_single_value_category_for() {
|
||||
|
||||
@@ -338,15 +338,8 @@ private:
|
||||
template <std::size_t Index>
|
||||
decltype(auto) get_index() const {
|
||||
using Schema = type_descriptor_schema_t<Derived>;
|
||||
using Owner_Property = typename Schema::template property_type<Property_Index>;
|
||||
using Property = typename Schema::template property_type<Index>;
|
||||
if constexpr (Owner_Property::declares_dependencies) {
|
||||
static_assert(schema_property_depends_on_index_v<Schema, Property_Index, Index>, "computed property reads an undeclared dependency");
|
||||
} else if constexpr (Property::writable || Property::accessor_type::synchronized_view_read) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
static_assert(schema_property_depends_on_index_v<Schema, Property_Index, Index>, "computed property reads an undeclared dependency");
|
||||
if constexpr (Property::accessor_type::synchronized_view_read) {
|
||||
Single_Read_View<Index> nested{*owner_, lock_slot_};
|
||||
return owner_->template read_unlocked<Index>(nested);
|
||||
|
||||
@@ -238,13 +238,7 @@ struct Schema_Dependency_Indices<Schema, Property_Dependencies<Dependencies...>>
|
||||
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;
|
||||
constexpr auto indices = Schema_Dependency_Indices<Schema, dependency_spec>::value;
|
||||
for (auto dependency : indices) {
|
||||
if (dependency == Index) {
|
||||
throw "computed property cannot depend on itself";
|
||||
}
|
||||
}
|
||||
return indices;
|
||||
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();
|
||||
@@ -271,6 +265,37 @@ consteval auto schema_property_dependency_matrix() {
|
||||
}(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:
|
||||
@@ -301,11 +326,11 @@ constexpr decltype(auto) declared_effective_attribute(const Schema& schema) requ
|
||||
}
|
||||
}
|
||||
template <class Schema>
|
||||
concept Valid_Property_Schema = Property_Schema<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 && Schema::template property_type<Index>::declares_dependencies;
|
||||
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>
|
||||
@@ -331,7 +356,6 @@ Resolved_Synchronization_Plan<Schema::property_count> resolve_synchronization_pl
|
||||
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>();
|
||||
constexpr auto keys = schema_property_keys<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;
|
||||
@@ -407,18 +431,12 @@ Resolved_Synchronization_Plan<Schema::property_count> resolve_synchronization_pl
|
||||
}
|
||||
}
|
||||
if constexpr (Schema::property_count > 0) {
|
||||
std::array<unsigned char, Schema::property_count> dependency_state{};
|
||||
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]) {
|
||||
if (!derived_synchronization[index] || dependency_resolved[index]) {
|
||||
return resolved.lock_slots[index];
|
||||
}
|
||||
if (dependency_state[index] == 2) {
|
||||
return resolved.lock_slots[index];
|
||||
}
|
||||
if (dependency_state[index] == 1) {
|
||||
throw std::invalid_argument("Computed property dependency cycle: " + std::string(keys[index]));
|
||||
}
|
||||
dependency_state[index] = 1;
|
||||
bool has_dependency_domain = false;
|
||||
std::size_t dependency_slot = unsynchronized_slot;
|
||||
for (std::size_t dependency = 0; dependency < Schema::property_count; ++dependency) {
|
||||
@@ -434,7 +452,7 @@ Resolved_Synchronization_Plan<Schema::property_count> resolve_synchronization_pl
|
||||
}
|
||||
}
|
||||
resolved.lock_slots[index] = has_dependency_domain ? dependency_slot : unsynchronized_slot;
|
||||
dependency_state[index] = 2;
|
||||
dependency_resolved[index] = true;
|
||||
return resolved.lock_slots[index];
|
||||
};
|
||||
for (std::size_t index = 0; index < Schema::property_count; ++index) {
|
||||
@@ -512,10 +530,13 @@ bool visit_schema_property(const Schema& schema, std::string_view key_value, Fun
|
||||
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>);
|
||||
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::move(plan), std::tuple<std::decay_t<Properties>...>{std::forward<Properties>(properties)...}};
|
||||
static_cast<void>(resolve_synchronization_plan(schema, schema.synchronization_plan()));
|
||||
if constexpr (valid_schema) {
|
||||
static_cast<void>(resolve_synchronization_plan(schema, schema.synchronization_plan()));
|
||||
}
|
||||
return schema;
|
||||
}
|
||||
template <class Object, class Defaults, class Source, class... Properties>
|
||||
|
||||
Reference in New Issue
Block a user