12 KiB
Structive Property Core 完整指南
1. Include 与 CMake Target
#include <structive/property/property.hpp>
target_link_libraries(my_target PRIVATE structive::property_core)
Property Core 使用 C++20。
2. 定义 Managed Object
using namespace structive;
struct Device : Property_Object<Device> {
double temperature{25.0};
double pressure{101.3};
int serial_number{1001};
};
Property_Object<Device> 增加 managed operation,字段本身仍是普通成员。
3. 定义 Type Descriptor
template <>
struct structive::Type_Descriptor<Device> {
static auto get() {
return object<Device>(
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::serial_number>(
key<"serial_number">,
read_only
)
);
}
};
Descriptor 就是 Device 的结构定义。
4. Schema 静态保证
合法 Schema 保证:
- 每个 Property 都有非空 key;
- key 唯一;
- member selector 属于对应 object type;
- 同一 Property 上 single-valued Attribute category 不重复;
- capability metadata 不会要求底层 Accessor 不支持的操作;
- Constraint 与 Property value type 兼容。
static_assert(Property_Described_Object<Device>);
using Schema = type_descriptor_schema_t<Device>;
static_assert(Valid_Property_Schema<Schema>);
5. 获取 Schema
const auto& schema = type_descriptor<Device>();
const auto& same_schema = device.schema();
按 index 或 member pointer 获取 Property:
const auto& first = schema.property<0>();
const auto& temperature = schema.property<&Device::temperature>();
6. Property Descriptor
Descriptor 暴露编译期结构事实:
using Property = std::remove_cvref_t<decltype(schema.property<&Device::temperature>())>;
static_assert(Property::readable);
static_assert(Property::writable);
using Value = Property::value_type;
using Accessor = Property::accessor_type;
read_only 字段:
using Serial = std::remove_cvref_t<decltype(schema.property<&Device::serial_number>())>;
static_assert(Serial::readable);
static_assert(!Serial::writable);
运行时可直接取得 key:
auto key_value = schema.property<&Device::temperature>().key();
7. Intrinsic Capability
Core capability:
Property_Capability::none
Property_Capability::read
Property_Capability::write
Property_Capability::read_write
便捷 Attribute:
read_only
write_only
read_write
inaccessible
Capability 属于 Property 自身,不是 authorization rule。
不声明 capability 时,Structive 根据 Accessor 自动推导。
普通非 const member 默认 read/write;getter-only computed property 默认 read-only。
Capability 可以收窄 Accessor,但不能创造 Accessor 本来不存在的操作。
8. Core 不存在访问控制 API
Property Core 不提供 domain access mode,也不区分:
internal
external
persistence
Consumer 自己决定 policy。例如 GUI 可以只展示部分 Property,即使这些 Property 在结构上都 readable。
Core 只给出 intrinsic readable / writable。
9. Core Attribute
9.1 Key
每个 Property 必须有 key:
key<"temperature">
Key 是 runtime lookup 与 adapter 使用的结构协议身份。
9.2 Capability
read_only
write_only
read_write
inaccessible
Capability Attribute 是 single-valued、non-inheritable。
9.3 Unit
unit<"C">
unit<"kPa">
Core 保存单位信息,但不负责换算。
9.4 Sensitive
sensitive<>
sensitive<false>
sensitive 是可继承 metadata,不实现访问控制。Consumer 可以把它作为自己 policy 的一个输入。
10. 自定义 Attribute 与 Defaults
任何遵守 Attribute protocol 的类型都可以挂在 Property 上。
struct Group_Category {};
template <Fixed_String Value>
struct Group_Attribute {
using attribute_category = Group_Category;
static constexpr bool single_valued = true;
static constexpr bool inheritable = true;
static constexpr auto value = Value;
};
Inheritable Attribute 可以放进 defaults(...):
object<Device>(
defaults(sensitive<>),
field<&Device::temperature>(key<"temperature">)
)
Capability 故意不可继承,因为每个 Property 的固有操作集合必须独立成立。
11. Constraint 与 Validation
内置 Constraint:
min_value<0>
max_value<100>
finite
自定义 Constraint:
constraint<"even">([](int value) {
return value % 2 == 0;
})
显式验证:
auto result = validate_property_value<&Device::temperature>(device.schema(), candidate);
if (result) {
auto key_value = result->property_key;
auto code = result->code;
}
write() 不自动调用 Validation。
12. Typed Managed Read/Write
C++ 业务代码优先 member pointer:
auto temperature = device.read<&Device::temperature>();
device.write<&Device::temperature>(30.0);
也支持编译期 key:
auto temperature = device.read_key<"temperature">();
device.write_key<"temperature">(30.0);
Typed API 由 intrinsic capability 约束。对 read_only 调用 write 时,函数在 overload resolution 阶段就不可用。
template <class Object>
concept Can_Write_Serial = requires(Object& object) {
object.template write<&Device::serial_number>(1);
};
static_assert(!Can_Write_Serial<Device>);
13. Read-Only Fast Path
Stored read-only property 在编译期和同步解析阶段都会被裁掉。
field<&Device::serial_number>(key<"serial_number">, read_only)
最终:
serial_number -> unsynchronized_slot
read<&Device::serial_number>() 不查询 lock slot,也不构造 shared_lock。
即使默认是 sync_all_shared 也一样。
如果一个对象只有 stored read-only property,这些 Property 对 resolved_synchronization().lock_count 的贡献为 0。
14. Synchronization Plan
14.1 Independent
synchronization(sync_all_independent)
每个真正需要同步的 Property 各自一个 lock domain。
14.2 Shared
synchronization(sync_all_shared)
所有真正需要同步的 Property 共用一个 lock domain。
Read-only stored property 在 slot materialization 前就被排除。
14.3 Unsynchronized
synchronization(sync_all_unsynchronized)
即使 writable property 也不执行真实锁。
14.4 单 Property Override
synchronization(
sync_all_independent,
sync_unsynchronized<&Device::temperature>()
)
也支持动态 key 规则。
14.5 Group
synchronization(
sync_all_independent,
sync_group<&Device::min_speed, &Device::max_speed>("speed_range")
)
同组、且真正需要同步的 Property 解析到同一个 lock slot。
15. 每实例 Synchronization Override
对象可以显式覆盖类型默认策略:
Device device{
property_synchronization(
synchronization(sync_all_shared)
)
};
Typed member rule:
Device device{
property_synchronization<Device>(
synchronization(
sync_all_independent,
sync_group<&Device::temperature, &Device::pressure>("environment")
)
)
};
默认 topology 每类型共享。只有显式 override 的实例才保存紧凑 override layout。
16. 查看 Resolved Synchronization
auto view = device.resolved_synchronization();
auto count = view.lock_count;
成员 slot:
auto slot = device.lock_slot<&Device::temperature>();
无锁 Property 使用:
Resolved_Synchronization_View::unsynchronized_slot
Stored read-only property 自动得到这个值。
17. 静态多属性 Guard
Read Guard:
auto guard = device.lock_shared<&Device::temperature, &Device::pressure>();
auto temperature = guard.get<&Device::temperature>();
Write Guard:
auto guard = device.lock_unique<&Device::min_speed, &Device::max_speed>();
guard.set<&Device::min_speed>(20);
guard.set<&Device::max_speed>(120);
Static Guard 会对 slot 去重,并使用稳定 slot 顺序获取锁。
Typed unique guard 要求所有目标 Property intrinsically writable。
18. Dynamic-Key Guard
auto read_guard = device.lock_shared({"temperature", "pressure"});
auto write_guard = device.lock_unique({"temperature", "pressure"});
未知 key 抛 std::invalid_argument。
Dynamic unique guard 如果遇到 read-only property,也会因为 key 只能运行期确定而运行时拒绝。
19. Traversal
只遍历 Schema:
schema.for_each_property([&](auto index, const auto& descriptor) {
});
遍历 readable value:
device.for_each_readable([&](auto index, const auto& descriptor, const auto& value) {
});
一次性锁定后遍历:
device.for_each_readable_locked([&](auto index, const auto& descriptor, const auto& value) {
});
Locked traversal 只获取真实存在的同步 slot,read-only stored property 不会引入锁。
获取全部 writable lock domain:
device.with_all_writable_locked([&](auto& guard) {
});
它不会自动执行 validation 或 rollback。
20. Computed Property
Synchronized computed property 接收 read view:
computed_property<Device, int>([](const auto& view) {
return view.template get<&Device::max_speed>() - view.template get<&Device::min_speed>();
}, key<"speed_span">)
如果 writable dependency 需要同一快照,应共享 computed synchronization domain:
synchronization(
sync_all_independent,
sync_group("speed", "min_speed", "max_speed", "speed_span")
)
Read-only stored dependency 可以直接通过 computed view 读取,不需要加入 lock slot,因为它没有 managed writer。
21. Trusted Accessor Property
Trusted getter:
trusted_computed_property<&Device::value>(key<"value">)
Trusted getter/setter:
trusted_accessor_property<&Device::get_value, &Device::set_value>(key<"value">)
这类入口直接访问对象,而不是通过 synchronized dependency view。只有调用方明确掌握成员函数同步语义时才应该使用。
22. Runtime Type-Erased Access
动态 Adapter 使用:
Property_Object_Base& erased = device;
类型信息:
erased.runtime_object_type();
erased.runtime_property_count();
读取:
auto result = erased.runtime_read("temperature", context, callback);
写入:
auto result = erased.runtime_write("temperature", typeid(double), &value);
结果:
ok
unknown_property
not_readable
not_writable
type_mismatch
Runtime access 没有 external/persistence mode。Adapter 自己拥有 policy,然后决定是否调用 intrinsic runtime read/write。
Stored read-only property 的 runtime read 同样走 no-lock fast path。
23. Lock Policy
默认:
Property_Object<Device, Shared_Mutex_Policy>
NoLock:
Property_Object<Device, No_Lock_Policy>
No_Lock_Policy 不保存真实 mutex array,并从 managed hot path 去除真实 lock object。
24. Raw Object Access
Device& raw = device.unsafe_object();
或直接 public member:
device.temperature = 30.0;
都绕过 Structive managed contract。
这是刻意设计。Structive 是协作式结构基础设施,不是强制封装。
25. 推荐规则
- 存储保持普通 C++ member。
- 只注册真正属于结构模型的字段。
- C++ 业务代码优先 member-pointer typed API。
- 当 managed model 永远不应该写某个存储字段时,用
read_only。 - 利用 stored read-only 的 zero-lock 优化。
- 并发 managed code 中不要通过 raw path 修改 read-only 字段。
- 跨字段 mutable consistency 使用 synchronization group。
- Validation 保持显式。
- Runtime access 只用于真正动态的 Adapter。
- GUI/RPC/Persistence/Authorization 自己拥有 exposure 与 access policy。