扫码加入

  • 正文
  • 相关推荐
申请入驻 产业图谱

一个面向嵌入式C开发的通用框架库!

2025/11/16
1801
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

一、项目简介

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属性

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录

本公众号专注于嵌入式技术,包括但不限于C/C++、嵌入式、物联网、Linux等编程学习笔记,同时,公众号内包含大量的学习资源。欢迎关注,一同交流学习,共同进步!