From b2d7f1821399db6dd195917863d9201edcf62910 Mon Sep 17 00:00:00 2001 From: skycurtain Date: Wed, 4 Feb 2026 01:07:37 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .trae/documents/cross-language-sdk-guide.md | 170 ++++++++++++++ .../documents/sdk-initialization-patterns.md | 213 ++++++++++++++++++ .trae/rules/project-introduction.md | 15 ++ .trae/rules/project_rules.md | 15 -- .trae/rules/tracer-type.md | 19 ++ 5 files changed, 417 insertions(+), 15 deletions(-) create mode 100644 .trae/documents/cross-language-sdk-guide.md create mode 100644 .trae/documents/sdk-initialization-patterns.md create mode 100644 .trae/rules/project-introduction.md delete mode 100644 .trae/rules/project_rules.md create mode 100644 .trae/rules/tracer-type.md diff --git a/.trae/documents/cross-language-sdk-guide.md b/.trae/documents/cross-language-sdk-guide.md new file mode 100644 index 0000000..668b588 --- /dev/null +++ b/.trae/documents/cross-language-sdk-guide.md @@ -0,0 +1,170 @@ +# 跨语言 SDK 实现指南:数据传递与序列化 + +## 背景 + +Structrail 平台的核心目标是提供一套**语言无关**的数据结构可视化协议。这意味着我们的 SDK 需要在不同特性的编程语言中(从高动态的 Python/JS 到静态底层的 C/C++)提供一致的功能体验。 + +其中,`preset` 指令(一次性同步数组/容器状态)在静态类型语言(特别是 C 语言)中的实现是最大的挑战。本文档旨在论证其可行性,并为未来各语言 SDK 的实现提供参考规范。 + +## 核心挑战:数组的异构性 + +在动态语言(TS, Python)中,数组是自描述的对象,包含长度和元素类型信息。 +在静态托管语言(Java, C#, Go)中,数组/列表也是对象,具备反射或自省能力。 +在 C/C++ 中,原生数组仅仅是内存地址(指针),且 C 语言完全丢失了长度和类型信息。 + +## 通用解决方案:SDK 接口分层 + +为了抹平差异,我们建议 SDK 接口设计遵循\*\*“渐进式暴露”\*\*原则: + +1. **Level 1: 智能推断接口**(针对 TS, Python, Java 等) + + * 用户直接传数组对象。 + + * SDK 内部自动获取长度、遍历序列化。 +2. **Level 2: 显式元数据接口**(针对 C++, Go, Rust 等) + + * 用户传递数据指针 + 长度。 + + * 利用泛型/模板自动推导元素序列化逻辑。 +3. **Level 3: 手动序列化接口**(针对 C 语言) + + * 用户传递数据指针 + 长度 + 元素大小 + 序列化策略。 + +*** + +## 各语言实现参考 + +### 1. TypeScript / JavaScript (已实现) + +利用语言的动态特性,直接接受 `any[]`。 + +```typescript +// SDK +preset(data: T[]) { + // JSON.stringify 天然支持数组序列化 + this.emit('preset', { data }); +} +``` + +### 2. Java / C# / Python + +利用标准库的反射或序列化能力。 + +**Java 示例**: + +```java +// SDK +public void preset(List data) { + // 使用 Jackson 或 Gson 库 + String json = gson.toJson(data); + this.emit("preset", json); +} +``` + +### 3. C++ (Modern C++) + +利用模板和 STL 容器特性。 + +**C++ 示例**: + +```cpp +// 针对 std::vector 的重载 +template +void preset(const std::vector& data) { + json j = data; // 使用 nlohmann/json 库,自动支持 STL 容器 + emit("preset", j); +} + +// 针对原生数组的重载 (C++20 std::span 最佳,或传指针+长度) +template +void preset(const T* arr, size_t size) { + std::vector vec(arr, arr + size); + preset(vec); +} +``` + +### 4. C 语言 (重点攻坚) + +C 语言没有泛型,没有反射,没有对象。我们需要用户手动提供元数据。 + +#### 方案 A: 宏魔法 (Generic Selection) - 推荐用于基础类型 + +利用 C11 `_Generic` 关键字模拟函数重载,提升基础类型的使用体验。 + +```c +// 底层 API +void _tracer_preset_int(tracer_t* t, int* arr, size_t len); +void _tracer_preset_float(tracer_t* t, float* arr, size_t len); + +// 用户宏接口 +#define tracer_preset(t, arr, len) _Generic((arr), \ + int*: _tracer_preset_int, \ + float*: _tracer_preset_float \ +)(t, arr, len) + +// 用户调用 +int nums[] = {1, 2, 3}; +tracer_preset(t, nums, 3); // 自动匹配 int 版本 +``` + +#### 方案 B: 格式化字符串 (Printf Style) - 推荐用于复杂类型 + +对于结构体或未覆盖的基础类型,采用类似 `printf` 的描述符。 + +```c +/** + * @param elem_fmt: 元素类型描述符 + * "d": int + * "f": float + * "s": string + * "{x:d,y:d}": struct Point {int x; int y} + */ +void tracer_preset_fmt(tracer_t* t, void* data, size_t len, size_t elem_size, const char* elem_fmt); + +// 用户调用 +struct Point pts[] = {{1,2}, {3,4}}; +tracer_preset_fmt(t, pts, 2, sizeof(struct Point), "{x:d,y:d}"); +``` + +#### 方案 C: 回调函数 (Callback) - 兜底方案 + +万能方案,用户自己负责把元素转成字符串。 + +```c +typedef void (*serializer_func)(void* elem, char* buffer); + +void tracer_preset_cb(tracer_t* t, void* data, size_t len, size_t elem_size, serializer_func cb); + +// 用户调用 +// 1. 用户定义序列化器 +void my_int_serializer(void* elem, char* buffer) { + sprintf(buffer, "%d", *(int*)elem); +} + +// 2. 传递给 SDK +int nums[] = {1, 2, 3}; +tracer_preset_cb(t, nums, 3, sizeof(int), my_int_serializer); +``` + +## 协议一致性保障 + +无论采用哪种语言实现,生成的 JSON 指令必须严格一致: + +```json +{ + "type": "ArrayTracer", + "tracer": "uuid-...", + "action": "preset", + "params": { + "data": [1, 2, 3, 4] // 或者是 [{"x":1,"y":2}, ...] + } +} +``` + +这意味着 C 语言 SDK 内部必须手动拼接 JSON 字符串(`[` + `elem` + `,` + `elem` + `]`)。 + +## 结论 + +1. **`preset`** **指令是可移植的**:即使在最底层的 C 语言中,通过适当的 API 封装(宏或格式化串),也能实现与高级语言近似的开发体验。 +2. **不要为了 C 语言阉割协议**:不需要因为 C 语言处理数组麻烦,就放弃 `preset` 而强迫所有语言都用 `patch` 循环。SDK 应该把复杂性封装在内部,留给用户简洁的接口。 + diff --git a/.trae/documents/sdk-initialization-patterns.md b/.trae/documents/sdk-initialization-patterns.md new file mode 100644 index 0000000..3e6cef9 --- /dev/null +++ b/.trae/documents/sdk-initialization-patterns.md @@ -0,0 +1,213 @@ +# 多语言 SDK 初始化设计规范 + +## 背景 +为了简化 SDK 的使用流程并统一 API 设计体验,我们决定将 Tracer 的初始化数据(Initial Data)合并到创建(Create)阶段。这意味着用户在实例化 Tracer 时,可以直接传入初始数据,而无需单独调用 `preset` 方法。 + +这一设计模式(Construct as Initialize)在大多数现代编程语言中都有成熟的最佳实践。本文档旨在为不同语言的 SDK 实现提供具体的代码范式参考。 + +## 核心原则 +1. **优先使用构造函数参数**:如果语言支持(如 Python, Kotlin, Swift),优先使用带默认值的命名参数。 +2. **利用语言特性**: + - **重载 (Overloading)**:适用于 Java, C++, C#。 + - **配置对象 (Options Object)**:适用于 TS, JS, Lua。 + - **Builder / Functional Options**:适用于 Go, Rust。 +3. **保持协议底层一致**:无论上层 API 如何设计,底层生成的 `create` 指令 JSON 必须包含 `array` (或对应数据字段) 参数。 + +--- + +## 各语言实现参考 + +### 1. TypeScript / JavaScript (当前基准) +利用接口(Interface)定义配置对象,简洁且扩展性强。 + +```typescript +// 定义 +interface ArrayTracerOptions { + description: string; + array?: T[]; // 可选初始化数据 +} + +export const createArrayTracer = (options: ArrayTracerOptions) => { ... } + +// 调用 +const t1 = createArrayTracer({ description: "Empty" }); +const t2 = createArrayTracer({ + description: "My Array", + array: [1, 2, 3] +}); +``` + +### 2. Python (Keyword Arguments) +利用 `**kwargs` 或显式关键字参数,非常符合 Pythonic 风格。 + +```python +class ArrayTracer: + def __init__(self, description: str, data: list = None): + self.description = description + if data: + self._emit_create(data) + +# 调用 +t1 = ArrayTracer(description="Empty") +t2 = ArrayTracer(description="My Array", data=[1, 2, 3]) +``` + +### 3. Java (Constructor Overloading) +利用构造函数重载提供多种初始化路径。 + +```java +public class ArrayTracer { + // 构造函数 1: 仅描述 + public ArrayTracer(String description) { + this(description, null); + } + + // 构造函数 2: 描述 + 数据 + public ArrayTracer(String description, List data) { + // ... implementation + } +} + +// 调用 +var t1 = new ArrayTracer("Empty"); +var t2 = new ArrayTracer("My Array", Arrays.asList(1, 2, 3)); +``` + +### 4. C++ (Overloading & Initializer List) +利用 `std::initializer_list` 支持花括号初始化,语法极其简洁。 + +```cpp +template +class ArrayTracer { +public: + // 基础构造 + ArrayTracer(std::string description) { ... } + + // 带数据构造 (支持 vector) + ArrayTracer(std::string description, const std::vector& data) { ... } + + // 带数据构造 (支持 {1,2,3} 字面量) + ArrayTracer(std::string description, std::initializer_list data) { ... } +}; + +// 调用 +ArrayTracer t1("Empty"); +ArrayTracer t2("My Array", {1, 2, 3}); +``` + +### 5. Go (Functional Options Pattern) +Go 社区处理复杂构造参数的标准模式。 + +```go +type Option func(*ArrayTracer) + +func WithData(data []interface{}) Option { + return func(t *ArrayTracer) { + t.initialData = data + } +} + +func NewArrayTracer(desc string, opts ...Option) *ArrayTracer { + t := &ArrayTracer{Description: desc} + for _, opt := range opts { + opt(t) + } + return t +} + +// 调用 +t1 := NewArrayTracer("Empty") +t2 := NewArrayTracer("My Array", WithData([]interface{}{1, 2, 3})) +``` + +### 6. Rust (Builder Pattern) +利用 Builder 模式处理构造参数,保证类型安全和可读性。 + +```rust +struct ArrayTracerBuilder { ... } + +impl ArrayTracer { + pub fn builder(description: &str) -> ArrayTracerBuilder { ... } +} + +impl ArrayTracerBuilder { + pub fn with_data(mut self, data: Vec) -> Self { ... } + pub fn build(self) -> ArrayTracer { ... } +} + +// 调用 +let t = ArrayTracer::builder("My Array") + .with_data(vec![1, 2, 3]) + .build(); +``` + +### 7. C# (Optional Arguments) +类似于 TypeScript 和 Kotlin,C# 支持命名参数和默认值。 + +```csharp +public class ArrayTracer { + public ArrayTracer(string description, IEnumerable data = null) { + // ... + } +} + +// 调用 +var t1 = new ArrayTracer("Empty"); +var t2 = new ArrayTracer("My Array", data: new[] { 1, 2, 3 }); +``` + +### 8. C 语言 (Special Case) +C 语言不支持重载,且缺乏自省能力,因此建议提供两种创建模式: + +**方案 A: 基础数据类型 (使用宏或特定后缀)** +对于 `int`, `float` 等基础类型,提供专用函数。 + +```c +// 基础创建 +tracer_t* tracer_create_array(const char* desc); + +// 带数据创建 (Explicit is better than implicit) +tracer_t* tracer_create_array_with_data(const char* desc, void* data, size_t len); + +// 调用 +tracer_t* t1 = tracer_create_array("Empty"); +int nums[] = {1, 2, 3}; +tracer_t* t2 = tracer_create_array_with_data("My Array", nums, 3); +``` + +**方案 B: 自定义结构体 (Callback 模式)** +对于用户自定义的 `struct`,采用类似 `qsort` 的回调函数模式,让用户提供序列化逻辑。 + +```c +// 定义序列化回调: 将 elem 转换为 JSON 字符串写入 buffer +typedef void (*serializer_func)(const void* elem, char* buffer); + +/** + * @param serializer 用户提供的序列化函数 + */ +tracer_t* tracer_create_array_custom( + const char* desc, + const void* data, + size_t len, + size_t elem_size, + serializer_func serializer +); + +// 用户代码示例 +typedef struct { int x; int y; } Point; + +// 用户编写序列化逻辑 +void point_serializer(const void* elem, char* buffer) { + const Point* p = (const Point*)elem; + sprintf(buffer, "{\"x\":%d,\"y\":%d}", p->x, p->y); +} + +// 调用 +Point pts[] = {{1,2}, {3,4}}; +tracer_t* t = tracer_create_array_custom("Points", pts, 2, sizeof(Point), point_serializer); +``` + +--- + +## 总结 +通过统一采用**“构造即初始化”**的设计模式,我们能够在几乎所有主流编程语言中提供一致、简洁且符合语言习惯(Idiomatic)的 SDK 使用体验。这不仅降低了用户的学习成本,也使得代码更加紧凑和易读。 diff --git a/.trae/rules/project-introduction.md b/.trae/rules/project-introduction.md new file mode 100644 index 0000000..c76495b --- /dev/null +++ b/.trae/rules/project-introduction.md @@ -0,0 +1,15 @@ +## 项目背景 + +我正在设计一款数据结构与算法可视化平台(以下简称为“平台”),允许用户在平台上编写算法的实现代码,当用户提交代码后,平台会能够获取到代码中所涉及的数据结构的变化过程,并以可视化的形式来渲染整个变化过程,并且将数据结构的变化与代码的执行过程关联起来,给用户的感受就像是在调试代码一样,算法执行到某一步时,代码中的相关部分会高亮显示。用户可以在平台上通过“下一步”功能来查看算法的执行过程中每一个步骤所对应的数据结构变化,并且还可以具有“时间旅行”的功能,允许用户通过“上一步”功能来向前回溯算法的执行。此外,从长期规划和产品化的角度来说,平台还会支持添加对多种主流编程语言的支持。 + +## 项目目标 + +基于上述设想,我肯定无法接受从编译技术的角度去适配每一种编程语言(例如代码插桩甚至改造编译器),因为这会导致平台的开发变得非常复杂,维护成本也会非常高。因此我的想法是设计一套语言无关的协议,用来描述各种数据结构的变化(跟踪器),因此这套协议会定义需要支持的数据结构类型,以及每一种数据结构类型需要支持的操作指令,并且我倾向于将指令设计得更加“底层”一些,类似于“原语”的概念。 + +> 例如针对顺序表结构,我可能会定义 pick(获取元素)、drop(取消获取)、patch(修改元素) 指令来替代 swap(交换元素) 操作,因为交换两个元素的本质就是 获取元素 + 修改元素,而我在其中添加 drop 指令的目的则是使可视化过程更加具体,例如 pick 指令会将其选定的元素高亮,那么 drop 指令就可以取消该元素的高亮行为。 + +我们会先选取一种编程语言(例如 TypeScript)来实现这套协议,在实现的过程中,需要考虑到未来将添加其他编程语言的支持,因此需要考虑到不同编程语言之间的差异,例如语法/类型系统差异,进而反过来优化这套协议,以使其能够更好地支持不同的编程语言,这是一个双向的过程。 + +当我们实现了这套协议后,就形成了一套 SDK,用户可以在平台中标记想要记录的数据结构(注册跟踪器),并调用 SDK 提供的 API 来记录数据结构的变化,当代码被编译运行后,就能够自然地获取数据结构的变化过程(序列化为 JSON)。 + +所以本项目的最终目标就是设计这一套语言无关的协议,并在多个主流编程语言上实现该协议。 diff --git a/.trae/rules/project_rules.md b/.trae/rules/project_rules.md deleted file mode 100644 index 59c2d7a..0000000 --- a/.trae/rules/project_rules.md +++ /dev/null @@ -1,15 +0,0 @@ -我正在设计一个数据结构与算法可视化平台,在我的设想中,平台会提供一个代码编辑器,用户可以在其中编写算法的实现代码,当用户点击运行按钮时,平台会调用后端的编译器来执行用户编写的代码,获取到算法执行过程中所涉及的数据结构的变化,并将整个过程可视化地展示在平台中。 - -从技术的角度,我希望它能够适配不同的编程语言,所以我不会考虑从编译技术的层面去适配不同的编程语言(例如自定义一个编译器),而是考虑设计一套多语言的 SDK 来实现这一目标。从整体上来看,SDK 提供的 API 用于“标记”数据结构的操作,用户可以在算法的实现中调用这些 API 来标记数据结构的变化,这样在代码被编译运行后,就能够获取数据结构的变化过程(序列化为 JSON)。 - -这样如果从用户的角度来看,用户可以几乎不修改算法的实现,而是只需要在合适的位置调用平台提供的 API 即可。 - -这个项目是关于这个平台的多语言 SDK 设计与实现方案。 - -项目中有这样一些概念: - -- Tracer: 用于记录数据结构的变化过程,每一种数据结构都有一个对应的 Tracer 类(ArrayTracer, StackTracer, QueueTracer, MatrixTracer, SortTracer, LinkTracer, TreeTracer, GraphTracer)。 - -- Action: 用于描述数据结构的操作类型,每一种操作都有一个对应的 Action。 - -- Command: 用于描述每一次数据结构操作的指令,一条指令包含了 tracer、action、params,并最终会序列化为 JSON。 diff --git a/.trae/rules/tracer-type.md b/.trae/rules/tracer-type.md new file mode 100644 index 0000000..7fc2611 --- /dev/null +++ b/.trae/rules/tracer-type.md @@ -0,0 +1,19 @@ +## Tracer 是什么? + +Tracer 代表一个数据结构变化的跟踪器,用户可以通过调用 Tracer 提供的 API 来记录数据结构的变化。每当用户想要记录一个数据结构的变化时,就需要创建一个对应的 Tracer。这种将数据结构变化的记录留给用户的设计,使得用户可以在算法的实现中灵活地记录数据结构的变化,从而降低对原有算法代码逻辑的侵入性,同时也在一定程度上提高了可视化效果的灵活度,用户可以自由控制记录数据结构变化的时机。 + +## Tracer 类型 + +- ArrayTracer: 顺序表(数组) +- StackTracer: 栈 +- QueueTracer: 队列 +- MatrixTracer: 矩阵 +- SortTracer: 排序算法(也许可以合并到 ArrayTracer,暂时不确定) +- LinkTracer: 链表 +- TreeTracer: 树 +- GraphTracer: 图 + +--- + +- LogTracer: 日志 +- ControlTracer: 控制