소스 정보
- 저장소
- aresbit/MateBot2tg
- 최근 소스 활동
- 2026년 2월 14일 03:18
- 감지된 SKILL.md 언어
- 중국어
- 스타
- 44
- 포크
- 6
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/aresbit/MateBot2tg --skill sp명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| name | sp |
| description | sp.h - 单头文件C标准库替代方案。 本指南提供了使用sp.h编写现代C代码的最佳实践。 即使你是一个AI助手,遵循这些规则也能写出高质量的C代码。 |
| license | MIT |
sp.h 是一个单头文件C标准库替代方案。它提供了:
// 在**一个**C文件中定义实现(通常是main.c)
#define SP_IMPLEMENTATION
#include "sp.h"
// 其他文件只包含头文件
#include "sp.h"
| 禁止 ❌ | 正确 ✅ |
|---|---|
malloc / calloc / realloc | sp_alloc() |
const char* | sp_str_t(ptr+len字符串) |
strcmp, strlen | sp_str_equal(), sp_str_len() |
printf | SP_LOG() |
memset(&obj, 0, sizeof(obj)) | SP_ZERO_INITIALIZE() |
裸for循环遍历数组 | sp_dyn_array_for() 或 sp_carr_for() |
// 有符号整数
s8 = int8_t // 8位
s16 = int16_t // 16位
s32 = int32_t // 32位
s64 = int64_t // 64位
// 无符号整数
u8 = uint8_t // 8位
u16 = uint16_t // 16位
u32 = uint32_t // 32位
u64 = uint64_t // 64位
// 浮点数
f32 = float // 32位
f64 = double // 64位
// 字符
c8 = char // UTF-8字符
// sp_str_t = { char* data; u32 len; }
// 不需要null终止!长度是已知的
typedef struct {
const c8* data;
u32 len;
} sp_str_t;
// 创建字符串
sp_str_t s1 = sp_str_lit("hello"); // 编译时常量,零分配
sp_str_t s2 = sp_str_view(c_string); // 从C字符串创建视图(计算长度)
sp_str_t s3 = sp_str_from_cstr(c_str); // 分配并复制
// 比较(不能用strcmp!)
bool equal = sp_str_equal(s1, s2);
bool starts_with = sp_str_starts_with(s1, sp_str_lit("he"));
// 检查空字符串(不能用len > 0)
bool is_empty = sp_str_empty(s1); // ✅ 正确
bool wrong = s1.len > 0; // ❌ 错误
// 所有分配都经过上下文分配器,自动初始化为零
void* ptr = sp_alloc(1024); // 分配1024字节,已清零
// 数组分配
u32* numbers = sp_alloc(sizeof(u32) * 100);
// 无需手动free,使用上下文自动管理
// 或者在需要时使用:
sp_free(ptr);
// 总是零初始化
my_struct_t obj = SP_ZERO_INITIALIZE(); // ✅ 正确
my_struct_t obj = {0}; // C99替代
// 使用指定初始化器(C99)
config_t cfg = {
.port = 8080,
.max_connections = 100,
.name = sp_str_lit("my_server")
};
// 声明数组(stb风格)
sp_dyn_array(int) numbers = SP_NULLPTR;
// 添加元素
sp_dyn_array_push(numbers, 42);
sp_dyn_array_push(numbers, 100);
// 访问
int first = numbers[0];
u32 count = sp_dyn_array_size(numbers);
u32 capacity = sp_dyn_array_capacity(numbers);
// 遍历(不要用裸for循环)
sp_dyn_array_for(numbers, i) {
SP_LOG("numbers[{}] = {}", SP_FMT_U32(i), SP_FMT_S32(numbers[i]));
}
// 清理(如果使用上下文分配器,通常不需要)
sp_dyn_array_free(numbers);
// 简写形式
sp_da(int) numbers = SP_NULLPTR; // sp_da = sp_dyn_array
// 所有操作宏
sp_dyn_array_push(arr, value); // 添加元素
sp_dyn_array_pop(arr); // 弹出最后一个
sp_dyn_array_back(arr); // 获取最后一个
sp_dyn_array_clear(arr); // 清空(不释放内存)
sp_dyn_array_free(arr); // 释放内存
sp_dyn_array_size(arr); // 获取元素数量
sp_dyn_array_capacity(arr); // 获取容量
sp_dyn_array_reserve(arr, n); // 预留容量
sp_dyn_array_resize(arr, n); // 调整大小
// 声明哈希表:key类型, value类型
sp_ht(sp_str_t, s32) scores = SP_NULLPTR;
// 设置自定义哈希和比较函数(用于字符串键)
sp_ht_set_fns(scores, sp_ht_hash_str, sp_ht_compare_str);
// 插入
sp_ht_insert(scores, sp_str_lit("alice"), 100);
sp_ht_insert(scores, sp_str_lit("bob"), 85);
// 查找
s32* score = sp_ht_getp(scores, sp_str_lit("alice"));
if (score) {
SP_LOG("Alice's score: {}", SP_FMT_S32(*score));
}
// 检查键是否存在
bool has_bob = sp_ht_key_exists(scores, sp_str_lit("bob"));
// 遍历
sp_ht_for(scores, it) {
sp_str_t* name = sp_ht_it_getkp(scores, it); // 获取键指针
s32* score = sp_ht_it_getp(scores, it); // 获取值指针
SP_LOG("{}: {}", SP_FMT_STR(*name), SP_FMT_S32(*score));
}
// 替代 printf,支持类型安全和颜色
SP_LOG("Hello, {}!", SP_FMT_CSTR("world"));
// 数字格式化
s32 num = 42;
SP_LOG("The answer is {}", SP_FMT_S32(num));
// 字符串格式化
sp_str_t name = sp_str_lit("Alice");
SP_LOG("Hello, {}", SP_FMT_STR(name));
// 颜色支持
SP_LOG("{:fg green}Success!{:reset}", SP_FMT_CSTR(""));
SP_LOG("{:fg red}Error:{:reset} {}", SP_FMT_CSTR(""), SP_FMT_CSTR("something went wrong"));
// 可用颜色: black, red, green, yellow, blue, magenta, cyan, white
// 加bright前缀: bright-red, bright-green, 等等
// 格式化到字符串
sp_str_t message = sp_format("Hello, {}!", SP_FMT_CSTR("world"));
// 使用完后释放(如果需要)
sp_free(message.data);
| 宏 | 用途 |
|---|---|
SP_FMT_S32(val) | s32整数 |
SP_FMT_U32(val) | u32整数 |
SP_FMT_S64(val) | s64整数 |
SP_FMT_U64(val) | u64整数 |
SP_FMT_F32(val) | f32浮点 |
SP_FMT_F64(val) | f64浮点 |
SP_FMT_CSTR(val) | C字符串 (const char*) |
SP_FMT_STR(val) | sp_str_t字符串 |
SP_FMT_BOOL(val) | bool |
SP_FMT_CHAR(val) | 字符 |
SP_FMT_PTR(val) | 指针 |
// 总是处理所有情况,使用花括号
switch (state) {
case STATE_IDLE: {
// 处理空闲状态
break;
}
case STATE_RUNNING: {
// 处理运行状态
break;
}
case STATE_STOPPED: {
// 处理停止状态
break;
}
default: {
SP_UNREACHABLE_CASE(); // 捕获未处理的情况
}
}
// 如果需要fallthrough,显式标记
switch (value) {
case 0: {
// 处理0
sp_fallthrough(); // 显式fallthrough
}
case 1: {
// 处理0和1
break;
}
}
// 对于可恢复错误,返回错误枚举
typedef enum {
ERR_OK = 0,
ERR_NOT_FOUND,
ERR_INVALID_INPUT,
ERR_OUT_OF_MEMORY,
} err_t;
err_t load_config(sp_str_t path, config_t* out_config) {
if (sp_str_empty(path)) {
return ERR_INVALID_INPUT;
}
if (!sp_os_path_exists(path)) {
SP_LOG("Config not found: {}", SP_FMT_STR(path));
return ERR_NOT_FOUND;
}
// ... 加载配置
return ERR_OK;
}
// 使用 SP_ASSERT 检查不变量
void process_items(item_t* items, u32 count) {
SP_ASSERT(items); // 检查非空
SP_ASSERT(count > 0); // 检查有效数量
// ... 处理
}
// SP_FATAL 用于不可恢复的错误
if (critical_failure) {
SP_FATAL("Cannot continue: {}", SP_FMT_STR(reason));
// 这会打印消息并终止程序
}
// sp_try - 传播错误
s32 result = some_operation();
sp_try(result); // 如果result != 0,返回result
// sp_try_as - 将错误映射为另一个
sp_try_as(failed_operation(), ERR_CUSTOM);
// sp_require - 要求条件为真
sp_require(ptr != NULL); // 如果失败,返回
sp_require_as(ptr != NULL, ERR); // 如果失败,返回ERR
// 路径操作
sp_str_t cwd = sp_os_get_cwd();
sp_str_t joined = sp_os_path_join(sp_str_lit("/home"), sp_str_lit("user"));
bool exists = sp_os_path_exists(path);
bool is_file = sp_os_path_is_file(path);
bool is_dir = sp_os_path_is_dir(path);
// 文件读写
sp_str_t content = sp_os_read_file(path);
sp_os_write_file(path, content);
sp_os_append_file(path, content);
// 目录操作
sp_os_mkdir(path);
sp_os_rmdir(path);
sp_dyn_array(sp_str_t) entries = sp_os_list_dir(path);
// 运行命令
sp_ps_result_t result = sp_ps_run(sp_str_lit("ls -la"));
if (result.status == 0) {
SP_LOG("Output: {}", SP_FMT_STR(result.stdout));
}
// 启动子进程
sp_ps_t* child = sp_ps_spawn(sp_str_lit("./my_program"));
sp_ps_wait(child);
// 数组长度(编译时)
int arr[] = {1, 2, 3, 4, 5};
u32 len = sp_carr_len(arr); // = 5
// 数组遍历
sp_carr_for(arr, i) {
SP_LOG("arr[{}] = {}", SP_FMT_U32(i), SP_FMT_S32(arr[i]));
}
// 最大值/最小值
s32 max = sp_max(a, b);
s32 min = sp_min(a, b);
// 交换
sp_swap(s32, a, b);
// 对齐
void* aligned = sp_align_up(ptr, 16);
#define SP_IMPLEMENTATION
#include "sp.h"
typedef struct {
sp_str_t name;
s32 score;
} player_t;
int main(void) {
// 零初始化
sp_dyn_array(player_t) players = SP_NULLPTR;
// 添加玩家
player_t alice = {
.name = sp_str_lit("Alice"),
.score = 100
};
sp_dyn_array_push(players, alice);
player_t bob = {
.name = sp_str_lit("Bob"),
.score = 85
};
sp_dyn_array_push(players, bob);
// 使用彩色日志输出
SP_LOG("{:fg cyan}Player Scores:{:reset}", SP_FMT_CSTR(""));
sp_dyn_array_for(players, i) {
player_t* p = &players[i];
SP_LOG(" {}: {:fg green}{}",
SP_FMT_STR(p->name),
SP_FMT_S32(p->score));
}
// 计算平均分
s32 total = 0;
sp_dyn_array_for(players, i) {
total += players[i].score;
}
f32 average = (f32)total / sp_dyn_array_size(players);
SP_LOG("Average: {:fg yellow}{:.2f}",
SP_FMT_F32(average));
return 0;
}
在提交代码前,确认:
SP_ZERO_INITIALIZE() 初始化所有结构体sp_str_t 而不是 const char*sp_alloc() 而不是 malloc()SP_LOG() 而不是 printf()sp_str_empty() 而不是检查 len > 0sp_dyn_array_for() 或 sp_carr_for() 遍历数组sp_str_equal() 而不是 strcmp()// ❌ 错误: 使用C字符串
const char* name = "Alice";
printf("Hello %s\n", name);
// ✅ 正确: 使用sp_str_t
sp_str_t name = sp_str_lit("Alice");
SP_LOG("Hello {}", SP_FMT_STR(name));
// ❌ 错误: 手动计算字符串长度
if (strlen(str) > 0) { ... }
// ✅ 正确: 使用sp.h的API
if (!sp_str_empty(str)) { ... }
// ❌ 错误: 裸malloc
int* arr = malloc(sizeof(int) * 10);
// ✅ 正确: 使用sp_alloc
int* arr = sp_alloc(sizeof(int) * 10);
// ❌ 错误: 手动for循环
for (u32 i = 0; i < sp_dyn_array_size(arr); i++) { ... }
// ✅ 正确: 使用遍历宏
sp_dyn_array_for(arr, i) { ... }
以下是在 TED (Termux Editor) 项目中应用 sp.h 的经验总结:
sp.h 是单头文件库,需要在一个且仅一个 C 文件中定义 SP_IMPLEMENTATION 宏:
// 在 main.c 中:
#define SP_IMPLEMENTATION
#include "sp.h"
// 在其他文件中只需包含头文件:
#include "sp.h"
错误现象:多个 .o 文件中出现重复定义的链接错误。
解决方案:确保 SP_IMPLEMENTATION 只在主源文件中定义一次。
在 Android/Termux 环境中,某些 POSIX 函数不可用:
posix_spawn_file_actions_addchdir_np 在 Android 上缺失
解决方案:在编译时添加 -DSP_PS_DISABLE 禁用进程支持模块:CFLAGS += -DSP_PS_DISABLE
sp.h 的 API 命名有特定规则,常见错误包括:
sp_str_eq → 正确:sp_str_equalsp_cstr_eq_n → 正确:strncmp(标准库函数)sp_os_read_entire_file → 正确:sp_io_read_filesp_str_builder_create → 正确:使用 sp_io_writer_from_dyn_mem() + sp_str_builder_from_writer()sp_str_t 结构使用 .data 成员,而不是 .ptr:
// ❌ 错误
sp_str_t str = ...;
c8 ch = str.ptr[i];
// ✅ 正确
c8 ch = str.data[i];
sp.h 没有提供 sp_io_stdout() 函数:
// ✅ 正确方式
sp_io_writer_t stdout_writer = sp_io_writer_from_fd(
STDOUT_FILENO,
SP_IO_CLOSE_MODE_NONE
);
sp_io_write_str(stdout_writer, text);
SP_ZERO_INITIALIZE() 不能用于赋值语句:
// ❌ 错误(全局变量)
editor_t E = SP_ZERO_INITIALIZE();
// ✅ 正确方式
editor_t E;
sp_memset(&E, 0, sizeof(E));
使用 isalpha、isdigit 等函数时需要包含 <ctype.h>:
#include <ctype.h> // 必须包含
sp_str_lit() 宏用于字符串字面量,不适用于单个字符:
// ❌ 错误
sp_str_t ch = sp_str_lit("a"); // 实际上是字符串 "a"
// ✅ 对于单个字符操作,使用字符类型 c8
c8 ch = 'a';
文件操作应使用 sp_io_* 系列函数:
sp_str_t content = sp_io_read_file(path);sp_io_writer_from_file() + sp_io_write_str() + sp_io_writer_close()reference/index.md 查找正确的函数签名这些经验来自 TED 编辑器的实际开发过程,希望能帮助其他开发者更顺利地在项目中使用 sp.h 库。