去除internel 访问级别

This commit is contained in:
2026-08-07 17:15:02 +08:00
parent 1ddd2799f2
commit 0c7fac70b2
8 changed files with 270 additions and 159 deletions
+15 -7
View File
@@ -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
+16 -6
View File
@@ -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 必须显式
@@ -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 <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,
@@ -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 <Managed_Access_Mode Mode, class Schema, std::size_t Index>
inline constexpr bool property_read_allowed_v = Mode == Managed_Access_Mode::internal ? Schema::template property_type<Index>::readable : Mode == Managed_Access_Mode::external ? external_readable_v<Schema, Index> : persistence_storable_v<Schema, Index>;
template <Managed_Access_Mode Mode, class Schema, std::size_t Index>
inline constexpr bool property_write_allowed_v = Mode == Managed_Access_Mode::internal ? Schema::template property_type<Index>::writable : Mode == Managed_Access_Mode::external ? external_writable_v<Schema, Index> : persistence_loadable_v<Schema, Index>;
template <Managed_Access_Mode Mode, class Schema, std::size_t Index>
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;
};
@@ -230,23 +249,23 @@ private:
}
count = write;
}
template <Managed_Access_Mode Mode>
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>{property_read_allowed_v<Mode, Schema, Index>...};
return std::array<bool, Schema::property_count>{detail::property_read_allowed_v<Mode, Schema, Index>...};
}(std::make_index_sequence<Schema::property_count>{});
return table[index];
}
template <Managed_Access_Mode Mode>
template <detail::Access_Kind Mode>
static bool runtime_property_visible(std::size_t index) {
using Schema = type_descriptor_schema_t<Derived>;
static const auto table = []<std::size_t... Index>(std::index_sequence<Index...>) {
return std::array<bool, Schema::property_count>{property_visible_v<Mode, Schema, Index>...};
return std::array<bool, Schema::property_count>{detail::property_visible_v<Mode, Schema, Index>...};
}(std::make_index_sequence<Schema::property_count>{});
return table[index];
}
template <Managed_Access_Mode Mode>
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;
@@ -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 <Managed_Access_Mode Mode, bool Shared_Access, std::size_t... Index>
template <detail::Access_Kind Mode, bool Shared_Access, std::size_t... Index>
Static_Lock_Targets<sizeof...(Index)> collect_static_lock_targets() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(((Shared_Access ? property_read_allowed_v<Mode, Schema, Index> : property_visible_v<Mode, Schema, Index>) && ...));
static_assert(((Shared_Access ? detail::property_read_allowed_v<Mode, Schema, Index> : detail::property_visible_v<Mode, Schema, Index>) && ...));
Static_Lock_Targets<sizeof...(Index)> targets;
auto collect = [&]<std::size_t Property_Index>() {
auto lock_slot = slot(Property_Index);
@@ -292,12 +311,12 @@ private:
sort_unique_prefix(targets.unsynchronized_properties, targets.unsynchronized_count);
return targets;
}
template <Managed_Access_Mode Mode, bool Shared_Access>
template <detail::Access_Kind Mode, bool Shared_Access>
auto collect_all_static_lock_targets() const {
using Schema = type_descriptor_schema_t<Derived>;
Static_Lock_Targets<Schema::property_count> targets;
auto collect = [&]<std::size_t Index>() {
constexpr bool allowed = Shared_Access ? property_read_allowed_v<Mode, Schema, Index> : property_write_allowed_v<Mode, Schema, Index>;
constexpr bool allowed = Shared_Access ? detail::property_read_allowed_v<Mode, Schema, Index> : detail::property_write_allowed_v<Mode, Schema, Index>;
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<Derived>().template property<Index>();
descriptor.accessor.write(static_cast<Derived&>(*this), std::forward<Value>(value));
}
template <Managed_Access_Mode Mode>
template <detail::Access_Kind Mode>
class Single_Read_View {
const Property_Object* owner_{};
std::size_t property_index_{};
@@ -358,10 +377,10 @@ private:
return owner_->template read_unlocked<index>(*this);
}
};
template <Managed_Access_Mode Mode, std::size_t Index>
template <detail::Access_Kind Mode, std::size_t Index>
auto read_one() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(property_read_allowed_v<Mode, Schema, Index>);
static_assert(detail::property_read_allowed_v<Mode, Schema, Index>);
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) {
@@ -378,10 +397,10 @@ private:
std::shared_lock lock{mutex(lock_slot)};
return Value(read_unlocked<Index>(view));
}
template <Managed_Access_Mode Mode, std::size_t Index, class Value>
template <detail::Access_Kind Mode, std::size_t Index, class Value>
void write_one(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(property_write_allowed_v<Mode, Schema, Index>);
static_assert(detail::property_write_allowed_v<Mode, Schema, Index>);
if constexpr (!uses_real_mutexes) {
write_unlocked<Index>(std::forward<Value>(value));
return;
@@ -394,9 +413,9 @@ private:
std::unique_lock lock{mutex(lock_slot)};
write_unlocked<Index>(std::forward<Value>(value));
}
public:
template <Managed_Access_Mode Mode>
class Read_Guard {
private:
template <detail::Access_Kind Mode>
class Basic_Read_Guard {
const Property_Object* owner_{};
std::vector<std::size_t> slots_;
std::vector<std::size_t> 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<Derived>;
static_assert(Index < Schema::property_count);
static_assert(property_read_allowed_v<Mode, Schema, Index>);
static_assert(detail::property_read_allowed_v<Mode, Schema, Index>);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
@@ -443,8 +462,8 @@ public:
return get_index<index>();
}
};
template <Managed_Access_Mode Mode>
class Write_Guard {
template <detail::Access_Kind Mode>
class Basic_Write_Guard {
Property_Object* owner_{};
std::vector<std::size_t> slots_;
std::vector<std::size_t> 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<Derived>;
static_assert(Index < Schema::property_count);
static_assert(property_read_allowed_v<Mode, Schema, Index>);
static_assert(detail::property_read_allowed_v<Mode, Schema, Index>);
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<Derived>;
static_assert(Index < Schema::property_count);
static_assert(property_write_allowed_v<Mode, Schema, Index>);
static_assert(detail::property_write_allowed_v<Mode, Schema, Index>);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
@@ -515,7 +534,7 @@ public:
set_index<index>(std::forward<Value>(value));
}
};
template <Managed_Access_Mode Mode, std::size_t Capacity>
template <detail::Access_Kind Mode, std::size_t Capacity>
class Static_Read_Guard {
const Property_Object* owner_{};
Static_Lock_Targets<Capacity> targets_;
@@ -540,7 +559,7 @@ public:
decltype(auto) get_index() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(property_read_allowed_v<Mode, Schema, Index>);
static_assert(detail::property_read_allowed_v<Mode, Schema, Index>);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
@@ -561,7 +580,7 @@ public:
return get_index<index>();
}
};
template <Managed_Access_Mode Mode, std::size_t Capacity>
template <detail::Access_Kind Mode, std::size_t Capacity>
class Static_Write_Guard {
Property_Object* owner_{};
Static_Lock_Targets<Capacity> targets_;
@@ -586,7 +605,7 @@ public:
decltype(auto) get_index() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(Index < Schema::property_count);
static_assert(property_read_allowed_v<Mode, Schema, Index>);
static_assert(detail::property_read_allowed_v<Mode, Schema, Index>);
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<Derived>;
static_assert(Index < Schema::property_count);
static_assert(property_write_allowed_v<Mode, Schema, Index>);
static_assert(detail::property_write_allowed_v<Mode, Schema, Index>);
if (!holds(Index)) {
throw std::logic_error("Property is outside the held synchronization set");
}
@@ -631,32 +650,39 @@ public:
set_index<index>(std::forward<Value>(value));
}
};
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>>;
private:
template <Managed_Access_Mode Mode, class Function>
template <detail::Access_Kind Mode, class Function>
void for_each_readable_impl(Function&& function) const {
using Schema = type_descriptor_schema_t<Derived>;
const auto& schema = type_descriptor<Derived>();
schema.for_each_property([&](auto index, const auto& descriptor) {
constexpr std::size_t property_index = decltype(index)::value;
if constexpr (property_read_allowed_v<Mode, Schema, property_index>) {
if constexpr (detail::property_read_allowed_v<Mode, Schema, property_index>) {
auto value = read_one<Mode, property_index>();
std::invoke(function, index, descriptor, value);
}
});
}
template <Managed_Access_Mode Mode, class Function>
template <detail::Access_Kind Mode, class Function>
void for_each_readable_locked_impl(Function&& function) const {
using Schema = type_descriptor_schema_t<Derived>;
auto targets = collect_all_static_lock_targets<Mode, true>();
Static_Read_Guard<Mode, Schema::property_count> guard{*this, std::move(targets)};
type_descriptor<Derived>().for_each_property([&](auto index, const auto& descriptor) {
constexpr std::size_t property_index = decltype(index)::value;
if constexpr (property_read_allowed_v<Mode, Schema, property_index>) {
if constexpr (detail::property_read_allowed_v<Mode, Schema, property_index>) {
std::invoke(function, index, descriptor, guard.template get_index<property_index>());
}
});
}
template <Managed_Access_Mode Mode, class Function>
template <detail::Access_Kind Mode, class Function>
void with_all_writable_locked_impl(Function&& function) {
using Schema = type_descriptor_schema_t<Derived>;
auto targets = collect_all_static_lock_targets<Mode, false>();
@@ -670,7 +696,7 @@ private:
using Schema = type_descriptor_schema_t<Derived>;
return Schema::property_count;
}
template <Managed_Access_Mode Mode>
template <detail::Access_Kind Mode>
Runtime_Access_Result runtime_read_mode(std::string_view key, void* context, Runtime_Read_Callback callback) const {
using Schema = type_descriptor_schema_t<Derived>;
auto index = schema_property_index<Schema>(key);
@@ -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<const Property_Object&>(base);
switch (mode) {
case Managed_Access_Mode::internal:
return self.template runtime_read_mode<Managed_Access_Mode::internal>(key, context, callback);
case Managed_Access_Mode::external:
return self.template runtime_read_mode<Managed_Access_Mode::external>(key, context, callback);
case Managed_Access_Mode::persistence:
return self.template runtime_read_mode<Managed_Access_Mode::persistence>(key, context, callback);
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;
}
template <Managed_Access_Mode Mode>
template <detail::Access_Kind Mode>
Runtime_Access_Result runtime_write_mode(std::string_view key, const std::type_info& value_type, const void* value) {
using Schema = type_descriptor_schema_t<Derived>;
auto index = schema_property_index<Schema>(key);
@@ -735,7 +761,7 @@ private:
using Property = typename Schema::template property_type<property_index>;
using Accessor = typename Property::accessor_type;
using Value = typename Property::value_type;
if constexpr (!property_write_allowed_v<Mode, Schema, property_index> || !requires(const Accessor& accessor, Derived& object, const Value& candidate) { accessor.write(object, candidate); }) {
if constexpr (!detail::property_write_allowed_v<Mode, Schema, property_index> || !requires(const Accessor& accessor, Derived& object, const Value& candidate) { accessor.write(object, candidate); }) {
result = Runtime_Access_Result::not_writable;
} else if (value_type != typeid(Value)) {
result = Runtime_Access_Result::type_mismatch;
@@ -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<Property_Object&>(base);
switch (mode) {
case Managed_Access_Mode::internal:
return self.template runtime_write_mode<Managed_Access_Mode::internal>(key, value_type, value);
case Managed_Access_Mode::external:
return self.template runtime_write_mode<Managed_Access_Mode::external>(key, value_type, value);
case Managed_Access_Mode::persistence:
return self.template runtime_write_mode<Managed_Access_Mode::persistence>(key, value_type, value);
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;
}
@@ -808,28 +834,28 @@ public:
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return read_one<Managed_Access_Mode::internal, index>();
return read_one<detail::Access_Kind::intrinsic, index>();
}
template <Fixed_String Key>
auto read_key() const {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
return read_one<Managed_Access_Mode::internal, index>();
return read_one<detail::Access_Kind::intrinsic, index>();
}
template <auto Member, class Value>
void write(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
write_one<Managed_Access_Mode::internal, index>(std::forward<Value>(value));
write_one<detail::Access_Kind::intrinsic, index>(std::forward<Value>(value));
}
template <Fixed_String Key, class Value>
void write_key(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_property_index_v<Schema, Key>;
static_assert(index < Schema::property_count);
write_one<Managed_Access_Mode::internal, index>(std::forward<Value>(value));
write_one<detail::Access_Kind::intrinsic, index>(std::forward<Value>(value));
}
template <auto Member>
std::size_t lock_slot() const noexcept {
@@ -838,67 +864,69 @@ public:
static_assert(index < Schema::property_count);
return slot(index);
}
template <Managed_Access_Mode Mode>
Read_Guard<Mode> lock_shared(std::span<const std::string_view> keys) const {
return Read_Guard<Mode>{*this, collect_dynamic_lock_targets<Mode>(keys, true)};
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)};
}
template <Managed_Access_Mode Mode>
Read_Guard<Mode> lock_shared(std::initializer_list<std::string_view> keys) const {
return lock_shared<Mode>(std::span<const std::string_view>{keys.begin(), keys.size()});
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()});
}
Read_Guard<Managed_Access_Mode::internal> lock_shared(std::span<const std::string_view> keys) const {
return lock_shared<Managed_Access_Mode::internal>(keys);
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)};
}
Read_Guard<Managed_Access_Mode::internal> lock_shared(std::initializer_list<std::string_view> keys) const {
return lock_shared<Managed_Access_Mode::internal>(keys);
template <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()});
}
template <Managed_Access_Mode Mode>
Write_Guard<Mode> lock_unique(std::span<const std::string_view> keys) {
return Write_Guard<Mode>{*this, collect_dynamic_lock_targets<Mode>(keys, false)};
}
template <Managed_Access_Mode Mode>
Write_Guard<Mode> lock_unique(std::initializer_list<std::string_view> keys) {
return lock_unique<Mode>(std::span<const std::string_view>{keys.begin(), keys.size()});
}
Write_Guard<Managed_Access_Mode::internal> lock_unique(std::span<const std::string_view> keys) {
return lock_unique<Managed_Access_Mode::internal>(keys);
}
Write_Guard<Managed_Access_Mode::internal> lock_unique(std::initializer_list<std::string_view> keys) {
return lock_unique<Managed_Access_Mode::internal>(keys);
}
template <Managed_Access_Mode Mode, auto... Members>
template <detail::Access_Kind Mode, auto... Members>
auto lock_shared_mode() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert((Schema_Property_Member<Schema, Members> && ...));
auto targets = collect_static_lock_targets<Mode, true, schema_member_property_index_v<Schema, Members>...>();
return Static_Read_Guard<Mode, sizeof...(Members)>{*this, std::move(targets)};
}
template <auto... Members>
auto lock_shared() const {
return lock_shared_mode<Managed_Access_Mode::internal, Members...>();
}
template <Managed_Access_Mode Mode, auto... Members>
template <detail::Access_Kind Mode, auto... Members>
auto lock_unique_mode() {
using Schema = type_descriptor_schema_t<Derived>;
static_assert((Schema_Property_Member<Schema, Members> && ...));
auto targets = collect_static_lock_targets<Mode, false, schema_member_property_index_v<Schema, Members>...>();
return Static_Write_Guard<Mode, sizeof...(Members)>{*this, std::move(targets)};
}
public:
Intrinsic_Read_Guard lock_shared(std::span<const std::string_view> keys) const {
return lock_shared_mode<detail::Access_Kind::intrinsic>(keys);
}
Intrinsic_Read_Guard lock_shared(std::initializer_list<std::string_view> keys) const {
return lock_shared_mode<detail::Access_Kind::intrinsic>(keys);
}
Intrinsic_Write_Guard lock_unique(std::span<const std::string_view> keys) {
return lock_unique_mode<detail::Access_Kind::intrinsic>(keys);
}
Intrinsic_Write_Guard lock_unique(std::initializer_list<std::string_view> keys) {
return lock_unique_mode<detail::Access_Kind::intrinsic>(keys);
}
template <auto... Members>
auto lock_shared() const {
return lock_shared_mode<detail::Access_Kind::intrinsic, Members...>();
}
template <auto... Members>
auto lock_unique() {
return lock_unique_mode<Managed_Access_Mode::internal, Members...>();
return lock_unique_mode<detail::Access_Kind::intrinsic, Members...>();
}
template <class Function>
void for_each_readable(Function&& function) const {
for_each_readable_impl<Managed_Access_Mode::internal>(std::forward<Function>(function));
for_each_readable_impl<detail::Access_Kind::intrinsic>(std::forward<Function>(function));
}
template <class Function>
void for_each_readable_locked(Function&& function) const {
for_each_readable_locked_impl<Managed_Access_Mode::internal>(std::forward<Function>(function));
for_each_readable_locked_impl<detail::Access_Kind::intrinsic>(std::forward<Function>(function));
}
template <class Function>
void with_all_writable_locked(Function&& function) {
with_all_writable_locked_impl<Managed_Access_Mode::internal>(std::forward<Function>(function));
with_all_writable_locked_impl<detail::Access_Kind::intrinsic>(std::forward<Function>(function));
}
template <Managed_Access_Mode Mode>
class Capability_View {
@@ -910,42 +938,42 @@ public:
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return owner_->template read_one<Mode, index>();
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<Mode, index>();
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<Mode, index>(std::forward<Value>(value));
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<Mode, index>(std::forward<Value>(value));
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<Mode, index>(std::forward<Value>(value));
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<Mode, index>(std::forward<Value>(value));
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) {
@@ -957,33 +985,33 @@ public:
}
template <auto... Members>
auto lock_shared() const {
return owner_->template lock_shared_mode<Mode, Members...>();
return owner_->template lock_shared_mode<detail::access_kind_v<Mode>, Members...>();
}
template <auto... Members>
auto lock_unique() {
return owner_->template lock_unique_mode<Mode, Members...>();
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>(keys);
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>(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<Mode>(std::forward<Function>(function));
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<Mode>(std::forward<Function>(function));
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<Mode>(std::forward<Function>(function));
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<Mode>(std::forward<Function>(function));
owner_->template with_all_writable_locked_impl<detail::access_kind_v<Mode>>(std::forward<Function>(function));
}
};
template <Managed_Access_Mode Mode>
@@ -996,14 +1024,14 @@ public:
using Schema = type_descriptor_schema_t<Derived>;
constexpr auto index = schema_member_property_index_v<Schema, Member>;
static_assert(index < Schema::property_count);
return owner_->template read_one<Mode, index>();
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<Mode, index>();
return owner_->template read_one<detail::access_kind_v<Mode>, index>();
}
template <auto Member>
auto store() const requires (Mode == Managed_Access_Mode::persistence) {
@@ -1015,18 +1043,18 @@ public:
}
template <auto... Members>
auto lock_shared() const {
return owner_->template lock_shared_mode<Mode, Members...>();
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>(keys);
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<Mode>(std::forward<Function>(function));
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<Mode>(std::forward<Function>(function));
owner_->template for_each_readable_locked_impl<detail::access_kind_v<Mode>>(std::forward<Function>(function));
}
};
Capability_View<Managed_Access_Mode::external> external() {
+12 -1
View File
@@ -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)));
+20 -5
View File
@@ -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
+20 -5
View File
@@ -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
+20 -8
View File
@@ -218,23 +218,34 @@ This duality is intentional rather than accidental.
Do not pretend that inheriting `Property_Object<T>` 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 accessors 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.
+20 -8
View File
@@ -218,23 +218,34 @@ Managed path 会经过 descriptor 和该实例的同步拓扑。
不要假装继承 `Property_Object<T>` 以后 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 不应该发展成第二份 SchemaPersistence 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。