一、项目简介
varch是一个面向嵌入式C开发的通用框架库,集成了算法、数据结构、解析器、数学运算等40+个模块。
https://gitee.com/Lamdonn/varch
它的设计目标非常明确:简单、通用、高效,让开发者能够"拿来就用"。它最大的价值在于提供了一套生产级的、经过充分测试的组件库,让嵌入式工程师可以专注于业务逻辑而非基础设施。
二、整体架构设计
2.1 分层架构
varch 采用清晰的分层设计,自底向上构建了一个完整的生态系统:
自底向上的依赖关系:底层模块不依赖上层,任何模块都可以单独提取使用
横向解耦:同层模块之间尽量独立,避免循环依赖
渐进式复杂度:从简单的工具类到复杂的容器、算法,逐层递进
2.2 目录结构解析
varch/
├── source/ # 源代码目录
│ ├── 00_application/ # 应用层:初始化框架、控制台
│ ├── 01_general/ # 通用工具:队列控制器、链表控制器
│ ├── 02_vstd/ # 独立标准库:减少对libc依赖
│ ├── 03_container/ # 容器:queue/stack/vector/map等
│ ├── 04_algorithm/ # 算法:排序/搜索/哈希/CRC等
│ ├── 05_parser/ # 解析器:json/xml/csv/ini等
│ ├── 06_performance/ # 性能工具:单元测试/内存测试
│ └── 07_math/ # 数学库:大整数/大浮点数运算
├── test/ # 完整的单元测试
├── doc/ # 详细的中英文文档
└── makefile # 灵活的编译配置
从目录命名可以看出设计者的用心:每个目录都有序号前缀,这不仅方便浏览,更暗示了编译依赖顺序。
三、部分代码剖析
3.1 双轨制泛型设计:编译期 vs 运行时
varch 提供了两套并行的容器实现,分别针对不同场景:
3.1.1 cQueue:基于宏的编译期泛型
// cQueue使用
cQueue(int, 64) int_queue; // 定义一个容量为64的int队列
cQueue_init(int_queue);
cQueue_push(int_queue, 42);
int value;
cQueue_pop(int_queue, value);
它使用匿名结构体来实现,非常精妙!
#define cQueue(type, cap) struct { cQueue queue; type data[cap]; }
// 宏展开后实际上是:
struct {
cQueue queue; // 控制信息:head, tail, size, cap
int data[64]; // 数据数组
} int_queue;
设计亮点:
零运行时开销:所有类型信息在编译期确定,没有void*转换开销
类型安全:编译器能检查类型错误
栈上分配:适合资源受限的嵌入式系统
这种设计的工作原理如下:
3.1.2 queue:基于void*的运行时泛型
// 使用示例
queue_t q = queue(int, 64); // 创建队列
int value = 42;
queue_push(q, &value);
queue_pop(q, &value);
_queue(q); // 删除队列
它的实现采用了经典的不透明指针模式:
typedef struct QUEUE *queue_t; // 不透明指针
struct QUEUE {
void* base; // 数据缓冲区
int dsize; // 单个元素大小
int capacity;
int head, tail, size;
};
// 通过指针运算访问元素
#define at(i) (((unsigned char*)(queue->base)) + (i)*(queue->dsize))
设计亮点:
动态灵活:可以运行时创建任意大小的队列
信息隐藏:外部看不到内部实现,易于维护
3.2 自动初始化框架:链接脚本的艺术
嵌入式系统的初始化顺序至关重要:硬件初始化 → 驱动初始化 → 系统初始化 → 应用初始化。
varch 通过巧妙利用编译器特性,实现了声明式的自动初始化:
void my_driver_init(void) {
// 初始化代码
}
init_export_driver(my_driver_init); // 声明为驱动级初始化
void my_app_init(void) {
// 应用初始化
}
init_export_app(my_app_init); // 声明为应用级初始化
其实现原理是利用GCC的__attribute__((section))特性:
#define init_export_driver(func)
INIT_USED INIT_SECTION("init.2")
const init_item init_item_##func = {func}
// 展开后:
__attribute__((used)) __attribute__((section("init.2")))
const init_item init_item_my_driver_init = {my_driver_init};
链接器会将所有init.X段按序号排列,然后init_execute()遍历执行:
void init_execute(void) {
const init_item *it = &init_item_start + 1;
while (it < &init_item_end) {
if (it->handler) it->handler();
it++;
}
}
设计优势:
解耦合:每个模块独立声明自己的初始化函数,无需修改main()
自动排序:通过section名字自动控制执行顺序
这种技术在RT-Thread、Linux内核中都有应用,是嵌入式系统的经典设计模式。
3.3 独立标准库:最小化依赖
很多嵌入式系统的标准库不完整或占用空间大,varch 在02_vstd目录实现了精简的标准库替代:
// vstddef.h - 定义基础类型
typedefunsignedlongsize_t;
typedeflongptrdiff_t;
#define NULL ((void*)0)
#define offsetof(s,m) (size_t)&(((s*)0)->m)
// vstdint.h - 定义固定宽度整型
typedefsignedchar int8_t;
typedefunsignedchar uint8_t;
typedefsigned short int16_t;
// ...
// vstring.h/vmem.h - 实现字符串和内存操作
void* vmemcpy(void* dst, const void* src, size_t n);
int vmemcmp(const void* s1, const void* s2, size_t n);
这样做的好处:
可移植性强:不依赖特定编译器的标准库实现
体积可控:只包含必要的功能
行为一致:避免不同平台标准库的差异
四、例子
演示 varch json 解析器的基本用法:
#include <stdio.h>
#include <stdlib.h>
#include "json.h"
// 创建 JSON 对象
void demo_create_json()
{
printf("n========== Demo : 创建 JSON 对象 ==========n");
// 创建一个 JSON 对象
json_t root = json_create();
json_set_object(root, NULL);
// 添加各种类型的数据
json_add_string_to_object(root, "name", "varch");
json_add_string_to_object(root, "description", "嵌入式C框架库");
json_add_int_to_object(root, "version_major", 0);
json_add_int_to_object(root, "version_minor", 3);
json_add_int_to_object(root, "version_patch", 4);
json_add_bool_to_object(root, "is_open_source", JSON_TRUE);
// 添加数组
json_t modules = json_create_array_for_object("modules");
json_add_string_to_array(modules, "queue");
json_add_string_to_array(modules, "stack");
json_add_string_to_array(modules, "json");
json_add_string_to_array(modules, "vector");
json_attach(root, json_size(root), modules);
// 转换为字符串并打印
char* json_str = json_dumps(root, 512, 0, NULL);
if (json_str) {
printf("%sn", json_str);
free(json_str);
}
json_delete(root);
}
int main(int argc, char* argv[])
{
demo_create_json();
return0;
}
五、使用时需注意
- C89兼容性:部分代码使用了C99/C11特性,老编译器可能需要修改编译器依赖:init框架依赖GCC/ARM/IAR的section属性
1801