16 KiB
Structive Property Core 完整指南
1. Include 与 CMake Target
完整 Core 接口:
#include <structive/property/property.hpp>
CMake:
target_link_libraries(my_target PRIVATE structive::property_core)
Core 是 header-only,要求 C++20。
2. 定义 Managed Object
通常让业务对象继承 Property_Object<Derived>:
using namespace structive;
struct Device : Property_Object<Device> {
double temperature{25.0};
double pressure{101.3};
double min_speed{10.0};
double max_speed{100.0};
std::string name{"device-1"};
};
这些成员仍然都是普通 C++ 成员。
3. 定义 Type Descriptor
特化 Type_Descriptor<T>,通过 object<T>(...) 返回 Object_Schema:
template <>
struct structive::Type_Descriptor<Device> {
static auto get() {
return object<Device>(
defaults(
external_access<External_Access::read_write>,
persistence_access<Persistence_Access::load_store>
),
synchronization(
sync_all_independent,
sync_group<&Device::min_speed, &Device::max_speed>("speed_range")
),
field<&Device::temperature>(
key<"temperature">,
unit<"C">,
min_value<-50.0>,
max_value<200.0>
),
field<&Device::pressure>(key<"pressure">, unit<"kPa">),
field<&Device::min_speed>(key<"min_speed">),
field<&Device::max_speed>(key<"max_speed">),
field<&Device::name>(key<"name">)
);
}
};
field<Member>(...) 是 property<Member>(...) 的别名,产生 member-backed Property_Descriptor。
4. Schema 提供的静态保证
Schema 会检查:
- 每个 property 都必须拥有非空
key; - 同一 Schema 内 key 唯一;
- 同一个成员存储不能重复注册;
- single-valued Attribute category 不能在同一声明中重复;
- constraint 必须能作用于 property value type;
defaults(...)只能放 inheritable Attribute;- External/Persistence capability 不能声明 accessor 实际不支持的读写能力。
因此大量结构错误会直接成为编译期错误。
5. 获取 Schema
类型级:
const auto& schema = type_descriptor<Device>();
实例级:
Device device;
const auto& schema = device.schema();
可以通过编译期 index 或成员指针定位 property:
const auto& first = schema.property<0>();
const auto& temperature = schema.property<&Device::temperature>();
业务 typed code 优先成员指针;index 主要用于泛型遍历。
6. Property Descriptor
获取 key:
auto key_value = temperature.key();
静态能力:
using Property = std::remove_cvref_t<decltype(temperature)>;
static_assert(Property::readable);
static_assert(Property::writable);
按 category 查询 Attribute:
static_assert(Property::has_attribute<Unit_Category>);
const auto& attribute = temperature.attribute<Unit_Category>();
遍历全部声明 Attribute:
temperature.for_each_attribute([](const auto& attribute) {
// 根据 attribute 类型处理
});
单独遍历 constraint:
temperature.for_each_constraint([](const auto& constraint_value) {
// 检查 constraint
});
7. Object Defaults 与 Effective Attribute
defaults(...) 给 inheritable Attribute 提供对象级默认值:
defaults(
external_access<External_Access::read_write>,
persistence_access<Persistence_Access::load_store>,
sensitive<false>
)
Property 可以覆盖:
field<&Device::name>(
key<"name">,
external_access<External_Access::read>
)
Core 提供 effective_external_access_v、external_readable_v、external_writable_v、persistence_loadable_v、persistence_storable_v、effective_sensitive_v 等有效属性计算入口。
Extension 自己的 inheritable category 可以通过 declared_effective_attribute<Index, Category>(schema) 读取 property 声明或 object default 中的有效值。
8. Core Attribute
8.1 Key
key<"temperature">
每个 property 必须存在,不能为空,同一 Schema 内唯一。
8.2 External Access
external_access<External_Access::none>
external_access<External_Access::read>
external_access<External_Access::write>
external_access<External_Access::read_write>
支持在 defaults(...) 中继承。
8.3 Persistence Access
persistence_access<Persistence_Access::none>
persistence_access<Persistence_Access::load>
persistence_access<Persistence_Access::store>
persistence_access<Persistence_Access::load_store>
同样支持继承。
8.4 Unit
unit<"C">
描述元数据,不支持 object default 继承。
8.5 Sensitive
sensitive<>
sensitive<false>
支持继承。Core 会计算 effective value,但不会自动执行脱敏或隐藏输出。
9. Constraint 与 Validation
内置 constraint:
min_value<0>
max_value<100>
finite
自定义 constraint:
constraint<"even">([](int value) {
return value % 2 == 0;
})
按成员指针验证:
auto error = validate_property_value<&Device::temperature>(device.schema(), candidate);
按编译期 key 验证:
auto error = validate_property_key_value<"temperature">(device.schema(), candidate);
失败结果:
struct Validation_Error {
std::string_view property_key;
std::string_view code;
};
Validation 是显式操作,不会在 managed write 中自动执行。
10. Intrinsic Managed Read/Write
成员指针形式:
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);
这些操作直接使用 Property 的固有能力,不再存在单独的 internal capability mode;可读、可写能力由 accessor 本身决定。
read() 返回值对象而不是底层存储引用。启用同步时,读取发生在配置的 shared lock 持有期间。
11. External Capability View
可以通过 object defaults 默认开放 external access:
defaults(external_access<External_Access::read_write>)
使用:
device.external().write<&Device::temperature>(30.0);
auto value = device.external().read<&Device::temperature>();
Const object 返回 const capability view,因此没有 write API。
Typed API 对静态不可访问操作直接在编译期拒绝。
12. Persistence Capability View
Persistence 使用 load/store 语义:
device.persistence().load<&Device::temperature>(30.0);
auto value = device.persistence().store<&Device::temperature>();
编译期 key 版本:
device.persistence().load_key<"temperature">(30.0);
auto value = device.persistence().store_key<"temperature">();
能力由 Persistence_Access 元数据决定。
13. Synchronization Plan
13.1 Independent
默认 Synchronization_Plan 就是 independent:每个同步 property 拥有独立逻辑锁域。
显式写法:
synchronization(sync_all_independent)
13.2 Shared
synchronization(sync_all_shared)
除显式 override 外,全部 property 共用一个 lock slot。
13.3 Unsynchronized
synchronization(sync_all_unsynchronized)
property 使用 unsynchronized_slot,Structive 不提供真实 mutex 保护。
13.4 单 Property Override
成员指针形式:
sync_independent<&Device::temperature>()
sync_unsynchronized<&Device::immutable_id>()
运行时字符串形式:
sync_independent("temperature")
sync_unsynchronized("immutable_id")
静态已知 property 优先成员指针形式。
13.5 Group
成员指针形式:
sync_group<&Device::min_speed, &Device::max_speed>("speed_range")
字符串形式:
sync_group("speed_range", "min_speed", "max_speed")
同一 group 的 property 共用 lock slot。
Resolver 会拒绝未知 key、重复 group name、空 group,以及同一 property 被重复显式配置。
14. 每实例覆盖同步策略
Type Descriptor 提供默认同步计划,但单个实例可以通过 Property_Synchronization 覆盖:
struct Device : Property_Object<Device> {
Device() = default;
explicit Device(Property_Synchronization synchronization) : Property_Object(std::move(synchronization)) {}
};
Device shared_device{
property_synchronization(synchronization(sync_all_shared))
};
如果使用成员指针同步规则,可以显式提供对象类型完成 Schema materialization:
auto policy = property_synchronization<Device>(
synchronization(
sync_all_independent,
sync_group<&Device::temperature, &Device::pressure>("environment")
)
);
Schema 本身不变,只有这个实例使用覆盖后的 lock topology。默认实例按类型共享一份解析结果,因此不会为每个对象重复解析默认同步计划。
15. 查看解析后的同步拓扑
单字段:
auto slot = device.lock_slot<&Device::temperature>();
完整 view:
auto resolved = device.resolved_synchronization();
auto count = resolved.lock_count;
auto slot = resolved.slot(0);
auto uses_lock = resolved.uses_lock(0);
Resolved_Synchronization_View::unsynchronized_slot 表示无同步 property。
16. 静态多属性 Guard
Shared guard:
auto guard = device.lock_shared<&Device::temperature, &Device::pressure>();
auto temperature = guard.get<&Device::temperature>();
auto pressure = guard.get<&Device::pressure>();
Unique guard:
auto guard = device.lock_unique<&Device::min_speed, &Device::max_speed>();
auto old_min = guard.get<&Device::min_speed>();
guard.set<&Device::min_speed>(20.0);
guard.set<&Device::max_speed>(120.0);
Guard 只允许访问自己持有同步集合中的 property。
Capability view 也提供受其 access mode 限制的静态 guard:
auto guard = device.external().lock_shared<&Device::temperature>();
17. 动态 Key Guard
运行时才知道属性集合时:
std::array<std::string_view, 2> keys{"temperature", "pressure"};
auto guard = device.lock_shared(keys);
或者:
auto guard = device.lock_unique({"min_speed", "max_speed"});
动态 guard 会在运行时检查 key 和 capability。获取锁前会对 lock slot 排序、去重。
Guard 获取以后仍然可以通过编译期成员或 key 读写,但访问未包含在 held set 中的 property 会抛出 std::logic_error。
18. Traversal
Schema 遍历:
schema.for_each_property([](auto index, const auto& property) {
// compile-time index + descriptor
});
Managed value 遍历:
device.for_each_readable([](auto index, const auto& descriptor, const auto& value) {
// 每个 property 单独 managed read
});
Locked traversal 会先获取完整 readable 同步集合:
device.external().for_each_readable_locked([](auto index, const auto& descriptor, const auto& value) {
// 全部选中 property 在 guard 下访问
});
集中 writable 操作:
device.with_all_writable_locked([](auto& guard) {
// guard.get / guard.set
});
Persistence view 对应提供 with_all_loadable_locked(...)。
19. Computed Property
同步 computed property 接收 read view:
computed_property<Device, double>([](const auto& view) {
return view.template get<&Device::max_speed>() - view.template get<&Device::min_speed>();
}, key<"speed_span">, external_access<External_Access::read>)
Computed property 自身与通过 view 读取的每个 dependency 必须解析到同一个 lock slot。
例如:
synchronization(
sync_all_independent,
sync_group("speed", "min_speed", "max_speed", "speed_span")
)
Computed property 是只读属性。
20. Trusted Accessor Property
Core 还支持基于成员函数的 trusted access:
trusted_computed_property<&Device::get_temperature>(key<"temperature">)
以及 getter/setter:
trusted_accessor_property<&Device::get_temperature, &Device::set_temperature>(key<"temperature">)
它们直接调用对象成员函数,并标记为 trusted object access,不受 synchronized computed view 的 dependency slot 检查。只有当成员函数本身拥有明确同步/一致性契约时才应该使用。
21. Runtime Type-Erased Access
任何 Property_Object<T> 同时也是 Property_Object_Base:
Property_Object_Base& erased = device;
Runtime introspection:
erased.runtime_object_type();
erased.runtime_property_count();
Intrinsic runtime write 不需要 mode:
double value = 35.0;
auto result = erased.runtime_write(
"temperature",
typeid(double),
&value
);
当 adapter 需要遵守 external 或 persistence capability metadata 时,再显式选择边界投影:
auto external_result = erased.runtime_write(
Managed_Access_Mode::external,
"temperature",
typeid(double),
&value
);
Runtime read 使用同样的 overload 模型,并通过 callback 返回值:
static void read_double(void* context, std::size_t, std::string_view, const std::type_info& type, const void* value) {
if (type == typeid(double)) {
*static_cast<double*>(context) = *static_cast<const double*>(value);
}
}
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,
&read_double
);
结果枚举:
Runtime_Access_Result::ok
Runtime_Access_Result::unknown_property
Runtime_Access_Result::not_readable
Runtime_Access_Result::not_writable
Runtime_Access_Result::type_mismatch
Intrinsic runtime overload 使用 accessor 的固有能力,并与 typed read()/write() 使用同一套 synchronization topology。带 Managed_Access_Mode 的 overload 会先应用 external 或 persistence 投影,再使用同样的同步规则。
22. Lock Policy
默认:
struct Device : Property_Object<Device, Shared_Mutex_Policy> {
};
等价简写:
struct Device : Property_Object<Device> {
};
也提供 no-op lock policy:
struct Device : Property_Object<Device, No_Lock_Policy> {
};
No_Lock_Policy 使用 Null_Shared_Mutex,它取消真实互斥;只有外部所有权规则能够保证正确性时才应该使用。默认 managed-object 路径不保存 mutex 数组,也不会为同步状态产生每实例堆分配。显式使用自定义 Property_Synchronization 时仍可能分配一份紧凑覆盖 topology,因为 computed property 的同步域检查必须保留该实例选择的布局。
23. Raw Object Access
unsafe_object() 可以拿到底层 derived object:
Device& raw = device.unsafe_object();
普通成员访问当然也仍然存在:
device.temperature = 40.0;
这些入口故意绕过 Structive managed access。适合调用方已经持有正确同步、或者明确在 Structive 管理契约之外操作的情况。
24. 推荐使用规则
- Typed 业务访问优先 member pointer。
- Runtime integration boundary 使用 string key。
- Key 按协议标识对待。
- 稳定的对象级 capability 放
defaults(...),特殊字段单独 override。 - 需要一致观察的字段放同一个 synchronization group。
- Validation 在业务操作边界显式执行。
- 跨字段 invariant 使用静态 multi-property guard。
- 默认优先 synchronized computed property。
- Runtime access 用于 adapter,不要把普通 typed code 动态化。
- Raw access 只在调用方明确拥有被绕过的管理保证时使用。