Compare commits
4 Commits
b2d7f18213
...
0ae8df927d
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ae8df927d | |||
| b67115b50c | |||
| e874d1bb21 | |||
| 881185e8cd |
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# dependencies (bun install)
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# output
|
||||||
|
out
|
||||||
|
dist
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# code coverage
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# logs
|
||||||
|
logs
|
||||||
|
_.log
|
||||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# caches
|
||||||
|
.eslintcache
|
||||||
|
.cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# IntelliJ based IDEs
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Finder (MacOS) folder config
|
||||||
|
.DS_Store
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
Structrail 平台的核心目标是提供一套**语言无关**的数据结构可视化协议。这意味着我们的 SDK 需要在不同特性的编程语言中(从高动态的 Python/JS 到静态底层的 C/C++)提供一致的功能体验。
|
Structrail 平台的核心目标是提供一套**语言无关**的数据结构可视化协议。这意味着我们的 SDK 需要在不同特性的编程语言中(从高动态的 Python/JS 到静态底层的 C/C++)提供一致的功能体验。
|
||||||
|
|
||||||
|
**注意,我们不再需要 `preset` 指令了,但是即便 `preset` 的参数被合并到 `create` 指令中,这篇文档对于如何在不同语言中实现 `preset` 功能的说明仍然是有价值的。**
|
||||||
|
|
||||||
其中,`preset` 指令(一次性同步数组/容器状态)在静态类型语言(特别是 C 语言)中的实现是最大的挑战。本文档旨在论证其可行性,并为未来各语言 SDK 的实现提供参考规范。
|
其中,`preset` 指令(一次性同步数组/容器状态)在静态类型语言(特别是 C 语言)中的实现是最大的挑战。本文档旨在论证其可行性,并为未来各语言 SDK 的实现提供参考规范。
|
||||||
|
|
||||||
## 核心挑战:数组的异构性
|
## 核心挑战:数组的异构性
|
||||||
@@ -17,20 +19,17 @@ Structrail 平台的核心目标是提供一套**语言无关**的数据结构
|
|||||||
为了抹平差异,我们建议 SDK 接口设计遵循\*\*“渐进式暴露”\*\*原则:
|
为了抹平差异,我们建议 SDK 接口设计遵循\*\*“渐进式暴露”\*\*原则:
|
||||||
|
|
||||||
1. **Level 1: 智能推断接口**(针对 TS, Python, Java 等)
|
1. **Level 1: 智能推断接口**(针对 TS, Python, Java 等)
|
||||||
|
- 用户直接传数组对象。
|
||||||
|
- SDK 内部自动获取长度、遍历序列化。
|
||||||
|
|
||||||
* 用户直接传数组对象。
|
|
||||||
|
|
||||||
* SDK 内部自动获取长度、遍历序列化。
|
|
||||||
2. **Level 2: 显式元数据接口**(针对 C++, Go, Rust 等)
|
2. **Level 2: 显式元数据接口**(针对 C++, Go, Rust 等)
|
||||||
|
- 用户传递数据指针 + 长度。
|
||||||
|
- 利用泛型/模板自动推导元素序列化逻辑。
|
||||||
|
|
||||||
* 用户传递数据指针 + 长度。
|
|
||||||
|
|
||||||
* 利用泛型/模板自动推导元素序列化逻辑。
|
|
||||||
3. **Level 3: 手动序列化接口**(针对 C 语言)
|
3. **Level 3: 手动序列化接口**(针对 C 语言)
|
||||||
|
- 用户传递数据指针 + 长度 + 元素大小 + 序列化策略。
|
||||||
|
|
||||||
* 用户传递数据指针 + 长度 + 元素大小 + 序列化策略。
|
---
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
## 各语言实现参考
|
## 各语言实现参考
|
||||||
|
|
||||||
@@ -156,7 +155,7 @@ tracer_preset_cb(t, nums, 3, sizeof(int), my_int_serializer);
|
|||||||
"tracer": "uuid-...",
|
"tracer": "uuid-...",
|
||||||
"action": "preset",
|
"action": "preset",
|
||||||
"params": {
|
"params": {
|
||||||
"data": [1, 2, 3, 4] // 或者是 [{"x":1,"y":2}, ...]
|
"data": [1, 2, 3, 4] // 或者是 [{"x":1,"y":2}, ...]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -167,4 +166,3 @@ tracer_preset_cb(t, nums, 3, sizeof(int), my_int_serializer);
|
|||||||
|
|
||||||
1. **`preset`** **指令是可移植的**:即使在最底层的 C 语言中,通过适当的 API 封装(宏或格式化串),也能实现与高级语言近似的开发体验。
|
1. **`preset`** **指令是可移植的**:即使在最底层的 C 语言中,通过适当的 API 封装(宏或格式化串),也能实现与高级语言近似的开发体验。
|
||||||
2. **不要为了 C 语言阉割协议**:不需要因为 C 语言处理数组麻烦,就放弃 `preset` 而强迫所有语言都用 `patch` 循环。SDK 应该把复杂性封装在内部,留给用户简洁的接口。
|
2. **不要为了 C 语言阉割协议**:不需要因为 C 语言处理数组麻烦,就放弃 `preset` 而强迫所有语言都用 `patch` 循环。SDK 应该把复杂性封装在内部,留给用户简洁的接口。
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
# 多语言 SDK 初始化设计规范
|
# 多语言 SDK 初始化设计规范
|
||||||
|
|
||||||
## 背景
|
## 背景
|
||||||
|
|
||||||
为了简化 SDK 的使用流程并统一 API 设计体验,我们决定将 Tracer 的初始化数据(Initial Data)合并到创建(Create)阶段。这意味着用户在实例化 Tracer 时,可以直接传入初始数据,而无需单独调用 `preset` 方法。
|
为了简化 SDK 的使用流程并统一 API 设计体验,我们决定将 Tracer 的初始化数据(Initial Data)合并到创建(Create)阶段。这意味着用户在实例化 Tracer 时,可以直接传入初始数据,而无需单独调用 `preset` 方法。
|
||||||
|
|
||||||
这一设计模式(Construct as Initialize)在大多数现代编程语言中都有成熟的最佳实践。本文档旨在为不同语言的 SDK 实现提供具体的代码范式参考。
|
这一设计模式(Construct as Initialize)在大多数现代编程语言中都有成熟的最佳实践。本文档旨在为不同语言的 SDK 实现提供具体的代码范式参考。
|
||||||
|
|
||||||
## 核心原则
|
## 核心原则
|
||||||
|
|
||||||
1. **优先使用构造函数参数**:如果语言支持(如 Python, Kotlin, Swift),优先使用带默认值的命名参数。
|
1. **优先使用构造函数参数**:如果语言支持(如 Python, Kotlin, Swift),优先使用带默认值的命名参数。
|
||||||
2. **利用语言特性**:
|
2. **利用语言特性**:
|
||||||
- **重载 (Overloading)**:适用于 Java, C++, C#。
|
- **重载 (Overloading)**:适用于 Java, C++, C#。
|
||||||
@@ -18,6 +20,7 @@
|
|||||||
## 各语言实现参考
|
## 各语言实现参考
|
||||||
|
|
||||||
### 1. TypeScript / JavaScript (当前基准)
|
### 1. TypeScript / JavaScript (当前基准)
|
||||||
|
|
||||||
利用接口(Interface)定义配置对象,简洁且扩展性强。
|
利用接口(Interface)定义配置对象,简洁且扩展性强。
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
@@ -38,6 +41,7 @@ const t2 = createArrayTracer({
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 2. Python (Keyword Arguments)
|
### 2. Python (Keyword Arguments)
|
||||||
|
|
||||||
利用 `**kwargs` 或显式关键字参数,非常符合 Pythonic 风格。
|
利用 `**kwargs` 或显式关键字参数,非常符合 Pythonic 风格。
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -53,6 +57,7 @@ t2 = ArrayTracer(description="My Array", data=[1, 2, 3])
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 3. Java (Constructor Overloading)
|
### 3. Java (Constructor Overloading)
|
||||||
|
|
||||||
利用构造函数重载提供多种初始化路径。
|
利用构造函数重载提供多种初始化路径。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@@ -74,6 +79,7 @@ var t2 = new ArrayTracer<Integer>("My Array", Arrays.asList(1, 2, 3));
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 4. C++ (Overloading & Initializer List)
|
### 4. C++ (Overloading & Initializer List)
|
||||||
|
|
||||||
利用 `std::initializer_list` 支持花括号初始化,语法极其简洁。
|
利用 `std::initializer_list` 支持花括号初始化,语法极其简洁。
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -96,6 +102,7 @@ ArrayTracer<int> t2("My Array", {1, 2, 3});
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 5. Go (Functional Options Pattern)
|
### 5. Go (Functional Options Pattern)
|
||||||
|
|
||||||
Go 社区处理复杂构造参数的标准模式。
|
Go 社区处理复杂构造参数的标准模式。
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -121,6 +128,7 @@ t2 := NewArrayTracer("My Array", WithData([]interface{}{1, 2, 3}))
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 6. Rust (Builder Pattern)
|
### 6. Rust (Builder Pattern)
|
||||||
|
|
||||||
利用 Builder 模式处理构造参数,保证类型安全和可读性。
|
利用 Builder 模式处理构造参数,保证类型安全和可读性。
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
@@ -142,6 +150,7 @@ let t = ArrayTracer::builder("My Array")
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 7. C# (Optional Arguments)
|
### 7. C# (Optional Arguments)
|
||||||
|
|
||||||
类似于 TypeScript 和 Kotlin,C# 支持命名参数和默认值。
|
类似于 TypeScript 和 Kotlin,C# 支持命名参数和默认值。
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
@@ -157,6 +166,7 @@ var t2 = new ArrayTracer<int>("My Array", data: new[] { 1, 2, 3 });
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 8. C 语言 (Special Case)
|
### 8. C 语言 (Special Case)
|
||||||
|
|
||||||
C 语言不支持重载,且缺乏自省能力,因此建议提供两种创建模式:
|
C 语言不支持重载,且缺乏自省能力,因此建议提供两种创建模式:
|
||||||
|
|
||||||
**方案 A: 基础数据类型 (使用宏或特定后缀)**
|
**方案 A: 基础数据类型 (使用宏或特定后缀)**
|
||||||
@@ -210,4 +220,5 @@ tracer_t* t = tracer_create_array_custom("Points", pts, 2, sizeof(Point), point_
|
|||||||
---
|
---
|
||||||
|
|
||||||
## 总结
|
## 总结
|
||||||
|
|
||||||
通过统一采用**“构造即初始化”**的设计模式,我们能够在几乎所有主流编程语言中提供一致、简洁且符合语言习惯(Idiomatic)的 SDK 使用体验。这不仅降低了用户的学习成本,也使得代码更加紧凑和易读。
|
通过统一采用**“构造即初始化”**的设计模式,我们能够在几乎所有主流编程语言中提供一致、简洁且符合语言习惯(Idiomatic)的 SDK 使用体验。这不仅降低了用户的学习成本,也使得代码更加紧凑和易读。
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ bun install
|
|||||||
To run:
|
To run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run index.ts
|
bun run src/index.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
This project was created using `bun init` in bun v1.2.23. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
This project was created using `bun init` in bun v1.3.4. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 1,
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"": {
|
"": {
|
||||||
"name": "tracers.ts",
|
"name": "tracers.ts",
|
||||||
@@ -12,15 +13,11 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@types/bun": ["@types/bun@1.3.2", "", { "dependencies": { "bun-types": "1.3.2" } }, "sha512-t15P7k5UIgHKkxwnMNkJbWlh/617rkDGEdSsDbu+qNHTaz9SKf7aC8fiIlUdD5RPpH6GEkP0cK7WlvmrEBRtWg=="],
|
"@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="],
|
||||||
|
|
||||||
"@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
|
"@types/node": ["@types/node@25.2.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w=="],
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.2.4", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-tBFxBp9Nfyy5rsmefN+WXc1JeW/j2BpBHFdLZbEVfs9wn3E3NRFxwV0pJg8M1qQAexFpvz73hJXFofV0ZAu92A=="],
|
"bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="],
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.3.2", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-i/Gln4tbzKNuxP70OWhJRZz1MRfvqExowP7U6JKoI8cntFrtxg7RJK3jvz7wQW54UuvNC8tbKHHri5fy74FVqg=="],
|
|
||||||
|
|
||||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
|
||||||
|
|
||||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "tracers.ts",
|
"name": "tracers.ts",
|
||||||
"module": "index.ts",
|
"module": "src/index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
const logTracer = createLogTracer({ description: 'LogTracer' });
|
const logTracer = createLogTracer({ description: 'LogTracer' });
|
||||||
const controlTracer = createControlTracer({ description: 'ControlTracer' });
|
const controlTracer = createControlTracer({ description: 'ControlTracer' });
|
||||||
|
|
||||||
const arrayTracer = createArrayTracer<number>({
|
const arrayTracer = createArrayTracer({
|
||||||
description: 'ArrayTracer',
|
description: 'ArrayTracer',
|
||||||
array: [1, 2, 3],
|
array: [1, 2, 3],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { getTracerContext } from '../context';
|
import { getTracerContext } from '../context';
|
||||||
import type { JsonValue } from '../types';
|
import type { JsonValue } from '../types';
|
||||||
|
|
||||||
interface ArrayTracerCreateOptions<T extends JsonValue> {
|
interface ArrayTracerCreateOptions<T extends JsonValue[]> {
|
||||||
description?: string;
|
description?: string;
|
||||||
array?: T[];
|
array?: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createArrayTracer = <T extends JsonValue>(
|
export const createArrayTracer = <T extends JsonValue[]>(
|
||||||
options: ArrayTracerCreateOptions<T>,
|
options: ArrayTracerCreateOptions<T>,
|
||||||
) => {
|
) => {
|
||||||
const { description = 'ArrayTracer', array } = options;
|
const { description = 'ArrayTracer', array } = options;
|
||||||
@@ -24,17 +24,6 @@ export const createArrayTracer = <T extends JsonValue>(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const preset = (array: T[]) => {
|
|
||||||
command({
|
|
||||||
type: 'ArrayTracer',
|
|
||||||
tracer: tracer,
|
|
||||||
action: 'preset',
|
|
||||||
params: {
|
|
||||||
array: array,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const scale = (size: number) => {
|
const scale = (size: number) => {
|
||||||
command({
|
command({
|
||||||
type: 'ArrayTracer',
|
type: 'ArrayTracer',
|
||||||
@@ -68,7 +57,7 @@ export const createArrayTracer = <T extends JsonValue>(
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const patch = (index: number, value: T) => {
|
const patch = (index: number, value: T[number]) => {
|
||||||
command({
|
command({
|
||||||
type: 'ArrayTracer',
|
type: 'ArrayTracer',
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
@@ -92,7 +81,6 @@ export const createArrayTracer = <T extends JsonValue>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// preset,
|
|
||||||
scale,
|
scale,
|
||||||
pick,
|
pick,
|
||||||
drop,
|
drop,
|
||||||
|
|||||||
@@ -12,13 +12,6 @@ type ArrayTracerCreateCommand = BaseArrayTracerCommand & {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type ArrayTracerPresetCommand = BaseArrayTracerCommand & {
|
|
||||||
action: 'preset';
|
|
||||||
params: {
|
|
||||||
array: JsonValue[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type ArrayTracerScaleCommand = BaseArrayTracerCommand & {
|
type ArrayTracerScaleCommand = BaseArrayTracerCommand & {
|
||||||
action: 'scale';
|
action: 'scale';
|
||||||
params: {
|
params: {
|
||||||
@@ -57,7 +50,6 @@ type ArrayTracerUnsetCommand = BaseArrayTracerCommand & {
|
|||||||
|
|
||||||
export type ArrayTracerCommand =
|
export type ArrayTracerCommand =
|
||||||
| ArrayTracerCreateCommand
|
| ArrayTracerCreateCommand
|
||||||
| ArrayTracerPresetCommand
|
|
||||||
| ArrayTracerScaleCommand
|
| ArrayTracerScaleCommand
|
||||||
| ArrayTracerPickCommand
|
| ArrayTracerPickCommand
|
||||||
| ArrayTracerDropCommand
|
| ArrayTracerDropCommand
|
||||||
|
|||||||
Reference in New Issue
Block a user