Files
Structive/docs/CORE_GUIDE.zh-CN.md
T
2026-08-07 17:50:04 +08:00

12 KiB
Raw Blame History

Structive Property Core 完整指南

English

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/writegetter-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 只获取真实存在的同步 slotread-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. 推荐规则

  1. 存储保持普通 C++ member。
  2. 只注册真正属于结构模型的字段。
  3. C++ 业务代码优先 member-pointer typed API。
  4. 当 managed model 永远不应该写某个存储字段时,用 read_only
  5. 利用 stored read-only 的 zero-lock 优化。
  6. 并发 managed code 中不要通过 raw path 修改 read-only 字段。
  7. 跨字段 mutable consistency 使用 synchronization group。
  8. Validation 保持显式。
  9. Runtime access 只用于真正动态的 Adapter。
  10. GUI/RPC/Persistence/Authorization 自己拥有 exposure 与 access policy。