# Structive 设计理念与原则 [English](DESIGN.md) ## 1. 设计目标 Structive 的目标是在保留 C++ 原生对象模型的前提下,为普通 struct 增加显式结构语义。 它不是第二套语言、反射运行时、安全边界、ORM 或对象框架。它是一层尽可能贴近普通 C++ 的结构元数据和 managed property 能力。 目标模型: ```text 普通 C++ 数据 + 显式 Schema + Property 固有能力 + Metadata + 可选 Managed Synchronization + 可选 Runtime Adapter ``` ## 2. 第一原则:增强,而不是替代 第一原则: > **增强 struct,而不是替代 struct。** 注册字段仍是真实 C++ 成员。未注册状态仍然是普通 C++。只要 C++ 类型本身允许,raw access 永远存在。 Structive 不要求每个字段变成 `Property`,也不应该迫使业务对象进入第二套对象模型。 ### 原则 如果一个能力可以通过 metadata 或很薄的 managed layer 实现,就不要改变原生 member model。 ## 3. 类型信息与实例行为必须分层 Structive 有两层架构。 ### 3.1 类型层 `Type_Descriptor` 与 `Object_Schema` 保存结构事实: - 注册 Property; - key; - intrinsic readable/writable; - Attribute; - Constraint; - dependency graph; - 默认 synchronization 描述。 这些属于类型。 ### 3.2 实例层 `Property_Object` 提供实例行为: - managed read/write; - 可写同步域所需的锁存储; - 多属性 Guard; - traversal; - type-erased runtime access; - 可选的每实例 synchronization override。 ### 原则 除非信息真的随实例变化,否则不要把类型级事实复制到每个对象里。 ## 4. 注册必须显式 Structive 不假设所有成员都是 Property。 ```cpp struct Device : Property_Object { int temperature; int internal_cache; }; ``` 只注册 `temperature` 时,`internal_cache` 完全不属于 Structive Schema。 ### 原则 注册决定参与;不在 Schema 中就不属于 Structive。 ## 5. Intrinsic Capability 属于 Property 自己 Structive **没有内建访问控制系统**。 Property 只描述自己固有支持什么: ```text none read write read_write ``` 通常由 Accessor 自动推导,也可以通过 metadata 显式收窄。 ```cpp field<&Device::serial_number>(key<"serial_number">, read_only) ``` 它表达: > 在 Structive managed model 中,这个属性可读、不可写。 它不表达哪个用户、服务、GUI 或进程有权限看到它。 ### 原则 Property capability 描述结构,不描述授权。 ## 6. Core 不拥有外部访问策略 Structive 不定义下面这些 managed access mode: ```text internal external persistence role context permission ``` GUI 自己决定哪些属性可编辑,RPC 自己决定暴露哪些字段,持久化系统自己决定保存哪些字段。 Core 只提供结构事实,Consumer 自己定义策略。 ### 原则 不要因为某个 Adapter 需要策略,就把这个策略塞进 Core。 ## 7. Raw Access 与 Managed Access 是两种契约 下面两句故意具有不同语义: ```cpp device.temperature = 30; device.write<&Device::temperature>(30); ``` Raw path 遵守普通 C++。Managed path 遵守 Structive Schema 与同步模型。 如果代码直接写一个 `read_only` public member,它就是主动绕过 Structive contract。 ### 原则 Structive 服务于遵守 managed contract 的代码,不假装能阻止故意绕过系统的 raw C++。 ## 8. Read-Only Metadata 必须产生真实优化 如果一个存储属性无法通过 Structive 被写入,那么不存在另一个 Structive managed writer 与它竞争。 因此 intrinsic read-only 的存储属性: - 不分配 lock slot; - 不贡献 mutex; - 不受宽泛默认同步模式影响; - managed read 不查询 lock slot; - managed read 不构造 `shared_lock`。 这是 Schema 信息直接产生的结构优化。 ### 原则 如果 Schema 已经证明某项运行时状态没有必要,就不要分配,也不要执行。 ## 9. 编译期信息必须消除运行时工作 Typed API 在编译期知道目标 Property: ```cpp device.read<&Device::serial_number>(); ``` 对于 stored read-only property,编译期路径直接绕过同步。 同样,对只读属性的 typed write 应该通过 `requires` 从接口中消失,而不是运行后再拒绝。 动态 key API 因为 key 只能运行期确定,所以才做 runtime check。 ### 原则 静态事实优先变成 `constexpr`、`requires`、`if constexpr`,不要变成 runtime branch。 ## 10. Synchronization 描述的是可变一致性域 Synchronization 的目的,是协调 mutable managed state。 默认模式: ```text independent shared unsynchronized ``` Group 让多个可变 Property 共享一个锁域。 Read-only stored property 即使被宽泛规则包含,也会从最终 resolved topology 中移除。 ### 原则 Synchronization 解决 mutable consistency,不解决可见性。 ## 11. Computed Read-Only 是特殊情况 Computed Property 自身可以只读,但它可能依赖可写字段。 Computed value 本身没有可写存储,但读取它时可能需要 mutable dependency 的一致快照。 Dependency 必须显式进入 Schema。每个 Property Accessor 都必须声明 `dependency_spec`,无依赖 Accessor 显式使用 `No_Property_Dependencies`。`Synchronized_Computed_Accessor` 的 read view 只能读取声明过的直接 dependency,computed property 的读取 slot 从 dependency graph 推导,而不是由调用方再次把 computed key 写进 synchronization group。Dependency graph 是 DAG invariant,Schema 形成时在编译期拒绝 cycle。 需要原子一致性的 writable dependency 应彼此处于同一个同步域。Read-only stored dependency 没有 managed writer,所以可以直接读取而无需锁。 ### 原则 不要给 read-only stored field 创建 mutex。Computed read 的同步只服务于需要一致性的 mutable dependency。 ## 12. 只有一份 Property Metadata Storage Property Descriptor 只有一份 metadata storage,其中的条目只能是 Attribute 或 Constraint。 `Object_Schema` 还可以通过 `type_metadata(...)` 保存类型级 Attribute。这是被描述类型 自身事实的唯一 metadata storage,明确区别于 Property metadata 和可继承的 Property defaults。Core 复用同一个 Attribute 协议与 category 唯一性规则,但不解释 Extension 拥有的类型语义。 Core 与 Extension 的描述性 metadata 共用 Attribute mechanism。Attribute 拥有 category,并声明 single-valued 与 inheritable 语义;Constraint 使用 validation protocol,但与 Attribute 保存在同一份 descriptor metadata storage 中。 UI、serialization、诊断等新领域不应该另起第二套 metadata storage。 ### 原则 新的描述性 metadata 扩展 Attribute protocol,新的校验规则扩展 Constraint protocol;两者都作为同一份 Property metadata 的条目存在。 ## 13. Category 必须有明确所有者 定义 Attribute category 的组件拥有它的语义。 Presentation 拥有 Presentation Category。Core 可以保存、遍历、泛型读取,但不解释它。 依赖方向保持: ```text Extension → Core Core -X→ Extension ``` ### 原则 Core 可以存储未知 Extension metadata,但不能学习 Extension 语义。 ## 14. Defaults 只做 Metadata 继承 `defaults(...)` 用于 inheritable Attribute,不用于偷偷执行行为。 Property-level metadata 可以覆盖同一 single-valued category 的 object default。 Intrinsic capability 不允许继承,因为每个 Property 自己的读写能力必须单独成立。 ### 原则 Defaults 只降低 metadata 重复,不得变成隐藏行为引擎。 ## 15. Validation 必须显式 Constraint 描述有效值,但不自动塞进每次 `write()`。 ```cpp auto error = validate_property_value<&Device::temperature>(schema, candidate); ``` 单字段 Validation、跨字段 invariant、transaction、rollback 是不同操作。 ### 原则 不要让 `write()` 变成 validation + event + transaction + rollback 的黑盒工作流。 ## 16. Runtime Access 是适配能力,不是主要编程模型 `Property_Object_Base` 提供动态 key 的 type-erased access。 Runtime access 只遵守 intrinsic capability,没有 runtime permission mode。 Adapter 自己决定是否暴露某个 Property、是否调用 runtime read/write。 ### 原则 普通 C++ 业务代码优先 member-pointer typed API;runtime access 留给动态边界。 ## 17. Compile-Time Error 与 Runtime Error 分工明确 静态 typed operation 应在编译期拒绝不可能的结构操作,例如: - 写 intrinsic read-only property; - 对 read-only property 请求 typed unique guard; - 读取 write-only property。 Runtime key API 才使用 `Runtime_Access_Result` 返回动态错误。Runtime write 明确是 copy-input 边界:intrinsic `writable` 继续表示结构事实,`runtime_copy_writable` 单独表示 Accessor 是否能参与 type-erased copy write。这样 move-only typed write 不会被错误描述成结构上不可写。 ### 原则 静态可知的 Property 错误不要拖到运行期。 ## 18. Synchronization Policy 必须保持可替换 `Shared_Mutex_Policy` 提供真实 shared/exclusive lock。`No_Lock_Policy` 不保存真实 mutex。 NoLock 不应该分配假的 mutex array,也不应该构造无意义的 lock object。 ### 原则 一个 Policy 移除某项能力时,应尽可能同时移除它的存储和热路径成本。 ## 19. 每实例 Synchronization Override 只让使用者付费 默认 resolved topology 每个类型共享。对象可以显式传入 `Property_Synchronization` 覆盖。 只有这种对象才保存 override layout。Copy/Move construction 保留该布局;Assignment 保留目标对象自身的 synchronization policy。 ### 原则 默认路径不为少数实例才使用的功能承担存储成本。 ## 20. Extension 自己决定领域行为 Persistence Extension 可以根据自己的 metadata 决定保存字段,GUI 可以决定 editability,RPC 可以实现 authorization。 这些系统可以读取 Structive 的 `readable`、`writable`、`sensitive` 或自定义 Attribute,但 Structive 不替它们做策略决定。 ### 原则 Core 描述结构,Consumer 在自己的边界决定行为。 ## 21. Core 的非目标 Property Core 不准备成为: - Authorization Engine; - ORM; - JSON Library; - RPC Framework; - GUI Binding Framework; - Transaction Manager; - Event Bus; - Scripting Engine。 这些系统可以消费 Structive,但应该独立存在。 ## 22. 演进审核规则 以后扩展 Structive 时逐条检查: 1. 它是在增强普通 C++,还是替代普通 C++? 2. 这个信息属于类型还是实例? 3. 这是 intrinsic structural capability,还是外部 policy? 4. 编译期信息能不能直接消掉运行时工作? 5. Read-only stored property 是否仍然完全不进入 lock topology? 6. Synchronization 是否只服务于 mutable consistency? 7. Validation 是否仍然显式? 8. Extension 是否拥有自己的 category 语义? 9. Runtime adaptation 是否与 typed business API 保持分离? 10. 新 Core 概念是否真的普适? ## 23. 架构总结 最终架构: ```text Native C++ struct │ ├── raw C++ access │ └── Structive Schema │ ├── intrinsic readable/writable ├── Attributes ├── Constraints ├── synchronization description │ └── 只有 mutable consistency domain 创建锁 └── managed object ├── typed read/write ├── guards ├── traversal └── intrinsic runtime access External systems ├── GUI policy ├── RPC policy ├── persistence policy └── other domain policy 外部 Policy 消费 Structive 的结构事实,但不是 Structive Core 的 access mode。 ``` 一句话概括: > **Structive 描述 Property 是什么、它自身能做什么;Structive 不决定谁能使用它。**