diff --git a/README.md b/README.md index bf1f502..470dc0d 100644 --- a/README.md +++ b/README.md @@ -170,24 +170,32 @@ The second is the **managed path**. It resolves the registered property and uses Structive intentionally keeps both. A codebase should choose the appropriate path according to its ownership and concurrency rules. -## Access capability views +## Intrinsic capability and boundary projections -Core defines three managed access modes: +A property first has an **intrinsic capability** derived from its accessor: -- `internal`: normal managed access from application code. -- `external`: controlled by `External_Access` metadata. -- `persistence`: controlled by `Persistence_Access` metadata. +- readable if the accessor can read it; +- writable if the accessor can write it. -Example: +Normal managed application code uses that intrinsic capability directly: ```cpp device.write<&Device::temperature>(30.0); +auto value = device.read<&Device::temperature>(); +``` + +Core then defines two boundary projections over the same property definition: + +- `external`: controlled by `External_Access` metadata; +- `persistence`: controlled by `Persistence_Access` metadata. + +```cpp device.external().write<&Device::temperature>(31.0); device.persistence().load<&Device::temperature>(32.0); auto stored = device.persistence().store<&Device::temperature>(); ``` -Capability semantics are checked by the schema: a property cannot advertise readable/writable capabilities that its accessor does not actually support. +There is no separate `internal` capability mode. The default managed API is the intrinsic property capability itself. `Managed_Access_Mode` therefore identifies only boundary projections. A projection can only narrow abilities that the accessor actually provides; it cannot make an intrinsically unreadable or unwritable property readable or writable. ## Validation is explicit diff --git a/README.zh-CN.md b/README.zh-CN.md index d003f74..0a9f02e 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -170,22 +170,32 @@ device.write<&Device::temperature>(30.0); Structive 有意保留两条路径。业务代码应该根据对象所有权和并发规则选择,而不是假装所有成员访问都能被框架强制拦截。 -## 访问能力视图 +## 固有能力与边界投影 -Core 当前定义三种受管理访问模式: +一个 Property 首先拥有由 accessor 自身决定的 **固有能力(intrinsic capability)**: -- `internal`:应用内部正常受管理访问。 -- `external`:由 `External_Access` 元数据控制。 -- `persistence`:由 `Persistence_Access` 元数据控制。 +- accessor 能读,则 Property intrinsically readable; +- accessor 能写,则 Property intrinsically writable。 + +应用内部的普通 managed code 直接使用这套固有能力: ```cpp device.write<&Device::temperature>(30.0); +auto value = device.read<&Device::temperature>(); +``` + +Core 在同一份 Property definition 之上只定义两种边界投影: + +- `external`:由 `External_Access` 元数据控制; +- `persistence`:由 `Persistence_Access` 元数据控制。 + +```cpp device.external().write<&Device::temperature>(31.0); device.persistence().load<&Device::temperature>(32.0); auto stored = device.persistence().store<&Device::temperature>(); ``` -Schema 会在编译期约束 capability 与 accessor 能力一致,例如只读 computed property 不能声明为可写。 +Structive 不再定义单独的 `internal` capability mode。默认 managed API 本身就是 Property 的固有能力,因此 `Managed_Access_Mode` 只用于标识边界投影。投影只能收窄 accessor 原本具备的能力,不能把 intrinsically 不可读或不可写的 Property 变成可读或可写。 ## Validation 必须显式 diff --git a/core/include/structive/property/property_object.hpp b/core/include/structive/property/property_object.hpp index 9e8aaa5..234694f 100644 --- a/core/include/structive/property/property_object.hpp +++ b/core/include/structive/property/property_object.hpp @@ -80,10 +80,21 @@ public: }; } enum class Managed_Access_Mode { - internal, 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 +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, @@ -96,8 +107,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&, Managed_Access_Mode, std::string_view, void*, Runtime_Read_Callback); - Runtime_Access_Result (*write)(Property_Object_Base&, Managed_Access_Mode, std::string_view, const std::type_info&, const void*); + 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*); }; const Runtime_Interface* runtime_interface_{}; protected: @@ -116,19 +127,27 @@ public: std::size_t runtime_property_count() const noexcept { 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, mode, key, context, callback); + return runtime_interface_->read(*this, detail::access_kind(mode), 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, mode, key, value_type, value); + return runtime_interface_->write(*this, detail::access_kind(mode), key, value_type, value); } }; -template -inline constexpr bool property_read_allowed_v = Mode == Managed_Access_Mode::internal ? Schema::template property_type::readable : Mode == Managed_Access_Mode::external ? external_readable_v : persistence_storable_v; -template -inline constexpr bool property_write_allowed_v = Mode == Managed_Access_Mode::internal ? Schema::template property_type::writable : Mode == Managed_Access_Mode::external ? external_writable_v : persistence_loadable_v; -template +namespace detail { +template +inline constexpr bool property_read_allowed_v = Mode == Access_Kind::intrinsic ? Schema::template property_type::readable : Mode == Access_Kind::external ? external_readable_v : persistence_storable_v; +template +inline constexpr bool property_write_allowed_v = Mode == Access_Kind::intrinsic ? Schema::template property_type::writable : Mode == Access_Kind::external ? external_writable_v : persistence_loadable_v; +template inline constexpr bool property_visible_v = property_read_allowed_v || property_write_allowed_v; +} struct Property_Synchronization { Synchronization_Plan plan; }; @@ -230,23 +249,23 @@ private: } count = write; } - template + template static bool runtime_property_readable(std::size_t index) { using Schema = type_descriptor_schema_t; static const auto table = [](std::index_sequence) { - return std::array{property_read_allowed_v...}; + return std::array{detail::property_read_allowed_v...}; }(std::make_index_sequence{}); return table[index]; } - template + template static bool runtime_property_visible(std::size_t index) { using Schema = type_descriptor_schema_t; static const auto table = [](std::index_sequence) { - return std::array{property_visible_v...}; + return std::array{detail::property_visible_v...}; }(std::make_index_sequence{}); return table[index]; } - template + template Dynamic_Lock_Targets collect_dynamic_lock_targets(std::span keys, bool shared_access) const { using Schema = type_descriptor_schema_t; Dynamic_Lock_Targets targets; @@ -274,10 +293,10 @@ private: targets.unsynchronized_properties.erase(std::unique(targets.unsynchronized_properties.begin(), targets.unsynchronized_properties.end()), targets.unsynchronized_properties.end()); return targets; } - template + template Static_Lock_Targets collect_static_lock_targets() const { using Schema = type_descriptor_schema_t; - static_assert(((Shared_Access ? property_read_allowed_v : property_visible_v) && ...)); + static_assert(((Shared_Access ? detail::property_read_allowed_v : detail::property_visible_v) && ...)); Static_Lock_Targets targets; auto collect = [&]() { auto lock_slot = slot(Property_Index); @@ -292,12 +311,12 @@ private: sort_unique_prefix(targets.unsynchronized_properties, targets.unsynchronized_count); return targets; } - template + template auto collect_all_static_lock_targets() const { using Schema = type_descriptor_schema_t; Static_Lock_Targets targets; auto collect = [&]() { - constexpr bool allowed = Shared_Access ? property_read_allowed_v : property_write_allowed_v; + constexpr bool allowed = Shared_Access ? detail::property_read_allowed_v : detail::property_write_allowed_v; if constexpr (allowed) { auto lock_slot = slot(Index); if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) { @@ -330,7 +349,7 @@ private: const auto& descriptor = type_descriptor().template property(); descriptor.accessor.write(static_cast(*this), std::forward(value)); } - template + template class Single_Read_View { const Property_Object* owner_{}; std::size_t property_index_{}; @@ -358,10 +377,10 @@ private: return owner_->template read_unlocked(*this); } }; - template + template auto read_one() const { using Schema = type_descriptor_schema_t; - static_assert(property_read_allowed_v); + static_assert(detail::property_read_allowed_v); using Property = typename Schema::template property_type; using Value = typename Property::value_type; if constexpr (!uses_real_mutexes && !Property::accessor_type::synchronized_view_read) { @@ -378,10 +397,10 @@ private: std::shared_lock lock{mutex(lock_slot)}; return Value(read_unlocked(view)); } - template + template void write_one(Value&& value) { using Schema = type_descriptor_schema_t; - static_assert(property_write_allowed_v); + static_assert(detail::property_write_allowed_v); if constexpr (!uses_real_mutexes) { write_unlocked(std::forward(value)); return; @@ -394,9 +413,9 @@ private: std::unique_lock lock{mutex(lock_slot)}; write_unlocked(std::forward(value)); } -public: - template - class Read_Guard { +private: + template + class Basic_Read_Guard { const Property_Object* owner_{}; std::vector slots_; std::vector unsynchronized_properties_; @@ -409,7 +428,7 @@ public: return std::binary_search(slots_.begin(), slots_.end(), lock_slot); } friend class Property_Object; - Read_Guard(const Property_Object& owner, Dynamic_Lock_Targets targets) : owner_(&owner), slots_(std::move(targets.slots)), unsynchronized_properties_(std::move(targets.unsynchronized_properties)) { + Basic_Read_Guard(const Property_Object& owner, Dynamic_Lock_Targets targets) : owner_(&owner), slots_(std::move(targets.slots)), unsynchronized_properties_(std::move(targets.unsynchronized_properties)) { if constexpr (uses_real_mutexes) { locks_.reserve(slots_.size()); for (auto lock_slot : slots_) { @@ -422,7 +441,7 @@ public: decltype(auto) get_index() const { using Schema = type_descriptor_schema_t; static_assert(Index < Schema::property_count); - static_assert(property_read_allowed_v); + static_assert(detail::property_read_allowed_v); if (!holds(Index)) { throw std::logic_error("Property is outside the held synchronization set"); } @@ -443,8 +462,8 @@ public: return get_index(); } }; - template - class Write_Guard { + template + class Basic_Write_Guard { Property_Object* owner_{}; std::vector slots_; std::vector unsynchronized_properties_; @@ -457,7 +476,7 @@ public: return std::binary_search(slots_.begin(), slots_.end(), lock_slot); } friend class Property_Object; - Write_Guard(Property_Object& owner, Dynamic_Lock_Targets targets) : owner_(&owner), slots_(std::move(targets.slots)), unsynchronized_properties_(std::move(targets.unsynchronized_properties)) { + Basic_Write_Guard(Property_Object& owner, Dynamic_Lock_Targets targets) : owner_(&owner), slots_(std::move(targets.slots)), unsynchronized_properties_(std::move(targets.unsynchronized_properties)) { if constexpr (uses_real_mutexes) { locks_.reserve(slots_.size()); for (auto lock_slot : slots_) { @@ -470,7 +489,7 @@ public: decltype(auto) get_index() const { using Schema = type_descriptor_schema_t; static_assert(Index < Schema::property_count); - static_assert(property_read_allowed_v); + static_assert(detail::property_read_allowed_v); if (!holds(Index)) { throw std::logic_error("Property is outside the held synchronization set"); } @@ -494,7 +513,7 @@ public: void set_index(Value&& value) { using Schema = type_descriptor_schema_t; static_assert(Index < Schema::property_count); - static_assert(property_write_allowed_v); + static_assert(detail::property_write_allowed_v); if (!holds(Index)) { throw std::logic_error("Property is outside the held synchronization set"); } @@ -515,7 +534,7 @@ public: set_index(std::forward(value)); } }; - template + template class Static_Read_Guard { const Property_Object* owner_{}; Static_Lock_Targets targets_; @@ -540,7 +559,7 @@ public: decltype(auto) get_index() const { using Schema = type_descriptor_schema_t; static_assert(Index < Schema::property_count); - static_assert(property_read_allowed_v); + static_assert(detail::property_read_allowed_v); if (!holds(Index)) { throw std::logic_error("Property is outside the held synchronization set"); } @@ -561,7 +580,7 @@ public: return get_index(); } }; - template + template class Static_Write_Guard { Property_Object* owner_{}; Static_Lock_Targets targets_; @@ -586,7 +605,7 @@ public: decltype(auto) get_index() const { using Schema = type_descriptor_schema_t; static_assert(Index < Schema::property_count); - static_assert(property_read_allowed_v); + static_assert(detail::property_read_allowed_v); if (!holds(Index)) { throw std::logic_error("Property is outside the held synchronization set"); } @@ -610,7 +629,7 @@ public: void set_index(Value&& value) { using Schema = type_descriptor_schema_t; static_assert(Index < Schema::property_count); - static_assert(property_write_allowed_v); + static_assert(detail::property_write_allowed_v); if (!holds(Index)) { throw std::logic_error("Property is outside the held synchronization set"); } @@ -631,32 +650,39 @@ public: set_index(std::forward(value)); } }; +public: + using Intrinsic_Read_Guard = Basic_Read_Guard; + using Intrinsic_Write_Guard = Basic_Write_Guard; + template + using Read_Guard = Basic_Read_Guard>; + template + using Write_Guard = Basic_Write_Guard>; private: - template + template void for_each_readable_impl(Function&& function) const { using Schema = type_descriptor_schema_t; const auto& schema = type_descriptor(); schema.for_each_property([&](auto index, const auto& descriptor) { constexpr std::size_t property_index = decltype(index)::value; - if constexpr (property_read_allowed_v) { + if constexpr (detail::property_read_allowed_v) { auto value = read_one(); std::invoke(function, index, descriptor, value); } }); } - template + template void for_each_readable_locked_impl(Function&& function) const { using Schema = type_descriptor_schema_t; auto targets = collect_all_static_lock_targets(); Static_Read_Guard guard{*this, std::move(targets)}; type_descriptor().for_each_property([&](auto index, const auto& descriptor) { constexpr std::size_t property_index = decltype(index)::value; - if constexpr (property_read_allowed_v) { + if constexpr (detail::property_read_allowed_v) { std::invoke(function, index, descriptor, guard.template get_index()); } }); } - template + template void with_all_writable_locked_impl(Function&& function) { using Schema = type_descriptor_schema_t; auto targets = collect_all_static_lock_targets(); @@ -670,7 +696,7 @@ private: using Schema = type_descriptor_schema_t; return Schema::property_count; } - template + template Runtime_Access_Result runtime_read_mode(std::string_view key, void* context, Runtime_Read_Callback callback) const { using Schema = type_descriptor_schema_t; auto index = schema_property_index(key); @@ -707,19 +733,19 @@ private: }); return result; } - static Runtime_Access_Result runtime_read_impl(const Property_Object_Base& base, Managed_Access_Mode mode, std::string_view key, void* context, Runtime_Read_Callback callback) { + 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) { const auto& self = static_cast(base); switch (mode) { - case Managed_Access_Mode::internal: - return self.template runtime_read_mode(key, context, callback); - case Managed_Access_Mode::external: - return self.template runtime_read_mode(key, context, callback); - case Managed_Access_Mode::persistence: - return self.template runtime_read_mode(key, context, callback); + case detail::Access_Kind::intrinsic: + return self.template runtime_read_mode(key, context, callback); + case detail::Access_Kind::external: + return self.template runtime_read_mode(key, context, callback); + case detail::Access_Kind::persistence: + return self.template runtime_read_mode(key, context, callback); } return Runtime_Access_Result::not_readable; } - template + template Runtime_Access_Result runtime_write_mode(std::string_view key, const std::type_info& value_type, const void* value) { using Schema = type_descriptor_schema_t; auto index = schema_property_index(key); @@ -735,7 +761,7 @@ private: using Property = typename Schema::template property_type; using Accessor = typename Property::accessor_type; using Value = typename Property::value_type; - if constexpr (!property_write_allowed_v || !requires(const Accessor& accessor, Derived& object, const Value& candidate) { accessor.write(object, candidate); }) { + if constexpr (!detail::property_write_allowed_v || !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; @@ -746,15 +772,15 @@ private: }); return result; } - static Runtime_Access_Result runtime_write_impl(Property_Object_Base& base, Managed_Access_Mode mode, std::string_view key, const std::type_info& value_type, const void* value) { + 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) { auto& self = static_cast(base); switch (mode) { - case Managed_Access_Mode::internal: - return self.template runtime_write_mode(key, value_type, value); - case Managed_Access_Mode::external: - return self.template runtime_write_mode(key, value_type, value); - case Managed_Access_Mode::persistence: - return self.template runtime_write_mode(key, value_type, value); + case detail::Access_Kind::intrinsic: + return self.template runtime_write_mode(key, value_type, value); + case detail::Access_Kind::external: + return self.template runtime_write_mode(key, value_type, value); + case detail::Access_Kind::persistence: + return self.template runtime_write_mode(key, value_type, value); } return Runtime_Access_Result::not_writable; } @@ -808,28 +834,28 @@ public: using Schema = type_descriptor_schema_t; constexpr auto index = schema_member_property_index_v; static_assert(index < Schema::property_count); - return read_one(); + return read_one(); } template auto read_key() const { using Schema = type_descriptor_schema_t; constexpr auto index = schema_property_index_v; static_assert(index < Schema::property_count); - return read_one(); + return read_one(); } template void write(Value&& value) { using Schema = type_descriptor_schema_t; constexpr auto index = schema_member_property_index_v; static_assert(index < Schema::property_count); - write_one(std::forward(value)); + write_one(std::forward(value)); } template void write_key(Value&& value) { using Schema = type_descriptor_schema_t; constexpr auto index = schema_property_index_v; static_assert(index < Schema::property_count); - write_one(std::forward(value)); + write_one(std::forward(value)); } template std::size_t lock_slot() const noexcept { @@ -838,67 +864,69 @@ public: static_assert(index < Schema::property_count); return slot(index); } - template - Read_Guard lock_shared(std::span keys) const { - return Read_Guard{*this, collect_dynamic_lock_targets(keys, true)}; +private: + template + Basic_Read_Guard lock_shared_mode(std::span keys) const { + return Basic_Read_Guard{*this, collect_dynamic_lock_targets(keys, true)}; } - template - Read_Guard lock_shared(std::initializer_list keys) const { - return lock_shared(std::span{keys.begin(), keys.size()}); + template + Basic_Read_Guard lock_shared_mode(std::initializer_list keys) const { + return lock_shared_mode(std::span{keys.begin(), keys.size()}); } - Read_Guard lock_shared(std::span keys) const { - return lock_shared(keys); + template + Basic_Write_Guard lock_unique_mode(std::span keys) { + return Basic_Write_Guard{*this, collect_dynamic_lock_targets(keys, false)}; } - Read_Guard lock_shared(std::initializer_list keys) const { - return lock_shared(keys); + template + Basic_Write_Guard lock_unique_mode(std::initializer_list keys) { + return lock_unique_mode(std::span{keys.begin(), keys.size()}); } - template - Write_Guard lock_unique(std::span keys) { - return Write_Guard{*this, collect_dynamic_lock_targets(keys, false)}; - } - template - Write_Guard lock_unique(std::initializer_list keys) { - return lock_unique(std::span{keys.begin(), keys.size()}); - } - Write_Guard lock_unique(std::span keys) { - return lock_unique(keys); - } - Write_Guard lock_unique(std::initializer_list keys) { - return lock_unique(keys); - } - template + template auto lock_shared_mode() const { using Schema = type_descriptor_schema_t; static_assert((Schema_Property_Member && ...)); auto targets = collect_static_lock_targets...>(); return Static_Read_Guard{*this, std::move(targets)}; } - template - auto lock_shared() const { - return lock_shared_mode(); - } - template + template auto lock_unique_mode() { using Schema = type_descriptor_schema_t; static_assert((Schema_Property_Member && ...)); auto targets = collect_static_lock_targets...>(); return Static_Write_Guard{*this, std::move(targets)}; } +public: + Intrinsic_Read_Guard lock_shared(std::span keys) const { + return lock_shared_mode(keys); + } + Intrinsic_Read_Guard lock_shared(std::initializer_list keys) const { + return lock_shared_mode(keys); + } + Intrinsic_Write_Guard lock_unique(std::span keys) { + return lock_unique_mode(keys); + } + Intrinsic_Write_Guard lock_unique(std::initializer_list keys) { + return lock_unique_mode(keys); + } + template + auto lock_shared() const { + return lock_shared_mode(); + } template auto lock_unique() { - return lock_unique_mode(); + return lock_unique_mode(); } template void for_each_readable(Function&& function) const { - for_each_readable_impl(std::forward(function)); + for_each_readable_impl(std::forward(function)); } template void for_each_readable_locked(Function&& function) const { - for_each_readable_locked_impl(std::forward(function)); + for_each_readable_locked_impl(std::forward(function)); } template void with_all_writable_locked(Function&& function) { - with_all_writable_locked_impl(std::forward(function)); + with_all_writable_locked_impl(std::forward(function)); } template class Capability_View { @@ -910,42 +938,42 @@ public: using Schema = type_descriptor_schema_t; constexpr auto index = schema_member_property_index_v; static_assert(index < Schema::property_count); - return owner_->template read_one(); + return owner_->template read_one, index>(); } template auto read_key() const { using Schema = type_descriptor_schema_t; constexpr auto index = schema_property_index_v; static_assert(index < Schema::property_count); - return owner_->template read_one(); + return owner_->template read_one, index>(); } template void write(Value&& value) requires (Mode != Managed_Access_Mode::persistence) { using Schema = type_descriptor_schema_t; constexpr auto index = schema_member_property_index_v; static_assert(index < Schema::property_count); - owner_->template write_one(std::forward(value)); + owner_->template write_one, index>(std::forward(value)); } template void write_key(Value&& value) requires (Mode != Managed_Access_Mode::persistence) { using Schema = type_descriptor_schema_t; constexpr auto index = schema_property_index_v; static_assert(index < Schema::property_count); - owner_->template write_one(std::forward(value)); + owner_->template write_one, index>(std::forward(value)); } template void load(Value&& value) requires (Mode == Managed_Access_Mode::persistence) { using Schema = type_descriptor_schema_t; constexpr auto index = schema_member_property_index_v; static_assert(index < Schema::property_count); - owner_->template write_one(std::forward(value)); + owner_->template write_one, index>(std::forward(value)); } template void load_key(Value&& value) requires (Mode == Managed_Access_Mode::persistence) { using Schema = type_descriptor_schema_t; constexpr auto index = schema_property_index_v; static_assert(index < Schema::property_count); - owner_->template write_one(std::forward(value)); + owner_->template write_one, index>(std::forward(value)); } template auto store() const requires (Mode == Managed_Access_Mode::persistence) { @@ -957,33 +985,33 @@ public: } template auto lock_shared() const { - return owner_->template lock_shared_mode(); + return owner_->template lock_shared_mode, Members...>(); } template auto lock_unique() { - return owner_->template lock_unique_mode(); + return owner_->template lock_unique_mode, Members...>(); } Read_Guard lock_shared(std::span keys) const { - return owner_->template lock_shared(keys); + return owner_->template lock_shared_mode>(keys); } Write_Guard lock_unique(std::span keys) { - return owner_->template lock_unique(keys); + return owner_->template lock_unique_mode>(keys); } template void for_each_readable(Function&& function) const { - owner_->template for_each_readable_impl(std::forward(function)); + owner_->template for_each_readable_impl>(std::forward(function)); } template void for_each_readable_locked(Function&& function) const { - owner_->template for_each_readable_locked_impl(std::forward(function)); + owner_->template for_each_readable_locked_impl>(std::forward(function)); } template void with_all_writable_locked(Function&& function) requires (Mode != Managed_Access_Mode::persistence) { - owner_->template with_all_writable_locked_impl(std::forward(function)); + owner_->template with_all_writable_locked_impl>(std::forward(function)); } template void with_all_loadable_locked(Function&& function) requires (Mode == Managed_Access_Mode::persistence) { - owner_->template with_all_writable_locked_impl(std::forward(function)); + owner_->template with_all_writable_locked_impl>(std::forward(function)); } }; template @@ -996,14 +1024,14 @@ public: using Schema = type_descriptor_schema_t; constexpr auto index = schema_member_property_index_v; static_assert(index < Schema::property_count); - return owner_->template read_one(); + return owner_->template read_one, index>(); } template auto read_key() const { using Schema = type_descriptor_schema_t; constexpr auto index = schema_property_index_v; static_assert(index < Schema::property_count); - return owner_->template read_one(); + return owner_->template read_one, index>(); } template auto store() const requires (Mode == Managed_Access_Mode::persistence) { @@ -1015,18 +1043,18 @@ public: } template auto lock_shared() const { - return owner_->template lock_shared_mode(); + return owner_->template lock_shared_mode, Members...>(); } Read_Guard lock_shared(std::span keys) const { - return owner_->template lock_shared(keys); + return owner_->template lock_shared_mode>(keys); } template void for_each_readable(Function&& function) const { - owner_->template for_each_readable_impl(std::forward(function)); + owner_->template for_each_readable_impl>(std::forward(function)); } template void for_each_readable_locked(Function&& function) const { - owner_->template for_each_readable_locked_impl(std::forward(function)); + owner_->template for_each_readable_locked_impl>(std::forward(function)); } }; Capability_View external() { diff --git a/core/tests/property_core_test.cpp b/core/tests/property_core_test.cpp index bc099cb..86be2da 100644 --- a/core/tests/property_core_test.cpp +++ b/core/tests/property_core_test.cpp @@ -196,12 +196,23 @@ int main() { REQUIRE(erased.runtime_object_type() == typeid(Device)); 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); int runtime_write_value = 35; REQUIRE(erased.runtime_write(Managed_Access_Mode::external, "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); 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); @@ -213,7 +224,7 @@ int main() { auto guard = device.lock_unique<&Device::temperature>(); runtime_writer = std::jthread([&] { int value = 36; - REQUIRE(erased.runtime_write(Managed_Access_Mode::external, "temperature", typeid(int), &value) == Runtime_Access_Result::ok); + REQUIRE(erased.runtime_write("temperature", typeid(int), &value) == Runtime_Access_Result::ok); runtime_blocked_done.release(); }); REQUIRE(!runtime_blocked_done.try_acquire_for(std::chrono::milliseconds(20))); diff --git a/docs/CORE_GUIDE.md b/docs/CORE_GUIDE.md index 377c2ec..17addf2 100644 --- a/docs/CORE_GUIDE.md +++ b/docs/CORE_GUIDE.md @@ -261,7 +261,7 @@ struct Validation_Error { Validation is explicit and is not automatically executed by managed writes. -## 10. Managed internal read/write +## 10. Intrinsic managed read/write Typed member-pointer access: @@ -277,7 +277,7 @@ auto temperature = device.read_key<"temperature">(); device.write_key<"temperature">(30.0); ``` -These operations use `Managed_Access_Mode::internal`. +These operations use the property's intrinsic capability directly. There is no separate `internal` capability mode; readability and writability come from the accessor itself. `read()` returns a value object, not a reference to storage. The read is performed while the configured shared lock is held when synchronization is enabled. @@ -564,11 +564,21 @@ erased.runtime_object_type(); erased.runtime_property_count(); ``` -Runtime write: +Intrinsic runtime write does not require a mode: ```cpp double value = 35.0; auto result = erased.runtime_write( + "temperature", + typeid(double), + &value +); +``` + +A boundary projection is selected explicitly when an adapter must respect external or persistence capability metadata: + +```cpp +auto external_result = erased.runtime_write( Managed_Access_Mode::external, "temperature", typeid(double), @@ -576,7 +586,7 @@ auto result = erased.runtime_write( ); ``` -Runtime read uses a callback: +Runtime read uses the same overload model and returns the value through a callback: ```cpp static void read_double(void* context, std::size_t, std::string_view, const std::type_info& type, const void* value) { @@ -586,6 +596,11 @@ static void read_double(void* context, std::size_t, std::string_view, const std: } double output = 0.0; auto result = erased.runtime_read( + "temperature", + &output, + &read_double +); +auto external_result = erased.runtime_read( Managed_Access_Mode::external, "temperature", &output, @@ -603,7 +618,7 @@ Runtime_Access_Result::not_writable Runtime_Access_Result::type_mismatch ``` -The runtime path uses the same capability and synchronization rules as typed managed access. +The intrinsic runtime overload uses the accessor's intrinsic capability and the same synchronization topology as typed `read()`/`write()`. The overload taking `Managed_Access_Mode` applies the selected external or persistence projection before using the same synchronization rules. ## 22. Lock policy diff --git a/docs/CORE_GUIDE.zh-CN.md b/docs/CORE_GUIDE.zh-CN.md index b68eb83..62f7529 100644 --- a/docs/CORE_GUIDE.zh-CN.md +++ b/docs/CORE_GUIDE.zh-CN.md @@ -261,7 +261,7 @@ struct Validation_Error { Validation 是显式操作,不会在 managed write 中自动执行。 -## 10. Internal Managed Read/Write +## 10. Intrinsic Managed Read/Write 成员指针形式: @@ -277,7 +277,7 @@ auto temperature = device.read_key<"temperature">(); device.write_key<"temperature">(30.0); ``` -这些操作属于 `Managed_Access_Mode::internal`。 +这些操作直接使用 Property 的固有能力,不再存在单独的 `internal` capability mode;可读、可写能力由 accessor 本身决定。 `read()` 返回值对象而不是底层存储引用。启用同步时,读取发生在配置的 shared lock 持有期间。 @@ -564,11 +564,21 @@ erased.runtime_object_type(); erased.runtime_property_count(); ``` -Runtime write: +Intrinsic runtime write 不需要 mode: ```cpp double value = 35.0; auto result = erased.runtime_write( + "temperature", + typeid(double), + &value +); +``` + +当 adapter 需要遵守 external 或 persistence capability metadata 时,再显式选择边界投影: + +```cpp +auto external_result = erased.runtime_write( Managed_Access_Mode::external, "temperature", typeid(double), @@ -576,7 +586,7 @@ auto result = erased.runtime_write( ); ``` -Runtime read 通过 callback 返回值: +Runtime read 使用同样的 overload 模型,并通过 callback 返回值: ```cpp static void read_double(void* context, std::size_t, std::string_view, const std::type_info& type, const void* value) { @@ -586,6 +596,11 @@ static void read_double(void* context, std::size_t, std::string_view, const std: } double output = 0.0; auto result = erased.runtime_read( + "temperature", + &output, + &read_double +); +auto external_result = erased.runtime_read( Managed_Access_Mode::external, "temperature", &output, @@ -603,7 +618,7 @@ Runtime_Access_Result::not_writable Runtime_Access_Result::type_mismatch ``` -Runtime path 与 typed managed access 使用同一套 capability 和 synchronization 规则。 +Intrinsic runtime overload 使用 accessor 的固有能力,并与 typed `read()`/`write()` 使用同一套 synchronization topology。带 `Managed_Access_Mode` 的 overload 会先应用 external 或 persistence 投影,再使用同样的同步规则。 ## 22. Lock Policy diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 3fcd3a4..3989307 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -218,23 +218,34 @@ This duality is intentional rather than accidental. Do not pretend that inheriting `Property_Object` turns public C++ members into encapsulated properties. If a subsystem requires managed synchronization, its coding rules must require the managed path. -## 11. Capabilities are views, not copies of the object model +## 11. Intrinsic capability comes before boundary projections -The same property may have different visibility under different managed modes: +Every property first has capabilities defined by its accessor itself: ```text -internal -external -persistence +intrinsic +├── read +└── write ``` -Core derives those capabilities from Attributes and the accessor’s actual read/write abilities. +Normal managed application code uses those intrinsic capabilities directly through `read()`, `write()`, locks and traversal. `internal` is therefore not a separate capability mode. + +External and persistence behavior are boundary projections over that same property definition: + +```text +Property +├── intrinsic: read / write +├── external: read / write projection +└── persistence: load / store projection +``` + +Core combines projection Attributes with the accessor's actual abilities. A projection may narrow intrinsic capability, but it must never invent an ability the accessor does not provide. An external view should not become a second schema. A persistence view should not become a second schema. They are projections over one schema. ### Rule -**One property definition, multiple capability projections.** +**One property definition, intrinsic capabilities, explicit boundary projections.** ## 12. Validation is metadata plus an explicit operation @@ -321,7 +332,8 @@ This makes consistency explicit rather than relying on a getter that casually re `Property_Object_Base` intentionally provides a type-erased runtime interface using: - string key; -- `Managed_Access_Mode`; +- intrinsic access when no mode is supplied; +- `Managed_Access_Mode` only when selecting the `external` or `persistence` projection; - `std::type_info`; - explicit result codes. diff --git a/docs/DESIGN.zh-CN.md b/docs/DESIGN.zh-CN.md index b0b66b3..e0a81bd 100644 --- a/docs/DESIGN.zh-CN.md +++ b/docs/DESIGN.zh-CN.md @@ -218,23 +218,34 @@ Managed path 会经过 descriptor 和该实例的同步拓扑。 不要假装继承 `Property_Object` 以后 public member 就自动变成强封装属性。如果某个子系统要求受管理同步,那么该子系统自己的编码约束必须要求使用 managed path。 -## 11. Capability 是 Schema 的投影视图,不是第二套 Schema +## 11. 固有能力先于边界投影 -同一个属性在不同 managed mode 下可以拥有不同可见性: +每个 Property 首先拥有 accessor 自身决定的能力: ```text -internal -external -persistence +intrinsic +├── read +└── write ``` -Core 根据 Attribute 和 accessor 实际读写能力计算这些 capability。 +应用内部的普通 managed code 通过 `read()`、`write()`、lock 和 traversal 直接使用这套固有能力,因此 `internal` 不再是单独的 capability mode。 + +External 和 Persistence 是同一份 Property definition 之上的边界投影: + +```text +Property +├── intrinsic: read / write +├── external: read / write projection +└── persistence: load / store projection +``` + +Core 会把投影 Attribute 与 accessor 的实际能力结合起来。投影可以收窄固有能力,但绝不能凭空创造 accessor 本身不具备的能力。 External view 不应该发展成第二份 Schema;Persistence view 也不应该成为第二份 Schema。它们都只是同一 Schema 的能力投影。 ### 原则 -**一份 property definition,多种 capability projection。** +**一份 property definition,固有能力明确,边界投影显式。** ## 12. Validation = 元数据 + 显式操作 @@ -321,7 +332,8 @@ synchronization( `Property_Object_Base` 提供 type-erased runtime interface,核心输入包括: - string key; -- `Managed_Access_Mode`; +- 不传 mode 时使用 intrinsic access; +- 只有选择 `external` 或 `persistence` 投影时才使用 `Managed_Access_Mode`; - `std::type_info`; - 显式 result code。