去除所有访问级别

This commit is contained in:
2026-08-07 17:50:04 +08:00
parent 0c7fac70b2
commit 3205dd84e8
17 changed files with 2086 additions and 2424 deletions
+4 -9
View File
@@ -1,14 +1,9 @@
# Structive Property Core
The Core is the C++20 header-only structural and managed-property layer of Structive.
`structive::property_core` describes intrinsic property structure and provides managed read/write, synchronization, traversal, validation metadata and type-erased runtime access.
It provides `Type_Descriptor<T>`, `Object_Schema`, member/computed property descriptors, the unified Attribute protocol, explicit constraints and validation, access capability views, synchronization plans/guards, typed traversal and type-erased runtime access.
Core has no access-control modes. A property only reports its own intrinsic `readable` / `writable` capability. External systems own their own exposure and authorization policy.
The core design rule is that registered properties remain ordinary C++ members. Structive adds a managed structural layer; it does not replace the native object model.
Stored `read_only` properties do not receive lock slots or contribute mutexes, and managed reads use the no-lock fast path.
See the complete documentation:
- [Core Guide](../docs/CORE_GUIDE.md)
- [Design Philosophy](../docs/DESIGN.md)
- [中文 Core 指南](../docs/CORE_GUIDE.zh-CN.md)
- [中文设计理念](../docs/DESIGN.zh-CN.md)
See [Core Guide](../docs/CORE_GUIDE.md) and [Design Philosophy](../docs/DESIGN.md).
+4 -9
View File
@@ -1,14 +1,9 @@
# Structive Property Core
Core 是 Structive 的 C++20 header-only 结构描述与 managed-property 核心层
`structive::property_core` 描述 Property 自身的固有结构,并提供 managed read/write、synchronization、traversal、validation metadata 和 type-erased runtime access
它提供 `Type_Descriptor<T>``Object_Schema`、member/computed property descriptor、统一 Attribute 协议、显式 constraint/validation、access capability view、synchronization plan/guard、typed traversal,以及 type-erased runtime access
Core 完全没有访问控制 mode。Property 只报告自身 intrinsic `readable` / `writable`;外部系统自己拥有 exposure 与 authorization policy
Core 的基本原则是:注册后的 property 仍然是普通 C++ 成员。Structive 增加受管理结构层,但不替代原生对象模型
Stored `read_only` Property 不分配 lock slot、不贡献 mutexmanaged read 直接走 no-lock fast path
完整文档:
- [Core 完整指南](../docs/CORE_GUIDE.zh-CN.md)
- [设计理念与原则](../docs/DESIGN.zh-CN.md)
- [Core Guide](../docs/CORE_GUIDE.md)
- [Design Philosophy](../docs/DESIGN.md)
详见 [Core 完整指南](../docs/CORE_GUIDE.zh-CN.md) 与 [设计理念](../docs/DESIGN.zh-CN.md)。
+8 -32
View File
@@ -1,51 +1,27 @@
#include <structive/property/property.hpp>
#include <iostream>
#include <string>
using namespace structive;
struct Device : Property_Object<Device> {
double temperature{25.0};
double pressure{101.3};
double min_speed{10.0};
double max_speed{100.0};
std::string name{"device-1"};
int serial_number{1001};
};
template <>
struct structive::Type_Descriptor<Device> {
static auto get() {
return object<Device>(
defaults(external_access<External_Access::read_write>, persistence_access<Persistence_Access::load_store>),
synchronization(sync_all_independent, sync_group<&Device::min_speed, &Device::max_speed>("speed_range")),
synchronization(sync_all_independent),
field<&Device::temperature>(key<"temperature">, unit<"C">, min_value<-50.0>, max_value<200.0>),
field<&Device::pressure>(key<"pressure">, unit<"kPa">),
field<&Device::min_speed>(key<"min_speed">),
field<&Device::max_speed>(key<"max_speed">),
field<&Device::name>(key<"name">)
field<&Device::serial_number>(key<"serial_number">, read_only)
);
}
};
static bool update_speed_range(Device& device, double min_speed, double max_speed) {
auto guard = device.lock_unique<&Device::min_speed, &Device::max_speed>();
auto old_min = guard.get<&Device::min_speed>();
auto old_max = guard.get<&Device::max_speed>();
guard.set < &Device::min_speed > (min_speed);
guard.set < &Device::max_speed > (max_speed);
if (min_speed <= max_speed) {
return true;
}
guard.set < &Device::min_speed > (old_min);
guard.set < &Device::max_speed > (old_max);
return false;
}
int main() {
Device device;
device.external().write < &Device::temperature > (30.0);
std::cout << "temperature=" << device.external().read<&Device::temperature>() << '\n';
std::cout << "temperature lock slot=" << device.lock_slot<&Device::temperature>() << '\n';
std::cout << "min/max shared slot=" << device.lock_slot<&Device::min_speed>() << '\n';
std::cout << "max_speed key=" << device.schema().property<&Device::max_speed>().key() << '\n';
if (!update_speed_range(device, 120.0, 80.0)) {
std::cout << "invalid range rolled back by business logic\n";
}
device.temperature = 31.0;
std::cout << "raw temperature=" << device.temperature << '\n';
device.write<&Device::temperature>(30.0);
std::cout << "temperature=" << device.read<&Device::temperature>() << '\n';
std::cout << "serial_number=" << device.read<&Device::serial_number>() << '\n';
std::cout << "lock_count=" << device.resolved_synchronization().lock_count << '\n';
return 0;
}
+12 -24
View File
@@ -7,22 +7,15 @@
#include <utility>
namespace structive {
struct Key_Category {};
struct Access_Category {};
struct Persistence_Category {};
struct Capability_Category {};
struct Unit_Category {};
struct Sensitive_Category {};
enum class External_Access {
enum class Property_Capability {
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;
@@ -30,18 +23,11 @@ struct Key_Attribute {
static constexpr bool inheritable = false;
static constexpr auto value = Value;
};
template <External_Access Value>
struct External_Access_Attribute {
using attribute_category = Access_Category;
template <Property_Capability Value>
struct Property_Capability_Attribute {
using attribute_category = Capability_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 bool inheritable = false;
static constexpr auto value = Value;
};
template <Fixed_String Value>
@@ -100,10 +86,12 @@ struct Custom_Constraint {
};
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 <Property_Capability Value>
inline constexpr Property_Capability_Attribute<Value> capability{};
inline constexpr auto read_only = capability<Property_Capability::read>;
inline constexpr auto write_only = capability<Property_Capability::write>;
inline constexpr auto read_write = capability<Property_Capability::read_write>;
inline constexpr auto inaccessible = capability<Property_Capability::none>;
template <Fixed_String Value>
inline constexpr Unit_Attribute<Value> unit{};
template <bool Value = true>
+18 -2
View File
@@ -25,11 +25,27 @@ struct Property_Descriptor {
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;
using capability_type = find_attribute_in_list_t<Capability_Category, attribute_types>;
static constexpr Property_Capability intrinsic_capability = [] {
if constexpr (!std::same_as<capability_type, void>) {
return capability_type::value;
} else if constexpr (Accessor::readable && Accessor::writable) {
return Property_Capability::read_write;
} else if constexpr (Accessor::readable) {
return Property_Capability::read;
} else if constexpr (Accessor::writable) {
return Property_Capability::write;
} else {
return Property_Capability::none;
}
}();
static constexpr bool readable = Accessor::readable && (intrinsic_capability == Property_Capability::read || intrinsic_capability == Property_Capability::read_write);
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_assert(unique_single_value_categories<Attributes...>());
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>;
static_assert(!std::same_as<key_type, void>);
static_assert(key_type::value.view().size() > 0);
@@ -79,22 +79,6 @@ public:
}
};
}
enum class Managed_Access_Mode {
external,
persistence
};
namespace detail {
enum class Access_Kind {
intrinsic,
external,
persistence
};
constexpr Access_Kind access_kind(Managed_Access_Mode mode) noexcept {
return mode == Managed_Access_Mode::external ? Access_Kind::external : Access_Kind::persistence;
}
template <Managed_Access_Mode Mode>
inline constexpr Access_Kind access_kind_v = Mode == Managed_Access_Mode::external ? Access_Kind::external : Access_Kind::persistence;
}
enum class Runtime_Access_Result {
ok,
unknown_property,
@@ -107,8 +91,8 @@ 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&, detail::Access_Kind, std::string_view, void*, Runtime_Read_Callback);
Runtime_Access_Result (*write)(Property_Object_Base&, detail::Access_Kind, std::string_view, const std::type_info&, const void*);
Runtime_Access_Result (*read)(const Property_Object_Base&, std::string_view, void*, Runtime_Read_Callback);
Runtime_Access_Result (*write)(Property_Object_Base&, std::string_view, const std::type_info&, const void*);
};
const Runtime_Interface* runtime_interface_{};
protected:
@@ -128,26 +112,12 @@ public:
return runtime_interface_->property_count();
}
Runtime_Access_Result runtime_read(std::string_view key, void* context, Runtime_Read_Callback callback) const {
return runtime_interface_->read(*this, detail::Access_Kind::intrinsic, key, context, callback);
}
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, detail::access_kind(mode), key, context, callback);
return runtime_interface_->read(*this, key, context, callback);
}
Runtime_Access_Result runtime_write(std::string_view key, const std::type_info& value_type, const void* value) {
return runtime_interface_->write(*this, detail::Access_Kind::intrinsic, key, value_type, value);
}
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, detail::access_kind(mode), key, value_type, value);
return runtime_interface_->write(*this, key, value_type, value);
}
};
namespace detail {
template <Access_Kind Mode, class Schema, std::size_t Index>
inline constexpr bool property_read_allowed_v = Mode == Access_Kind::intrinsic ? Schema::template property_type<Index>::readable : Mode == Access_Kind::external ? external_readable_v<Schema, Index> : persistence_storable_v<Schema, Index>;
template <Access_Kind Mode, class Schema, std::size_t Index>
inline constexpr bool property_write_allowed_v = Mode == Access_Kind::intrinsic ? Schema::template property_type<Index>::writable : Mode == Access_Kind::external ? external_writable_v<Schema, Index> : persistence_loadable_v<Schema, Index>;
template <Access_Kind 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;
};
@@ -249,23 +219,20 @@ private:
}
count = write;
}
template <detail::Access_Kind 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>{detail::property_read_allowed_v<Mode, Schema, Index>...};
return std::array<bool, Schema::property_count>{Schema::template property_type<Index>::readable...};
}(std::make_index_sequence<Schema::property_count>{});
return table[index];
}
template <detail::Access_Kind Mode>
static bool runtime_property_visible(std::size_t index) {
static bool runtime_property_writable(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>{detail::property_visible_v<Mode, Schema, Index>...};
return std::array<bool, Schema::property_count>{Schema::template property_type<Index>::writable...};
}(std::make_index_sequence<Schema::property_count>{});
return table[index];
}
template <detail::Access_Kind 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;
@@ -276,9 +243,9 @@ private:
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);
bool allowed = shared_access ? runtime_property_readable(*index) : runtime_property_writable(*index);
if (!allowed) {
throw std::invalid_argument("Property is not accessible through this view: " + std::string(key));
throw std::invalid_argument(std::string(shared_access ? "Property is not readable: " : "Property is not writable: ") + std::string(key));
}
auto lock_slot = slot(*index);
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
@@ -293,10 +260,10 @@ private:
targets.unsynchronized_properties.erase(std::unique(targets.unsynchronized_properties.begin(), targets.unsynchronized_properties.end()), targets.unsynchronized_properties.end());
return targets;
}
template <detail::Access_Kind Mode, bool Shared_Access, std::size_t... Index>
template <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 ? detail::property_read_allowed_v<Mode, Schema, Index> : detail::property_visible_v<Mode, Schema, Index>) && ...));
static_assert(((Shared_Access ? Schema::template property_type<Index>::readable : Schema::template property_type<Index>::writable) && ...));
Static_Lock_Targets<sizeof...(Index)> targets;
auto collect = [&]<std::size_t Property_Index>() {
auto lock_slot = slot(Property_Index);
@@ -311,12 +278,12 @@ private:
sort_unique_prefix(targets.unsynchronized_properties, targets.unsynchronized_count);
return targets;
}
template <detail::Access_Kind Mode, bool Shared_Access>
template <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 ? detail::property_read_allowed_v<Mode, Schema, Index> : detail::property_write_allowed_v<Mode, Schema, Index>;
constexpr bool allowed = Shared_Access ? Schema::template property_type<Index>::readable : Schema::template property_type<Index>::writable;
if constexpr (allowed) {
auto lock_slot = slot(Index);
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
@@ -349,7 +316,6 @@ private:
const auto& descriptor = type_descriptor<Derived>().template property<Index>();
descriptor.accessor.write(static_cast<Derived&>(*this), std::forward<Value>(value));
}
template <detail::Access_Kind Mode>
class Single_Read_View {
const Property_Object* owner_{};
std::size_t property_index_{};
@@ -361,6 +327,11 @@ private:
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
using Property = typename Schema::template property_type<index>;
static_assert(Property::readable);
if constexpr (!Property::writable && !Property::accessor_type::synchronized_view_read) {
return owner_->template read_unlocked<index>(*this);
}
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");
}
@@ -371,23 +342,28 @@ private:
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
using Property = typename Schema::template property_type<index>;
static_assert(Property::readable);
if constexpr (!Property::writable && !Property::accessor_type::synchronized_view_read) {
return owner_->template read_unlocked<index>(*this);
}
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 <detail::Access_Kind Mode, std::size_t Index>
template <std::size_t Index>
auto read_one() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(detail::property_read_allowed_v<Mode, Schema, Index>);
static_assert(Schema::template property_type<Index>::readable);
using Property = typename Schema::template property_type<Index>;
using Value = typename Property::value_type;
if constexpr (!uses_real_mutexes && !Property::accessor_type::synchronized_view_read) {
if constexpr ((!Property::writable && !Property::accessor_type::synchronized_view_read) || (!uses_real_mutexes && !Property::accessor_type::synchronized_view_read)) {
return Value(read_unlocked<Index>(*this));
}
auto lock_slot = slot(Index);
Single_Read_View<Mode> view{*this, Index, lock_slot};
Single_Read_View view{*this, Index, lock_slot};
if constexpr (!uses_real_mutexes) {
return Value(read_unlocked<Index>(view));
}
@@ -397,10 +373,10 @@ private:
std::shared_lock lock{mutex(lock_slot)};
return Value(read_unlocked<Index>(view));
}
template <detail::Access_Kind Mode, std::size_t Index, class Value>
template <std::size_t Index, class Value>
void write_one(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(detail::property_write_allowed_v<Mode, Schema, Index>);
static_assert(Schema::template property_type<Index>::writable);
if constexpr (!uses_real_mutexes) {
write_unlocked<Index>(std::forward<Value>(value));
return;
@@ -414,7 +390,6 @@ private:
write_unlocked<Index>(std::forward<Value>(value));
}
private:
template <detail::Access_Kind Mode>
class Basic_Read_Guard {
const Property_Object* owner_{};
std::vector<std::size_t> slots_;
@@ -441,7 +416,7 @@ private:
decltype(auto) get_index() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(detail::property_read_allowed_v<Mode, Schema, Index>);
static_assert(Schema::template property_type<Index>::readable);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
@@ -462,7 +437,6 @@ private:
return get_index<index>();
}
};
template <detail::Access_Kind Mode>
class Basic_Write_Guard {
Property_Object* owner_{};
std::vector<std::size_t> slots_;
@@ -489,7 +463,7 @@ private:
decltype(auto) get_index() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(detail::property_read_allowed_v<Mode, Schema, Index>);
static_assert(Schema::template property_type<Index>::readable);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
@@ -513,7 +487,7 @@ private:
void set_index(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(detail::property_write_allowed_v<Mode, Schema, Index>);
static_assert(Schema::template property_type<Index>::writable);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
@@ -534,7 +508,7 @@ private:
set_index<index>(std::forward<Value>(value));
}
};
template <detail::Access_Kind Mode, std::size_t Capacity>
template <std::size_t Capacity>
class Static_Read_Guard {
const Property_Object* owner_{};
Static_Lock_Targets<Capacity> targets_;
@@ -559,7 +533,7 @@ private:
decltype(auto) get_index() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(detail::property_read_allowed_v<Mode, Schema, Index>);
static_assert(Schema::template property_type<Index>::readable);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
@@ -580,7 +554,7 @@ private:
return get_index<index>();
}
};
template <detail::Access_Kind Mode, std::size_t Capacity>
template <std::size_t Capacity>
class Static_Write_Guard {
Property_Object* owner_{};
Static_Lock_Targets<Capacity> targets_;
@@ -605,7 +579,7 @@ private:
decltype(auto) get_index() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(detail::property_read_allowed_v<Mode, Schema, Index>);
static_assert(Schema::template property_type<Index>::readable);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
@@ -629,7 +603,7 @@ private:
void set_index(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(detail::property_write_allowed_v<Mode, Schema, Index>);
static_assert(Schema::template property_type<Index>::writable);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
@@ -651,42 +625,38 @@ private:
}
};
public:
using Intrinsic_Read_Guard = Basic_Read_Guard<detail::Access_Kind::intrinsic>;
using Intrinsic_Write_Guard = Basic_Write_Guard<detail::Access_Kind::intrinsic>;
template <Managed_Access_Mode Mode>
using Read_Guard = Basic_Read_Guard<detail::access_kind_v<Mode>>;
template <Managed_Access_Mode Mode>
using Write_Guard = Basic_Write_Guard<detail::access_kind_v<Mode>>;
using Read_Guard = Basic_Read_Guard;
using Write_Guard = Basic_Write_Guard;
private:
template <detail::Access_Kind Mode, class Function>
template <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 (detail::property_read_allowed_v<Mode, Schema, property_index>) {
auto value = read_one<Mode, property_index>();
if constexpr (Schema::template property_type<property_index>::readable) {
auto value = read_one<property_index>();
std::invoke(function, index, descriptor, value);
}
});
}
template <detail::Access_Kind Mode, class Function>
template <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)};
auto targets = collect_all_static_lock_targets<true>();
Static_Read_Guard<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 (detail::property_read_allowed_v<Mode, Schema, property_index>) {
if constexpr (Schema::template property_type<property_index>::readable) {
std::invoke(function, index, descriptor, guard.template get_index<property_index>());
}
});
}
template <detail::Access_Kind Mode, class Function>
template <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)};
auto targets = collect_all_static_lock_targets<false>();
Static_Write_Guard<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 {
@@ -696,63 +666,64 @@ private:
using Schema = type_descriptor_schema_t<Derived>;
return Schema::property_count;
}
template <detail::Access_Kind Mode>
Runtime_Access_Result runtime_read_mode(std::string_view key, void* context, Runtime_Read_Callback callback) const {
Runtime_Access_Result runtime_read_impl_local(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)) {
if (!runtime_property_readable(*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);
using Property = typename Schema::template property_type<property_index>;
using Value = typename Property::value_type;
if constexpr (!Property::writable && !Property::accessor_type::synchronized_view_read) {
if constexpr (std::is_reference_v<decltype(read_unlocked<property_index>(*this))>) {
auto&& value = read_unlocked<property_index>(*this);
callback(context, property_index, key, typeid(Value), std::addressof(value));
} else {
Value value = read_unlocked<property_index>(view);
Value value = read_unlocked<property_index>(*this);
callback(context, property_index, key, typeid(Value), std::addressof(value));
}
};
if constexpr (!uses_real_mutexes) {
emit();
} else if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
emit();
} else {
std::shared_lock lock{mutex(lock_slot)};
emit();
auto lock_slot = slot(property_index);
Single_Read_View 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 constexpr (!uses_real_mutexes) {
emit();
} else if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
emit();
} else {
std::shared_lock lock{mutex(lock_slot)};
emit();
}
}
result = Runtime_Access_Result::ok;
});
return result;
}
static Runtime_Access_Result runtime_read_impl(const Property_Object_Base& base, detail::Access_Kind mode, std::string_view key, void* context, Runtime_Read_Callback callback) {
static Runtime_Access_Result runtime_read_impl(const Property_Object_Base& base, std::string_view key, void* context, Runtime_Read_Callback callback) {
const auto& self = static_cast<const Property_Object&>(base);
switch (mode) {
case detail::Access_Kind::intrinsic:
return self.template runtime_read_mode<detail::Access_Kind::intrinsic>(key, context, callback);
case detail::Access_Kind::external:
return self.template runtime_read_mode<detail::Access_Kind::external>(key, context, callback);
case detail::Access_Kind::persistence:
return self.template runtime_read_mode<detail::Access_Kind::persistence>(key, context, callback);
}
return Runtime_Access_Result::not_readable;
return self.runtime_read_impl_local(key, context, callback);
}
template <detail::Access_Kind Mode>
Runtime_Access_Result runtime_write_mode(std::string_view key, const std::type_info& value_type, const void* value) {
Runtime_Access_Result runtime_write_impl_local(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)) {
if (!runtime_property_writable(*index)) {
return Runtime_Access_Result::not_writable;
}
Runtime_Access_Result result = Runtime_Access_Result::unknown_property;
@@ -761,28 +732,20 @@ private:
using Property = typename Schema::template property_type<property_index>;
using Accessor = typename Property::accessor_type;
using Value = typename Property::value_type;
if constexpr (!detail::property_write_allowed_v<Mode, Schema, property_index> || !requires(const Accessor& accessor, Derived& object, const Value& candidate) { accessor.write(object, candidate); }) {
if constexpr (!Schema::template property_type<property_index>::writable || !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));
write_one<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, detail::Access_Kind mode, std::string_view key, const std::type_info& value_type, const void* value) {
static Runtime_Access_Result runtime_write_impl(Property_Object_Base& base, std::string_view key, const std::type_info& value_type, const void* value) {
auto& self = static_cast<Property_Object&>(base);
switch (mode) {
case detail::Access_Kind::intrinsic:
return self.template runtime_write_mode<detail::Access_Kind::intrinsic>(key, value_type, value);
case detail::Access_Kind::external:
return self.template runtime_write_mode<detail::Access_Kind::external>(key, value_type, value);
case detail::Access_Kind::persistence:
return self.template runtime_write_mode<detail::Access_Kind::persistence>(key, value_type, value);
}
return Runtime_Access_Result::not_writable;
return self.runtime_write_impl_local(key, value_type, value);
}
static const Property_Object_Base::Runtime_Interface* runtime_interface() noexcept {
static const Property_Object_Base::Runtime_Interface value{
@@ -830,32 +793,32 @@ public:
return static_cast<const Derived&>(*this);
}
template <auto Member>
auto read() const {
auto read() const requires Schema_Readable_Property_Member<type_descriptor_schema_t<Derived>, Member> {
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<detail::Access_Kind::intrinsic, index>();
return read_one<index>();
}
template <Fixed_String Key>
auto read_key() const {
auto read_key() const requires Schema_Readable_Property_Key<type_descriptor_schema_t<Derived>, Key> {
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<detail::Access_Kind::intrinsic, index>();
return read_one<index>();
}
template <auto Member, class Value>
void write(Value&& value) {
void write(Value&& value) requires Schema_Writable_Property_Member<type_descriptor_schema_t<Derived>, Member> {
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<detail::Access_Kind::intrinsic, index>(std::forward<Value>(value));
write_one<index>(std::forward<Value>(value));
}
template <Fixed_String Key, class Value>
void write_key(Value&& value) {
void write_key(Value&& value) requires Schema_Writable_Property_Key<type_descriptor_schema_t<Derived>, Key> {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
write_one<detail::Access_Kind::intrinsic, index>(std::forward<Value>(value));
write_one<index>(std::forward<Value>(value));
}
template <auto Member>
std::size_t lock_slot() const noexcept {
@@ -865,209 +828,64 @@ public:
return slot(index);
}
private:
template <detail::Access_Kind Mode>
Basic_Read_Guard<Mode> lock_shared_mode(std::span<const std::string_view> keys) const {
return Basic_Read_Guard<Mode>{*this, collect_dynamic_lock_targets<Mode>(keys, true)};
Basic_Read_Guard lock_shared_impl(std::span<const std::string_view> keys) const {
return Basic_Read_Guard{*this, collect_dynamic_lock_targets(keys, true)};
}
template <detail::Access_Kind Mode>
Basic_Read_Guard<Mode> lock_shared_mode(std::initializer_list<std::string_view> keys) const {
return lock_shared_mode<Mode>(std::span<const std::string_view>{keys.begin(), keys.size()});
Basic_Read_Guard lock_shared_impl(std::initializer_list<std::string_view> keys) const {
return lock_shared_impl(std::span<const std::string_view>{keys.begin(), keys.size()});
}
template <detail::Access_Kind Mode>
Basic_Write_Guard<Mode> lock_unique_mode(std::span<const std::string_view> keys) {
return Basic_Write_Guard<Mode>{*this, collect_dynamic_lock_targets<Mode>(keys, false)};
Basic_Write_Guard lock_unique_impl(std::span<const std::string_view> keys) {
return Basic_Write_Guard{*this, collect_dynamic_lock_targets(keys, false)};
}
template <detail::Access_Kind Mode>
Basic_Write_Guard<Mode> lock_unique_mode(std::initializer_list<std::string_view> keys) {
return lock_unique_mode<Mode>(std::span<const std::string_view>{keys.begin(), keys.size()});
Basic_Write_Guard lock_unique_impl(std::initializer_list<std::string_view> keys) {
return lock_unique_impl(std::span<const std::string_view>{keys.begin(), keys.size()});
}
template <detail::Access_Kind Mode, auto... Members>
auto lock_shared_mode() const {
template <auto... Members>
auto lock_shared_impl() 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)};
auto targets = collect_static_lock_targets<true, schema_member_property_index_v<Schema, Members>...>();
return Static_Read_Guard<sizeof...(Members)>{*this, std::move(targets)};
}
template <detail::Access_Kind Mode, auto... Members>
auto lock_unique_mode() {
template <auto... Members>
auto lock_unique_impl() {
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)};
auto targets = collect_static_lock_targets<false, schema_member_property_index_v<Schema, Members>...>();
return Static_Write_Guard<sizeof...(Members)>{*this, std::move(targets)};
}
public:
Intrinsic_Read_Guard lock_shared(std::span<const std::string_view> keys) const {
return lock_shared_mode<detail::Access_Kind::intrinsic>(keys);
Read_Guard lock_shared(std::span<const std::string_view> keys) const {
return lock_shared_impl(keys);
}
Intrinsic_Read_Guard lock_shared(std::initializer_list<std::string_view> keys) const {
return lock_shared_mode<detail::Access_Kind::intrinsic>(keys);
Read_Guard lock_shared(std::initializer_list<std::string_view> keys) const {
return lock_shared_impl(keys);
}
Intrinsic_Write_Guard lock_unique(std::span<const std::string_view> keys) {
return lock_unique_mode<detail::Access_Kind::intrinsic>(keys);
Write_Guard lock_unique(std::span<const std::string_view> keys) {
return lock_unique_impl(keys);
}
Intrinsic_Write_Guard lock_unique(std::initializer_list<std::string_view> keys) {
return lock_unique_mode<detail::Access_Kind::intrinsic>(keys);
Write_Guard lock_unique(std::initializer_list<std::string_view> keys) {
return lock_unique_impl(keys);
}
template <auto... Members>
auto lock_shared() const {
return lock_shared_mode<detail::Access_Kind::intrinsic, Members...>();
auto lock_shared() const requires (Schema_Readable_Property_Member<type_descriptor_schema_t<Derived>, Members> && ...) {
return lock_shared_impl<Members...>();
}
template <auto... Members>
auto lock_unique() {
return lock_unique_mode<detail::Access_Kind::intrinsic, Members...>();
auto lock_unique() requires (Schema_Writable_Property_Member<type_descriptor_schema_t<Derived>, Members> && ...) {
return lock_unique_impl<Members...>();
}
template <class Function>
void for_each_readable(Function&& function) const {
for_each_readable_impl<detail::Access_Kind::intrinsic>(std::forward<Function>(function));
for_each_readable_impl(std::forward<Function>(function));
}
template <class Function>
void for_each_readable_locked(Function&& function) const {
for_each_readable_locked_impl<detail::Access_Kind::intrinsic>(std::forward<Function>(function));
for_each_readable_locked_impl(std::forward<Function>(function));
}
template <class Function>
void with_all_writable_locked(Function&& function) {
with_all_writable_locked_impl<detail::Access_Kind::intrinsic>(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<detail::access_kind_v<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<detail::access_kind_v<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<detail::access_kind_v<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<detail::access_kind_v<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<detail::access_kind_v<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<detail::access_kind_v<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<detail::access_kind_v<Mode>, Members...>();
}
template <auto... Members>
auto lock_unique() {
return owner_->template lock_unique_mode<detail::access_kind_v<Mode>, Members...>();
}
Read_Guard<Mode> lock_shared(std::span<const std::string_view> keys) const {
return owner_->template lock_shared_mode<detail::access_kind_v<Mode>>(keys);
}
Write_Guard<Mode> lock_unique(std::span<const std::string_view> keys) {
return owner_->template lock_unique_mode<detail::access_kind_v<Mode>>(keys);
}
template <class Function>
void for_each_readable(Function&& function) const {
owner_->template for_each_readable_impl<detail::access_kind_v<Mode>>(std::forward<Function>(function));
}
template <class Function>
void for_each_readable_locked(Function&& function) const {
owner_->template for_each_readable_locked_impl<detail::access_kind_v<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<detail::access_kind_v<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<detail::access_kind_v<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<detail::access_kind_v<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<detail::access_kind_v<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<detail::access_kind_v<Mode>, Members...>();
}
Read_Guard<Mode> lock_shared(std::span<const std::string_view> keys) const {
return owner_->template lock_shared_mode<detail::access_kind_v<Mode>>(keys);
}
template <class Function>
void for_each_readable(Function&& function) const {
owner_->template for_each_readable_impl<detail::access_kind_v<Mode>>(std::forward<Function>(function));
}
template <class Function>
void for_each_readable_locked(Function&& function) const {
owner_->template for_each_readable_locked_impl<detail::access_kind_v<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};
with_all_writable_locked_impl(std::forward<Function>(function));
}
};
}
+19 -36
View File
@@ -211,14 +211,8 @@ public:
};
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>;
@@ -236,37 +230,18 @@ constexpr decltype(auto) declared_effective_attribute(const Schema& schema) requ
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>();
};
concept Valid_Property_Schema = Property_Schema<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, 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>;
@@ -318,6 +293,14 @@ Resolved_Synchronization_Plan<Schema::property_count> resolve_synchronization_pl
logical[index] = token;
}
}
constexpr auto requires_synchronization = []<std::size_t... Index>(std::index_sequence<Index...>) {
return std::array<bool, Schema::property_count>{property_requires_synchronization_v<Schema, Index>...};
}(std::make_index_sequence<Schema::property_count>{});
for (std::size_t index = 0; index < Schema::property_count; ++index) {
if (!requires_synchronization[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;
+123 -64
View File
@@ -34,31 +34,31 @@ template <>
struct structive::Type_Descriptor<Device> {
static auto get() {
return object<Device>(
defaults(external_access<External_Access::read_write>, persistence_access<Persistence_Access::load_store>),
synchronization(sync_all_independent, sync_group < &Device::min_speed, &Device::max_speed > ("speed_range"), sync_unsynchronized<&Device::immutable_id>()),
field < &Device::temperature > (key < "temperature" >, min_value < -50 >, max_value < 200 >, unit < "C" >, test_tag<7>),
field < &Device::pressure > (key < "pressure" >),
field < &Device::min_speed > (key < "minimum_speed" >),
field < &Device::max_speed > (key < "maximum_speed" >),
field < &Device::immutable_id > (key < "immutable_id" >, external_access<External_Access::read>, persistence_access<Persistence_Access::store>)
synchronization(sync_all_independent, sync_group<&Device::min_speed, &Device::max_speed>("speed_range")),
field<&Device::temperature>(key<"temperature">, min_value<-50>, max_value<200>, unit<"C">, test_tag<7>),
field<&Device::pressure>(key<"pressure">),
field<&Device::min_speed>(key<"minimum_speed">),
field<&Device::max_speed>(key<"maximum_speed">),
field<&Device::immutable_id>(key<"immutable_id">, read_only)
);
}
};
struct Computed_Device : Property_Object<Computed_Device> {
int min_speed{10};
int max_speed{100};
int fixed_offset{5};
};
template <>
struct structive::Type_Descriptor<Computed_Device> {
static auto get() {
return object<Computed_Device>(
defaults(external_access<External_Access::read_write>),
synchronization(sync_all_independent, sync_group("speed", "min_speed", "max_speed", "speed_span")),
field < &Computed_Device::min_speed > (key < "min_speed" >),
field < &Computed_Device::max_speed > (key < "max_speed" >),
field<&Computed_Device::min_speed>(key<"min_speed">),
field<&Computed_Device::max_speed>(key<"max_speed">),
field<&Computed_Device::fixed_offset>(key<"fixed_offset">, read_only),
computed_property<Computed_Device, int>([](const auto& view) {
return view.template get<&Computed_Device::max_speed>() - view.template get<&Computed_Device::min_speed>();
}, key < "speed_span" >, external_access<External_Access::read>)
return view.template get<&Computed_Device::max_speed>() - view.template get<&Computed_Device::min_speed>() + view.template get<&Computed_Device::fixed_offset>();
}, key<"speed_span">)
);
}
};
@@ -68,10 +68,7 @@ struct Non_Copyable_Device : Property_Object<Non_Copyable_Device> {
template <>
struct structive::Type_Descriptor<Non_Copyable_Device> {
static auto get() {
return object<Non_Copyable_Device>(
defaults(external_access<External_Access::read>),
field < &Non_Copyable_Device::payload > (key < "payload" >)
);
return object<Non_Copyable_Device>(field<&Non_Copyable_Device::payload>(key<"payload">));
}
};
struct Lockless_Device : Property_Object<Lockless_Device, No_Lock_Policy> {
@@ -85,29 +82,63 @@ struct structive::Type_Descriptor<Lockless_Device> {
static auto get() {
return object<Lockless_Device>(
synchronization(sync_all_independent, sync_group("sum", "left", "right", "sum")),
field < &Lockless_Device::left > (key < "left" >),
field < &Lockless_Device::right > (key < "right" >),
field<&Lockless_Device::left>(key<"left">),
field<&Lockless_Device::right>(key<"right">),
computed_property<Lockless_Device, int>([](const auto& view) {
return view.template get<&Lockless_Device::left>() + view.template get<&Lockless_Device::right>();
}, key < "sum" >)
}, key<"sum">)
);
}
};
template <class View>
concept Has_Write_Temperature = requires(View view) {
view.template write<&Device::temperature>(1);
struct Read_Only_Device : Property_Object<Read_Only_Device> {
int id{11};
int version{3};
int value{9};
};
template <>
struct structive::Type_Descriptor<Read_Only_Device> {
static auto get() {
return object<Read_Only_Device>(
synchronization(sync_all_shared),
field<&Read_Only_Device::id>(key<"id">, read_only),
field<&Read_Only_Device::version>(key<"version">, read_only),
field<&Read_Only_Device::value>(key<"value">)
);
}
};
struct Pure_Read_Only_Device : Property_Object<Pure_Read_Only_Device> {
int id{21};
int version{4};
};
template <>
struct structive::Type_Descriptor<Pure_Read_Only_Device> {
static auto get() {
return object<Pure_Read_Only_Device>(
synchronization(sync_all_shared),
field<&Pure_Read_Only_Device::id>(key<"id">, read_only),
field<&Pure_Read_Only_Device::version>(key<"version">, read_only)
);
}
};
template <class Object>
concept Can_Write_Immutable = requires(Object& object) {
object.template write<&Device::immutable_id>(1);
};
template <class Object>
concept Can_Lock_Immutable_Unique = requires(Object& object) {
object.template lock_unique<&Device::immutable_id>();
};
static bool update_speed_range(Device& device, int min_speed, int max_speed) {
auto guard = device.lock_unique<&Device::min_speed, &Device::max_speed>();
int old_min = guard.get<&Device::min_speed>();
int old_max = guard.get<&Device::max_speed>();
guard.set < &Device::min_speed > (min_speed);
guard.set < &Device::max_speed > (max_speed);
guard.set<&Device::min_speed>(min_speed);
guard.set<&Device::max_speed>(max_speed);
if (min_speed <= max_speed) {
return true;
}
guard.set < &Device::min_speed > (old_min);
guard.set < &Device::max_speed > (old_max);
guard.set<&Device::min_speed>(old_min);
guard.set<&Device::max_speed>(old_max);
return false;
}
static void runtime_read_int(void* context, std::size_t, std::string_view, const std::type_info& type, const void* value) {
@@ -116,10 +147,15 @@ static void runtime_read_int(void* context, std::size_t, std::string_view, const
}
int main() {
static_assert(Property_Described_Object<Device>);
static_assert(!Can_Write_Immutable<Device>);
static_assert(!Can_Lock_Immutable_Unique<Device>);
const auto& schema = type_descriptor<Device>();
using Schema = type_descriptor_schema_t<Device>;
static_assert(Valid_Property_Schema<Schema>);
static_assert(Schema::property_count == 5);
using Immutable_Property = typename Schema::template property_type<4>;
static_assert(Immutable_Property::readable);
static_assert(!Immutable_Property::writable);
REQUIRE(schema.template property<0>().key() == "temperature");
REQUIRE(schema.template property<&Device::temperature>().key() == "temperature");
REQUIRE(schema.template property<&Device::min_speed>().key() == "minimum_speed");
@@ -128,20 +164,24 @@ int main() {
static_assert(Temperature_Property::template has_attribute<Test_Tag_Category>);
REQUIRE(Temperature_Property::template attribute_type<Test_Tag_Category>::value == 7);
Device device;
REQUIRE(device.resolved_synchronization().lock_count == 3);
REQUIRE(device.lock_slot<&Device::immutable_id>() == Resolved_Synchronization_View::unsynchronized_slot);
REQUIRE(device.temperature == 20);
device.temperature = 21;
REQUIRE(device.read<&Device::temperature>() == 21);
device.write < &Device::temperature > (22);
device.write<&Device::temperature>(22);
REQUIRE(device.temperature == 22);
REQUIRE(device.read<&Device::immutable_id>() == 7);
REQUIRE(&device.unsafe_object() == &device);
REQUIRE(&device.schema() == &schema);
REQUIRE(device.lock_slot<&Device::min_speed>() == device.lock_slot<&Device::max_speed>());
REQUIRE(device.lock_slot<&Device::temperature>() != device.lock_slot<&Device::pressure>());
REQUIRE(device.lock_slot<&Device::immutable_id>() == Resolved_Synchronization_View::unsynchronized_slot);
Device shared_device{property_synchronization(synchronization(sync_all_shared))};
REQUIRE(shared_device.resolved_synchronization().lock_count == 1);
REQUIRE(shared_device.lock_slot<&Device::temperature>() == shared_device.lock_slot<&Device::pressure>());
REQUIRE(shared_device.lock_slot<&Device::pressure>() == shared_device.lock_slot<&Device::min_speed>());
Device grouped_device{property_synchronization<Device>(synchronization(sync_all_independent, sync_group < &Device::temperature, &Device::pressure > ("environment")))};
REQUIRE(shared_device.lock_slot<&Device::immutable_id>() == Resolved_Synchronization_View::unsynchronized_slot);
Device grouped_device{property_synchronization<Device>(synchronization(sync_all_independent, sync_group<&Device::temperature, &Device::pressure>("environment")))};
REQUIRE(grouped_device.lock_slot<&Device::temperature>() == grouped_device.lock_slot<&Device::pressure>());
Device shared_copy{shared_device};
REQUIRE(shared_copy.lock_slot<&Device::temperature>() == shared_copy.lock_slot<&Device::pressure>());
@@ -151,11 +191,7 @@ int main() {
auto assignment_target_slot = assignment_target.lock_slot<&Device::pressure>();
assignment_target = shared_device;
REQUIRE(assignment_target.lock_slot<&Device::pressure>() == assignment_target_slot);
device.external().write < &Device::temperature > (30);
REQUIRE(device.external().read<&Device::temperature>() == 30);
device.persistence().load < &Device::pressure > (101);
REQUIRE(device.persistence().store<&Device::pressure>() == 101);
auto validation = validate_property_value < &Device::temperature > (device.schema(), 500);
auto validation = validate_property_value<&Device::temperature>(device.schema(), 500);
REQUIRE(validation.has_value());
REQUIRE(validation->code == "max_value");
REQUIRE(!update_speed_range(device, 200, 100));
@@ -170,24 +206,25 @@ int main() {
});
REQUIRE(schema_visits == 5);
std::size_t value_visits = 0;
device.external().for_each_readable_locked([&](auto, const auto&, const auto&) {
device.for_each_readable_locked([&](auto, const auto&, const auto&) {
++value_visits;
});
REQUIRE(value_visits == 5);
Computed_Device computed;
REQUIRE(computed.external().read_key<"speed_span">() == 90);
computed.write < &Computed_Device::min_speed > (20);
REQUIRE(computed.external().read_key<"speed_span">() == 80);
REQUIRE(computed.lock_slot<&Computed_Device::fixed_offset>() == Resolved_Synchronization_View::unsynchronized_slot);
REQUIRE(computed.read_key<"speed_span">() == 95);
computed.write<&Computed_Device::min_speed>(20);
REQUIRE(computed.read_key<"speed_span">() == 85);
Lockless_Device lockless;
REQUIRE(lockless.read<&Lockless_Device::left>() == 1);
lockless.write < &Lockless_Device::right > (4);
lockless.write<&Lockless_Device::right>(4);
REQUIRE(lockless.read_key<"sum">() == 5);
auto lockless_guard = lockless.lock_shared<&Lockless_Device::left, &Lockless_Device::right>();
REQUIRE(lockless_guard.get<&Lockless_Device::left>() == 1);
REQUIRE(lockless_guard.get<&Lockless_Device::right>() == 4);
Non_Copyable_Device non_copyable;
bool non_copyable_visited = false;
non_copyable.external().for_each_readable_locked([&](auto, const auto&, const auto& value) {
non_copyable.for_each_readable_locked([&](auto, const auto&, const auto& value) {
REQUIRE(*value == 42);
non_copyable_visited = true;
});
@@ -197,27 +234,41 @@ int main() {
REQUIRE(erased.runtime_property_count() == 5);
int runtime_value = 0;
REQUIRE(erased.runtime_read("temperature", &runtime_value, &runtime_read_int) == Runtime_Access_Result::ok);
REQUIRE(runtime_value == 30);
int intrinsic_runtime_write_value = 34;
REQUIRE(erased.runtime_write("immutable_id", typeid(int), &intrinsic_runtime_write_value) == Runtime_Access_Result::ok);
REQUIRE(device.immutable_id == 34);
REQUIRE(erased.runtime_read(Managed_Access_Mode::external, "temperature", &runtime_value, &runtime_read_int) == Runtime_Access_Result::ok);
REQUIRE(runtime_value == 30);
REQUIRE(runtime_value == 22);
REQUIRE(erased.runtime_read("immutable_id", &runtime_value, &runtime_read_int) == Runtime_Access_Result::ok);
REQUIRE(runtime_value == 7);
int runtime_write_value = 35;
REQUIRE(erased.runtime_write(Managed_Access_Mode::external, "temperature", typeid(int), &runtime_write_value) == Runtime_Access_Result::ok);
REQUIRE(erased.runtime_write("temperature", typeid(int), &runtime_write_value) == Runtime_Access_Result::ok);
REQUIRE(device.temperature == 35);
REQUIRE(erased.runtime_write(Managed_Access_Mode::external, "immutable_id", typeid(int), &runtime_write_value) == Runtime_Access_Result::not_writable);
int persistence_write_value = 102;
REQUIRE(erased.runtime_write(Managed_Access_Mode::persistence, "pressure", typeid(int), &persistence_write_value) == Runtime_Access_Result::ok);
REQUIRE(device.pressure == 102);
REQUIRE(erased.runtime_read(Managed_Access_Mode::persistence, "immutable_id", &runtime_value, &runtime_read_int) == Runtime_Access_Result::ok);
REQUIRE(runtime_value == 34);
REQUIRE(erased.runtime_write(Managed_Access_Mode::persistence, "immutable_id", typeid(int), &persistence_write_value) == Runtime_Access_Result::not_writable);
REQUIRE(erased.runtime_write("immutable_id", typeid(int), &runtime_write_value) == Runtime_Access_Result::not_writable);
double wrong_type = 1.0;
REQUIRE(erased.runtime_write(Managed_Access_Mode::external, "temperature", typeid(double), &wrong_type) == Runtime_Access_Result::type_mismatch);
REQUIRE(erased.runtime_write(Managed_Access_Mode::external, "missing", typeid(int), &runtime_write_value) == Runtime_Access_Result::unknown_property);
const Device& const_device = device;
static_assert(!Has_Write_Temperature<decltype(const_device.external())>);
REQUIRE(erased.runtime_write("temperature", typeid(double), &wrong_type) == Runtime_Access_Result::type_mismatch);
REQUIRE(erased.runtime_write("missing", typeid(int), &runtime_write_value) == Runtime_Access_Result::unknown_property);
Read_Only_Device read_only_device;
REQUIRE(read_only_device.resolved_synchronization().lock_count == 1);
REQUIRE(read_only_device.lock_slot<&Read_Only_Device::id>() == Resolved_Synchronization_View::unsynchronized_slot);
REQUIRE(read_only_device.lock_slot<&Read_Only_Device::version>() == Resolved_Synchronization_View::unsynchronized_slot);
REQUIRE(read_only_device.lock_slot<&Read_Only_Device::value>() == 0);
Pure_Read_Only_Device pure_read_only;
REQUIRE(pure_read_only.resolved_synchronization().lock_count == 0);
REQUIRE(pure_read_only.lock_slot<&Pure_Read_Only_Device::id>() == Resolved_Synchronization_View::unsynchronized_slot);
REQUIRE(pure_read_only.lock_slot<&Pure_Read_Only_Device::version>() == Resolved_Synchronization_View::unsynchronized_slot);
REQUIRE(pure_read_only.read<&Pure_Read_Only_Device::id>() == 21);
REQUIRE(pure_read_only.read<&Pure_Read_Only_Device::version>() == 4);
Property_Object_Base& read_only_erased = read_only_device;
std::binary_semaphore read_only_done{0};
std::jthread read_only_reader;
{
auto guard = read_only_device.lock_unique<&Read_Only_Device::value>();
read_only_reader = std::jthread([&] {
int value = 0;
REQUIRE(read_only_erased.runtime_read("id", &value, &runtime_read_int) == Runtime_Access_Result::ok);
REQUIRE(value == 11);
read_only_done.release();
});
REQUIRE(read_only_done.try_acquire_for(std::chrono::milliseconds(100)));
}
read_only_reader.join();
std::binary_semaphore runtime_blocked_done{0};
std::jthread runtime_writer;
{
@@ -236,25 +287,33 @@ int main() {
{
auto guard = device.lock_unique({"temperature"});
std::jthread writer([&] {
device.write < &Device::pressure > (200);
device.write<&Device::pressure>(200);
independent_done.release();
});
REQUIRE(independent_done.try_acquire_for(std::chrono::seconds(2)));
}
bool dynamic_read_only_unique_rejected = false;
try {
auto guard = device.lock_unique({"immutable_id"});
(void) guard;
} catch (const std::invalid_argument&) {
dynamic_read_only_unique_rejected = true;
}
REQUIRE(dynamic_read_only_unique_rejected);
std::barrier gate(2);
std::atomic<int> completed{0};
std::jthread first([&] {
gate.arrive_and_wait();
auto guard = device.lock_unique({"temperature", "pressure"});
guard.set < &Device::temperature > (40);
guard.set < &Device::pressure > (140);
guard.set<&Device::temperature>(40);
guard.set<&Device::pressure>(140);
completed.fetch_add(1, std::memory_order_release);
});
std::jthread second([&] {
gate.arrive_and_wait();
auto guard = device.lock_unique({"pressure", "temperature"});
guard.set < &Device::pressure > (141);
guard.set < &Device::temperature > (41);
guard.set<&Device::pressure>(141);
guard.set<&Device::temperature>(41);
completed.fetch_add(1, std::memory_order_release);
});
first.join();
@@ -262,7 +321,7 @@ int main() {
REQUIRE(completed.load(std::memory_order_acquire) == 2);
Device copied = device;
REQUIRE(copied.temperature == device.temperature);
copied.write < &Device::temperature > (99);
copied.write<&Device::temperature>(99);
REQUIRE(device.read<&Device::temperature>() != copied.read<&Device::temperature>());
return 0;
}