328 lines
9.9 KiB
Markdown
328 lines
9.9 KiB
Markdown
# Structive
|
||
|
||
**Structive 的目标是在不替换 C++ 原生数据模型的前提下,为普通 `struct` 增加显式的结构描述、属性元数据和受管理访问能力。**
|
||
|
||
[English](README.md) · [设计理念](docs/DESIGN.zh-CN.md) · [核心指南](docs/CORE_GUIDE.zh-CN.md) · [扩展体系](docs/EXTENSIONS.zh-CN.md)
|
||
|
||
## Structive 是什么
|
||
|
||
Structive 是一个 C++20 属性与结构描述系统。它刻意把“类型结构描述”和“对象实例管理”分成两层:
|
||
|
||
```text
|
||
C++ 原生对象模型
|
||
普通成员、成员函数、原生布局、直接访问
|
||
│
|
||
├── Type_Descriptor<T> → Object_Schema
|
||
│ 类型级结构、key、Attribute、约束、访问能力
|
||
│
|
||
└── Property_Object<T>
|
||
实例级受管理访问、同步、遍历、运行时访问
|
||
```
|
||
|
||
业务类型仍然可以保持非常普通:
|
||
|
||
```cpp
|
||
#include <structive/property/property.hpp>
|
||
using namespace structive;
|
||
struct Device : Property_Object<Device> {
|
||
double temperature{25.0};
|
||
double pressure{101.3};
|
||
};
|
||
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>
|
||
),
|
||
field<&Device::temperature>(
|
||
key<"temperature">,
|
||
unit<"C">,
|
||
min_value<-50.0>,
|
||
max_value<200.0>
|
||
),
|
||
field<&Device::pressure>(
|
||
key<"pressure">,
|
||
unit<"kPa">
|
||
)
|
||
);
|
||
}
|
||
};
|
||
```
|
||
|
||
成员仍然是真实的 C++ 成员。Structive 只是额外建立一层可以被泛型系统理解的结构语义。
|
||
|
||
## 核心思想
|
||
|
||
Structive 最核心的原则只有一句:
|
||
|
||
> **增强 struct,而不是替代 struct。**
|
||
|
||
因此它坚持:
|
||
|
||
- 注册后的字段仍然是普通 C++ 成员。
|
||
- 未注册成员完全不进入属性系统。
|
||
- 业务代码优先使用成员指针作为编译期属性身份。
|
||
- 字符串 `key` 主要服务运行时和适配器边界。
|
||
- 不要求把每个成员改造成 `Property<T>` 之类的包装类型。
|
||
- Validation、Synchronization、Persistence、Presentation 等语义相互分离。
|
||
|
||
这样,同一个业务结构体可以被 UI、持久化、序列化、RPC、脚本或工具系统理解,但业务类型本身不需要依赖这些系统。
|
||
|
||
## 项目分层
|
||
|
||
```text
|
||
core/
|
||
└── structive::property_core
|
||
├── INTERFACE target
|
||
├── Type_Descriptor<T> / Object_Schema
|
||
├── member / computed accessor
|
||
├── 统一 Attribute 协议
|
||
├── constraint 与显式 validation
|
||
├── access capability 元数据
|
||
├── synchronization plan 与 guard
|
||
├── typed / runtime access
|
||
└── traversal
|
||
|
||
extensions/
|
||
└── structive::property_extensions
|
||
├── STATIC target
|
||
├── 只依赖 property_core
|
||
├── 定义扩展自己拥有的 Attribute category
|
||
└── 当前实现 presentation 元数据解释
|
||
```
|
||
|
||
依赖方向必须保持单向:**Extension 依赖 Core;Core 永远不 include、不 link Extension。**
|
||
|
||
## 为什么不使用 `Property<T>` 成员
|
||
|
||
Structive 不要求这样定义对象:
|
||
|
||
```cpp
|
||
struct Device {
|
||
Property<double> temperature;
|
||
};
|
||
```
|
||
|
||
而是保留原生存储:
|
||
|
||
```cpp
|
||
struct Device : Property_Object<Device> {
|
||
double temperature;
|
||
};
|
||
```
|
||
|
||
然后通过 `Type_Descriptor<Device>` 单独声明结构语义。
|
||
|
||
这样可以保留真实成员指针、原生成员语义和必要时的直接访问能力。Structive 是增强层,不建立第二套替代 C++ 的对象模型。
|
||
|
||
## Schema 和 Managed Object 是两种概念
|
||
|
||
`Type_Descriptor<T>` 描述的是**类型**;`Property_Object<T>` 管理的是**实例**。
|
||
|
||
Schema 保存注册属性列表、对象默认 Attribute 和默认同步计划。`Property_Object<T>` 在每个实例上解析同步计划,并持有自己的锁拓扑和 mutex 存储。
|
||
|
||
这个边界必须长期保持:结构描述属于类型级;同步状态属于实例级。
|
||
|
||
## 只有一套 Attribute 协议
|
||
|
||
Core Attribute 和 Extension Attribute 使用同一机制,不存在额外的 `hint` 通道。
|
||
|
||
例如一个单值 Attribute category 可以这样定义:
|
||
|
||
```cpp
|
||
struct Label_Category {};
|
||
template <Fixed_String Value>
|
||
struct Label_Attribute {
|
||
using attribute_category = Label_Category;
|
||
static constexpr bool single_valued = true;
|
||
static constexpr bool inheritable = false;
|
||
static constexpr auto value = Value;
|
||
};
|
||
```
|
||
|
||
然后直接挂到属性上:
|
||
|
||
```cpp
|
||
field<&Device::temperature>(
|
||
key<"temperature">,
|
||
presentation::label<"Temperature">
|
||
)
|
||
```
|
||
|
||
Core 负责统一保存和遍历,但只解释自己拥有的 category。Presentation、JSON、RPC 等扩展应该自己解释自己的 category。
|
||
|
||
这是 Structive 最主要的扩展边界。
|
||
|
||
## Managed Access 不是强制封装
|
||
|
||
下面两种写法都合法,但语义不同:
|
||
|
||
```cpp
|
||
device.temperature = 30.0;
|
||
device.write<&Device::temperature>(30.0);
|
||
```
|
||
|
||
第一种是 **raw C++ path**,不会经过 Structive 的同步和 capability 管理。
|
||
|
||
第二种是 **managed path**,会定位注册属性并按该对象实例的同步计划执行。
|
||
|
||
Structive 有意保留两条路径。业务代码应该根据对象所有权和并发规则选择,而不是假装所有成员访问都能被框架强制拦截。
|
||
|
||
## 访问能力视图
|
||
|
||
Core 当前定义三种受管理访问模式:
|
||
|
||
- `internal`:应用内部正常受管理访问。
|
||
- `external`:由 `External_Access` 元数据控制。
|
||
- `persistence`:由 `Persistence_Access` 元数据控制。
|
||
|
||
```cpp
|
||
device.write<&Device::temperature>(30.0);
|
||
device.external().write<&Device::temperature>(31.0);
|
||
device.persistence().load<&Device::temperature>(32.0);
|
||
auto stored = device.persistence().store<&Device::temperature>();
|
||
```
|
||
|
||
Schema 会在编译期约束 capability 与 accessor 能力一致,例如只读 computed property 不能声明为可写。
|
||
|
||
## Validation 必须显式
|
||
|
||
Constraint 是结构元数据;`write()` **不会自动执行 constraint**。
|
||
|
||
```cpp
|
||
auto error = validate_property_value<&Device::temperature>(device.schema(), 500.0);
|
||
if (error) {
|
||
// error->property_key
|
||
// error->code
|
||
}
|
||
```
|
||
|
||
这是设计选择,不是缺功能。单字段校验、跨字段不变量、事务和回滚是不同概念,不应该偷偷塞进一个通用 setter。
|
||
|
||
## Synchronization 明确且可组合
|
||
|
||
默认同步模式包括:
|
||
|
||
```cpp
|
||
sync_all_independent
|
||
sync_all_shared
|
||
sync_all_unsynchronized
|
||
```
|
||
|
||
也可以覆盖单个字段或建立同步组:
|
||
|
||
```cpp
|
||
synchronization(
|
||
sync_all_independent,
|
||
sync_group<&Device::min_speed, &Device::max_speed>("speed_range")
|
||
)
|
||
```
|
||
|
||
同一 group 的属性会解析到同一个 lock slot。多属性 Guard 会对 lock slot 去重,并按稳定顺序获取锁。
|
||
|
||
```cpp
|
||
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);
|
||
```
|
||
|
||
Synchronization 只负责同步,不自动等价于 validation、transaction、rollback 或事件系统。
|
||
|
||
## Computed Property
|
||
|
||
Structive 支持基于同步视图读取依赖项的 computed property:
|
||
|
||
```cpp
|
||
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 的依赖字段必须和 computed property 本身落在同一个同步 slot,否则运行时会拒绝跨 slot 读取。同步计划因此显式表达了 computed value 的一致性边界。
|
||
|
||
Core 还提供 `trusted_computed_property` 和 `trusted_accessor_property` 作为高级入口。这类 accessor 直接通过受信任对象访问执行成员函数,不经过 synchronized view 的依赖检查,因此只应该在调用方明确掌握同步语义时使用。
|
||
|
||
## 运行时访问
|
||
|
||
`Property_Object_Base` 提供 type-erased runtime access:
|
||
|
||
```cpp
|
||
Property_Object_Base& erased = device;
|
||
auto type = erased.runtime_object_type();
|
||
auto count = erased.runtime_property_count();
|
||
```
|
||
|
||
Runtime read/write 使用 property key、access mode 和 `std::type_info`,返回:
|
||
|
||
```text
|
||
ok
|
||
unknown_property
|
||
not_readable
|
||
not_writable
|
||
type_mismatch
|
||
```
|
||
|
||
它适合 JSON、HTTP/RPC、脚本桥接、动态工具等边界。普通编译期业务代码仍然应优先使用成员指针。
|
||
|
||
## Core 当前元数据
|
||
|
||
当前 Core 提供:
|
||
|
||
- `key<"...">`
|
||
- `external_access<...>`
|
||
- `persistence_access<...>`
|
||
- `unit<"...">`
|
||
- `sensitive<>`
|
||
- `min_value<...>`
|
||
- `max_value<...>`
|
||
- `finite`
|
||
- `constraint<"code">(...)`
|
||
|
||
其中 `external_access`、`persistence_access`、`sensitive` 支持继承,可以放入 `defaults(...)`。同 category 的 property 级 Attribute 会覆盖 object default。
|
||
|
||
## 当前 Presentation Extension
|
||
|
||
当前扩展层定义:
|
||
|
||
- `presentation::label<"...">`
|
||
- `presentation::description<"...">`
|
||
- `presentation::group<"...">`
|
||
- `presentation::order<N>`
|
||
|
||
`presentation::describe()` 解释这些 Attribute;未提供 label 时由 Presentation Extension 使用 property key 作为 fallback。这个 fallback 不属于 Core。
|
||
|
||
## 构建
|
||
|
||
当前 CMake 目标适用于作为子目录接入:
|
||
|
||
```cmake
|
||
add_subdirectory(path/to/Structive)
|
||
target_link_libraries(my_target PRIVATE structive::property_core)
|
||
```
|
||
|
||
使用链接型扩展:
|
||
|
||
```cmake
|
||
target_link_libraries(my_target PRIVATE structive::property_extensions)
|
||
```
|
||
|
||
仓库构建测试:
|
||
|
||
```bash
|
||
cmake -S . -B build -DBUILD_TESTING=ON
|
||
cmake --build build
|
||
ctest --test-dir build --output-on-failure
|
||
```
|
||
|
||
## 详细文档
|
||
|
||
- [设计理念与原则](docs/DESIGN.zh-CN.md)
|
||
- [Core 完整指南](docs/CORE_GUIDE.zh-CN.md)
|
||
- [Extension 架构指南](docs/EXTENSIONS.zh-CN.md)
|
||
- [English README](README.md)
|
||
- [Design Philosophy](docs/DESIGN.md)
|
||
- [Core Guide](docs/CORE_GUIDE.md)
|
||
- [Extension Architecture](docs/EXTENSIONS.md)
|