403 lines
12 KiB
Markdown
403 lines
12 KiB
Markdown
# 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<T>`,也不应该迫使业务对象进入第二套对象模型。
|
||
|
||
### 原则
|
||
|
||
如果一个能力可以通过 metadata 或很薄的 managed layer 实现,就不要改变原生 member model。
|
||
|
||
## 3. 类型信息与实例行为必须分层
|
||
|
||
Structive 有两层架构。
|
||
|
||
### 3.1 类型层
|
||
|
||
`Type_Descriptor<T>` 与 `Object_Schema` 保存结构事实:
|
||
|
||
- 注册 Property;
|
||
- key;
|
||
- intrinsic readable/writable;
|
||
- Attribute;
|
||
- Constraint;
|
||
- dependency graph;
|
||
- 默认 synchronization 描述。
|
||
|
||
这些属于类型。
|
||
|
||
### 3.2 实例层
|
||
|
||
`Property_Object<T>` 提供实例行为:
|
||
|
||
- managed read/write;
|
||
- 可写同步域所需的锁存储;
|
||
- 多属性 Guard;
|
||
- traversal;
|
||
- type-erased runtime access;
|
||
- 可选的每实例 synchronization override。
|
||
|
||
### 原则
|
||
|
||
除非信息真的随实例变化,否则不要把类型级事实复制到每个对象里。
|
||
|
||
## 4. 注册必须显式
|
||
|
||
Structive 不假设所有成员都是 Property。
|
||
|
||
```cpp
|
||
struct Device : Property_Object<Device> {
|
||
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 不决定谁能使用它。**
|