| name | spclib |
| description | This skill should be used when the user asks to "write C code with sp.h", "use spclib", "sp.h API", "single-header C library",
"modern C programming", "sp_str_t usage", "sp_alloc memory", "sp_log formatting", "dynamic array in C", "hash table in C",
"cross-platform C code", or when working with the sp.h single-header C standard library replacement.
|
| license | MIT |
spclib - sp.h Programming Guide
sp.h is a single-header C standard library replacement providing modern, type-safe APIs for memory management, strings, containers, IO, and cross-platform system operations.
Core Principles
Always follow these rules when using sp.h:
| Never Use | Use Instead |
|---|
malloc/calloc/realloc | sp_alloc(mem, size) |
const char* | sp_str_t (ptr+len string) |
strcmp/strlen | sp_str_equal() / sp_str_empty() |
printf | sp_log() / sp_print() |
memset(&obj, 0, sizeof(obj)) | sp_mem_zero(&obj, sizeof(obj)) |
for(i=0; i<n; i++) on C arrays | sp_carr_for() / sp_da_for() |
Quick Reference
Setup
#define SP_IMPLEMENTATION
#include "sp.h"
Types
s8/s16/s32/s64 - signed integers
u8/u16/u32/u64 - unsigned integers
f32/f64 - floats
c8 - char (UTF-8)
sp_str_t - {const c8* data, u32 len} string (no null terminator required)
Memory Allocator
sp_mem_t mem = sp_mem_os_new();
void* ptr = sp_alloc(mem, 1024);
sp_free(mem, ptr);
sp_mem_arena_marker_t mark = sp_mem_begin_scratch();
sp_str_t msg = sp_fmt(sp_mem_get_scratch(), "hello {}", name);
sp_mem_end_scratch(mark);
Zero Initialization
my_struct_t obj = sp_zero;
my_struct_t* p = sp_alloc(mem, sizeof(my_struct_t));
sp_mem_zero(p, sizeof(my_struct_t));
String Operations
sp_str_t s = sp_str_lit("hello");
sp_str_t v = sp_str_view(cstr);
bool eq = sp_str_equal(a, b);
bool empty = sp_str_empty(s);
sp_str_t copy = sp_str_copy(mem, s);
Formatting & Logging
sp_log("Value = {}", x);
sp_print("Hello {}", name);
sp_str_t s = sp_fmt(mem, "x={}, y={}", x, y);
sp_fatal("unrecoverable: {}", err_code);
Dynamic Arrays
sp_da(int) arr = SP_NULLPTR;
sp_da_push(arr, 42);
sp_da_push(arr, 99);
sp_da_for(arr, i) {
sp_log("arr[{}] = {}", i, arr[i]);
}
u64 n = sp_da_size(arr);
sp_da_free(arr);
Directory Iteration
sp_fs_for(mem, dir, it) {
sp_log("Entry: {}", it.entry.name);
}
sp_fs_for_recursive(mem, dir, it) {
sp_log("Path: {}", it.entry.path);
}
Hash Tables
sp_str_ht(s32) map = SP_NULLPTR;
sp_str_ht_init(mem, map);
sp_str_ht_insert(map, sp_str_lit("key"), 100);
s32* val = sp_str_ht_get(map, sp_str_lit("key"));
sp_cstr_ht(s32) cmap = SP_NULLPTR;
sp_cstr_ht_init(mem, cmap);
sp_cstr_ht_insert(cmap, "hello", 42);
sp_ht(u64, f32) ht = SP_NULLPTR;
sp_ht_init(mem, ht);
sp_ht_insert(ht, 100, 3.14f);
f32* v = sp_ht_getp(ht, 100);
File IO
sp_str_t content;
sp_err_t err = sp_io_read_file(mem, sp_str_lit("path/to/file"), &content);
sp_io_file_writer_t w;
sp_io_file_writer_from_path(&w, sp_str_lit("output.txt"));
sp_io_write_str((sp_io_writer_t*)&w, sp_str_lit("hello"), SP_NULLPTR);
sp_io_file_writer_close(&w);
Error Handling
sp_try(expr);
sp_try_goto(expr, err, label);
sp_require(ptr != SP_NULLPTR);
SP_ASSERT(condition);
sp_fatal("msg {}", detail);
Switch Statements
switch (val) {
case A: { break; }
case B: { break; }
default: { SP_UNREACHABLE_CASE(); }
}
Module Namespaces
Search references/index.md for detailed API signatures:
| Namespace | Purpose | Key Functions |
|---|
sp_str_* | String operations | sp_str_lit, sp_str_view, sp_str_equal, sp_str_empty |
sp_cstr_* | C string operations | sp_cstr_len, sp_cstr_equal |
sp_da / sp_da_* | Dynamic arrays | sp_da(T), sp_da_push, sp_da_for, sp_da_size |
sp_ht / sp_str_ht / sp_cstr_ht | Hash tables | sp_ht_insert, sp_ht_getp, sp_str_ht_init |
sp_alloc / sp_free | Memory allocation | sp_alloc, sp_alloc_n, sp_free, sp_mem_zero |
sp_io_* | File IO | sp_io_read_file, sp_io_write_str |
sp_fs_* | Filesystem | sp_fs_exists, sp_fs_for, sp_fs_for_recursive |
sp_ps_* | Processes | sp_ps_run, sp_ps_create |
sp_tm_* | Time | sp_tm_now_epoch, sp_tm_now_point |
sp_thread_* | Threads | sp_thread_init, sp_thread_join |
sp_mutex_* | Mutexes | sp_mutex_init, sp_mutex_lock |
sp_env_* | Environment | sp_env_get, sp_env_set |
sp_os_* | Platform | sp_os_get_kind, sp_os_sleep_ms |
sp_log / sp_print / sp_fmt | Logging/formatting | sp_log, sp_print, sp_fmt, sp_fatal |
Common Patterns
Allocator Setup
sp_mem_t mem = sp_mem_os_new();
sp_mem_arena_marker_t mark = sp_mem_begin_scratch();
sp_mem_end_scratch(mark);
Error Propagation
sp_err_t do_work(sp_mem_t mem) {
sp_str_t content;
sp_try(sp_io_read_file(mem, sp_str_lit("data.txt"), &content));
return SP_OK;
}
String Building
sp_io_dyn_mem_writer_t w;
sp_io_dyn_mem_writer_init(mem, &w);
sp_io_write_cstr((sp_io_writer_t*)&w, "Hello ", SP_NULLPTR);
sp_io_write_str((sp_io_writer_t*)&w, name, SP_NULLPTR);
sp_str_t result = sp_io_dyn_mem_writer_as_str(&w);
Reference Files
For complete API documentation with full function signatures:
references/index.md - Comprehensive API reference (auto-generated from sp.h upstream)
include/sp.h - The actual single-header library source (authoritative)
Examples
Example code demonstrating sp.h usage:
| File | Description |
|---|
references/example/msvc.c | MSVC compiler specific examples |
references/example/cli/palette.c | Terminal color palette demo |
references/example/cli/prompt.c | Interactive CLI prompt example |
references/example/freestanding/embed.c | Embedded usage example |
Practical Tips & Pitfalls
SP_IMPLEMENTATION 的正确使用
sp.h 是单头文件库,需要在一个且仅一个 C 文件中定义 SP_IMPLEMENTATION 宏:
#define SP_IMPLEMENTATION
#include "sp.h"
#include "sp.h"
错误现象:多个 .o 文件中出现重复定义的链接错误。
Android/Termux 平台适配
在 Android/Termux 环境中,某些 POSIX 函数不可用:
posix_spawn_file_actions_addchdir_np 在 Android 上缺失
解决方案:在编译时添加 -DSP_PS_DISABLE 禁用进程支持模块:
CFLAGS += -DSP_PS_DISABLE
API 名称的常见错误
sp_str_eq → 正确:sp_str_equal
sp_os_read_entire_file → 正确:sp_io_read_file
sp_dyn_array(T) / sp_dyn_array_push / sp_dyn_array_for → 正确:sp_da(T) / sp_da_push / sp_da_for
字符串结构成员
sp_str_t 结构使用 .data 成员,而不是 .ptr:
c8 ch = str.ptr[i];
c8 ch = str.data[i];
零初始化注意事项
sp_zero 不能用于全局变量赋值(它不是编译时常量表达式):
editor_t E = sp_zero;
editor_t E;
sp_mem_zero(&E, sizeof(E));
注意:sp_zero 在局部变量和 sp_alloc + sp_mem_zero 组合使用是安全的。
字符处理头文件
使用 isalpha、isdigit 等函数时需要包含 <ctype.h>:
#include <ctype.h>
文件读写 API
文件操作应使用 sp_io_* 系列函数:
- 读取:
sp_err_t err = sp_io_read_file(mem, path, &content);(注意第三个参数是输出指针)
- 写入:使用
sp_io_file_writer_from_path() + sp_io_write_str() + sp_io_file_writer_close()
格式化参数无需包裹宏
sp.h 的格式字符串使用 {} 占位,参数直接传入:
sp_log("Value: {}", SP_FMT_S32(x));
sp_log("Value: {}", x);
sp_log("Name: {}, Age: {}", name, age);
Common Mistakes
const char* name = "Alice";
printf("Hello %s\n", name);
sp_str_t name = sp_str_lit("Alice");
sp_log("Hello {}", name);
if (strlen(str) > 0) { ... }
if (!sp_str_empty(str)) { ... }
int* arr = malloc(sizeof(int) * 10);
int* arr = sp_alloc(mem, sizeof(int) * 10);
for (u32 i = 0; i < sp_da_size(arr); i++) { ... }
sp_da_for(arr, i) { ... }
SP_LOG("Value: {}", SP_FMT_STR(name));
sp_log("Value: {}", name);
Checklist
在提交代码前,确认:
Finding APIs
When looking for a specific function:
- Check the namespace table above
- Search
references/index.md for the pattern
- Search
include/sp.h for the exact function/macro definition (authoritative)
- All public APIs are marked with
SP_API in the source