分享一个嵌入式代码生成器设计思路!

原创 嵌入式大杂烩 2024-09-14 11:38

大家好,我是杂烩君。

在之前转载的文章:嵌入式中,我们如何面对单调重复的任务?中,李先静前辈提到一点:让电脑去做单调重复的工作。

这点让我很受启发,在工作中需要这类重复性的工作时,我也会编写代码生成器来帮我处理。最近,又完成了一个代码生成器的开发,一键生成大部分原本需要靠体力输出的相似代码,极大地提高了开发效率。

业内知名的代码生成器有很多,如:STM32CubeMX生成STM32基础库代码、project_generator生成器生成基础工程、protoc生成protobuf协数据格式代码等。

project_generator相关文章:嵌入式项目生成器,了解一下!

protoc工具会解析xxx.proto文件并生成对应的xxx.pb-.c及xxx.pb-c.h:

protobuf相关:一种更轻量的数据格式——protobuf、干货 | protobuf-c之嵌入式平台使用

AI时代,各种AI辅助编程工具也很多,可以用来快速生成一些工具代码。比如:

  • MarsCode:豆包旗下的智能编程助手,提供智能代码补全等核心能力。支持主流编程语言及IDE,能在编码过程中提供单行或整个函数的建议,同时支持代码解释、单测生成、问题修复、技术问答等辅助功能。
  • GitHub Copilot:一款广为人知的代码编程辅助AI工具,由GitHub和OpenAI合作开发。基于OpenAI Codex模型,能够在各种编程环境中提供代码建议,支持多种编程语言。通过学习大量代码库,帮助开发者提高编码效率和质量。
  • CodeGeeX:CodeGeeX是一款基于智谱AI大模型GLM的强大智能编程助手,提供代码生成/完成、注释生成、代码翻译和基于人工智能的聊天等功能。支持多种编程语言,如Python、C++、Java、JavaScript等,并且适配多种主流IDE。

但是,在我们实际项目中,有些相似性比较高、重复性比较高、更新迭代过程需要频繁新增的一些需要复制、粘贴、然后再进行修改的代码。

以上这些代码生成的手段对我们的帮助可能比较有限。这时候我们可以自己根据自己的项目代码,定制化地写个代码自动生成的小软件/小脚本。

写代码生成工具之前,需要自己评估一下,这个事情是否值得做。因为如果你花了很大力气写了一个辅助工具,结果使用的频次很少,也不是很有必要。或者在某个文件生成几行代码,这手动适配也花不了多少时间,收益不是很大。

我觉得,需要频繁地修改到若干个文件的多处代码,这才比较有必要去写个代码生成工具。比如协议数据新增方面,就是个更新迭代可能会频繁修改的东西吧?

只要一条数据通了,后续新增是不是都会复制粘贴然后修改?

而且有些项目代码分层会比较细,数据传输链路可能会经过多层代码的多个文件,都需要做对应的修改。这手动适配起来费时费力,还容易搞错,复制粘贴,结果某一处忘记修改了没检查到,跑不通又得来回调试。

废话不多说,如果大家也遇到这种情况,那就自己写个代码生成工具吧,然后之后就可以快乐的摸鱼了。

思路很简单,在哪个位置,生成什么代码。我们可以根据需要自己评估是否有必要开发带图形界面的代码生成器,如果是自己使用,那就无所谓,怎么省时怎么来。

下面看看使用shell脚本来生成的例子。

例子

首先我们需要思考两个点:

"生成怎么样的代码?"

脚本处理的优势就是处理一些重复性的、相似性的事情。所以要想脚本生成代码,就得在写代码时有意识地构造脚本能找到一些共性的代码。

”在哪个位置插入代码?“

这个条件从原有的代码里可能不太好确定,为了脚本能简单点,这个条件我们可以自己构造:以注释的形式在想要插入代码的地方做个标记

脚本去匹配代码里的这些标记,然后插入想要插入的代码即可。

废话不多说,直接看例子:

假如我们有这么一份代码:读取命令行的测试指令并执行对应的测试代码。

test.c:

// 公众号:嵌入式大杂烩
#include 
#include 
#include 
#include 
#include 

#define int8   char
#define uint8  unsigned char
#define uint16 unsigned short int

typedef enum cmd_id_e
{
    CMD_ID_TEST_RAND,
    CMD_ID_TEST_MAX,
}cmd_id_t;

#define TO_CMD_ID_STR(cmd_id) #cmd_id

typedef int (*cmd_func_t)(void);  

typedef struct cmd_s
{

    cmd_id_t cmd_id;
    cmd_func_t cmd_func;
}cmd_t;


static int rand_test_func(void)
{
 printf("--------------------%s start!--------------------\n",__FUNCTION__);
 int a = 0;
 srand((unsigned)time(NULL));
 a = rand() % 10;  
 printf("%d\n", a);
 printf("--------------------%s end!--------------------\n",__FUNCTION__);
 printf("\n");
}

static cmd_t s_cmd_table[CMD_ID_TEST_MAX] = 
{  
    {CMD_ID_TEST_RAND, rand_test_func},  
};  

static void execute_cmd(cmd_id_t cmd_id) 
{  
    if (cmd_id > CMD_ID_TEST_MAX)
    {
        printf("[%s]Invalid param\n", __FUNCTION__);  
        return
    }

    for (int i = 0; i < CMD_ID_TEST_MAX; i++) 
    {  
        if (s_cmd_table[i].cmd_id == cmd_id) 
        {  
            s_cmd_table[i].cmd_func();  
            return;  
        }  
    }  


cmd_id_t menu_select(void)
{
 cmd_id_t cmd_id = CMD_ID_TEST_MAX;
 char str[64] = {0};
 
    printf("=============================================================================\n");
 printf("[%.3d] Test: %s\n", CMD_ID_TEST_RAND, TO_CMD_ID_STR(CMD_ID_TEST_RAND));
    printf("=============================================================================\n");

 do
 {
  printf("Enter your choice: ");
  scanf("%s", str);
  cmd_id = atoi(str);
 }while(cmd_id < 0 || cmd_id > CMD_ID_TEST_MAX);
 
 return cmd_id;
}

int main(void)
{
 cmd_id_t cmd_id = CMD_ID_TEST_MAX;
 
 while(1)  
 {
  cmd_id = menu_select();
        execute_cmd(cmd_id);
        usleep(1000 * 1000);
 }
 
 return 0;
}

这个测试代码,目前只有一个测试函数,之后每次新增测试函数,需要新增如下代码:

  • 命令id枚举,新增枚举值。
  • 新增测试函数函数体。
  • 补充命令表。
  • 补充菜单函数。

按照上面说的思路,我们在这四个地方以注释的形式插入代码插入点标签:

// code_insertion_point__cmd_id
// code_insertion_point__cmd_func
// code_insertion_point__cmd_table
// code_insertion_point__cmd_description

插入的代码?

提取公共部分,补充差异。这个例子中,为了脚本能简单点,我们可以依赖于CMD_ID_TEST_RAND,因为这里的信息可以区分不同的命令,比如新的命令就是CMD_ID_XXX格式的。

其它几个插入点的代码都可依赖这个生成。假如我们的脚本名称为code_generator.sh,我们期望最终的使用方式如:

./code_generator.sh CMD_ID_XXX

shell基本知识:Shell编程必备简明基础知识!

编写shell脚本:code_generator.sh

#!/bin/bash

code_generate__cmd_id()
{
    APPEND_STR=$1
    DST_FILE_PATH=$2
    sed -i "/\/\/ code_insertion_point__cmd_id/i \\$APPEND_STR" $DST_FILE_PATH
}

code_generate__cmd_func()
{
    APPEND_STR=$1
    DST_FILE_PATH=$2
    sed -i "/\/\/ code_insertion_point__cmd_func/i \\$APPEND_STR" $DST_FILE_PATH
}

code_generate__cmd_table()
{
    APPEND_STR=$1
    DST_FILE_PATH=$2
    sed -i "/\/\/ code_insertion_point__cmd_table/i \\$APPEND_STR" $DST_FILE_PATH
}

code_generate__cmd_description()
{
    APPEND_STR=$1
    DST_FILE_PATH=$2
    sed -i "/\/\/ code_insertion_point__cmd_description/i \\$APPEND_STR" $DST_FILE_PATH
}

code_generate()
{
    CMD_ID_STR=$1

    DST_FILE_PATH="./test.c"
    TAB_SPACE="    "

    # code_insertion_point__cmd_id
    echo "CMD_ID_STR = ${CMD_ID_STR}"
    CMD_ID_APPEND_STR="${TAB_SPACE}${CMD_ID_STR},"
    echo "CMD_ID_APPEND_STR = ${CMD_ID_APPEND_STR}"

    code_generate__cmd_id "${CMD_ID_APPEND_STR}" "${DST_FILE_PATH}"

    # code_insertion_point__cmd_table
    CMD_FLAG="${CMD_ID_STR##*_}" 
    echo "CMD_FLAG = ${CMD_FLAG}"
    CMD_FUNC_STR="${CMD_FLAG,,}_test_func"
    echo "CMD_FUNC_STR = ${CMD_FUNC_STR}"

    CMD_TABLE_APPEND_STR="${TAB_SPACE}{${CMD_ID_STR}${CMD_FUNC_STR}},"
    code_generate__cmd_table "${CMD_TABLE_APPEND_STR}" "${DST_FILE_PATH}"

    # code_insertion_point__cmd_description
    CMD_DSCRIPTION_APPEND_STR="${TAB_SPACE}printf(\"[%.3d] Test: %s\\\n\", ${CMD_ID_STR}, TO_CMD_ID_STR(${CMD_ID_STR}));"
    echo "CMD_DSCRIPTION_APPEND_STR = ${CMD_DSCRIPTION_APPEND_STR}"
    code_generate__cmd_description "${CMD_DSCRIPTION_APPEND_STR}" "${DST_FILE_PATH}"

    # code_insertion_point__cmd_func
    CMD_FUNC_STR_PART1="static int ${CMD_FUNC_STR}(void)\n{\n"
    CMD_FUNC_STR_PART2="${TAB_SPACE}printf(\"--------------------%s start!--------------------\\\n\",__FUNCTION__);\n"
    CMD_FUNC_STR_PART3="${TAB_SPACE}\/\/ todo: add your code\n"
    CMD_FUNC_STR_PART4="${TAB_SPACE}printf(\"--------------------%s end!--------------------\\\n\",__FUNCTION__);\n"
    CMD_FUNC_STR_PART5="${TAB_SPACE}printf(\"\\\n\");\n}"
    CMD_FUNC_APPEND_STR=${CMD_FUNC_STR_PART1}${CMD_FUNC_STR_PART2}${CMD_FUNC_STR_PART3}${CMD_FUNC_STR_PART4}${CMD_FUNC_STR_PART5}
    echo "CMD_FUNC_APPEND_STR = ${CMD_FUNC_APPEND_STR}"
    code_generate__cmd_func "${CMD_FUNC_APPEND_STR}" "${DST_FILE_PATH}"
}

code_generate $1

测试:

生成的代码:

一些代码细节,代码生成脚本不能事先知道,生成时可以统一备注:

// todo: add your code

方便后续填充代码。

编译、执行:

可见,我们写的代码生成脚本帮我们生成的代码,执行也是符合预期的。

以上只是提供了一个简单的代码生成脚本的思路及很理想情况下的demo,实现方式可能有多种,大家可以自己思考挖掘。

并且实际项目中的情况会复杂得多。

特别是处理代码格式,如果需要脚本做,脚本就会比较复杂了,比如:

  • 代码中间的不定空格数?

  • 代码增加注释?

  • ......

需要下很大的功夫。有时候需要做一些取舍,比如,假如上面的命令表代码为:

此时中间的空格长度根据命令的长度不一而是一个变化的值,脚本要去处理这个事情就需要做不少逻辑,这会增加一些脚本的开发时间。

最后,再说一下:

写代码生成工具之前,需要自己评估一下,这个事情是否值得做。因为如果你花了很大力气写了一个辅助工具,结果使用的频次很少,也不是很有必要。或者在某个文件生成几行代码,这手动适配也花不了多少时间,收益不是很大。

本文的c_test及脚本,可在【嵌入式大杂烩】公众号回复关键词:代码生成脚本 ,即可获取。


猜你喜欢:

WiFi6+蓝牙+星闪,三合一开发板,真香!

Github上热门 C 语言项目汇总!

嵌入式,可测试性软件设计!

一些低功耗软件设计的要点!

嵌入式 C 保护结构体的方式

实用 | 10分钟教你通过网页点灯

谈谈嵌入式软件的兼容性!

点击阅读原文,查看更多分享。

嵌入式大杂烩 专注于嵌入式技术,包括但不限于C/C++、嵌入式、物联网、Linux等编程学习笔记,同时,内包含大量的学习资源。欢迎关注,一同交流学习,共同进步!
评论
  • 振动样品磁强计是一种用于测量材料磁性的精密仪器,广泛应用于科研、工业检测等领域。然而,其测量准确度会受到多种因素的影响,下面我们将逐一分析这些因素。一、温度因素温度是影响振动样品磁强计测量准确度的重要因素之一。随着温度的变化,材料的磁性也会发生变化,从而影响测量结果的准确性。因此,在进行磁性测量时,应确保恒温环境,以减少温度波动对测量结果的影响。二、样品制备样品的制备过程同样会影响振动样品磁强计的测量准确度。样品的形状、尺寸和表面处理等因素都会对测量结果产生影响。为了确保测量准确度,应严格按照规
    锦正茂科技 2025-02-28 14:05 134浏览
  •           近日受某专业机构邀请,参加了官方举办的《广东省科技创新条例》宣讲会。在与会之前,作为一名技术工作者一直认为技术的法例都是保密和侵权方面的,而潜意识中感觉法律有束缚创新工作的进行可能。通过一个上午学习新法,对广东省的科技创新有了新的认识。广东是改革的前沿阵地,是科技创新的沃土,企业是创新的主要个体。《广东省科技创新条例》是广东省为促进科技创新、推动高质量发展而制定的地方性法规,主要内容包括: 总则:明确立法目
    广州铁金刚 2025-02-28 10:14 103浏览
  • RGB灯光无法同步?细致的动态光效设定反而成为产品客诉来源!随着科技的进步和消费者需求变化,电脑接口设备单一功能性已无法满足市场需求,因此在产品上增加「动态光效」的形式便应运而生,藉此吸引消费者目光。这种RGB灯光效果,不仅能增强电脑周边产品的视觉吸引力,还能为用户提供个性化的体验,展现独特自我风格。如今,笔记本电脑、键盘、鼠标、鼠标垫、耳机、显示器等多种电脑接口设备多数已配备动态光效。这些设备的灯光效果会随着音乐节奏、游戏情节或使用者的设置而变化。想象一个画面,当一名游戏玩家,按下电源开关,整
    百佳泰测试实验室 2025-02-27 14:15 137浏览
  • 1,微软下载免费Visual Studio Code2,安装C/C++插件,如果无法直接点击下载, 可以选择手动install from VSIX:ms-vscode.cpptools-1.23.6@win32-x64.vsix3,安装C/C++编译器MniGW (MinGW在 Windows 环境下提供类似于 Unix/Linux 环境下的开发工具,使开发者能够轻松地在 Windows 上编写和编译 C、C++ 等程序.)4,C/C++插件扩展设置中添加Include Path 5,
    黎查 2025-02-28 14:39 141浏览
  • 在物联网领域中,无线射频技术作为设备间通信的核心手段,已深度渗透工业自动化、智慧城市及智能家居等多元场景。然而,随着物联网设备接入规模的不断扩大,如何降低运维成本,提升通信数据的传输速度和响应时间,实现更广泛、更稳定的覆盖已成为当前亟待解决的系统性难题。SoC无线收发模块-RFM25A12在此背景下,华普微创新推出了一款高性能、远距离与高性价比的Sub-GHz无线SoC收发模块RFM25A12,旨在提升射频性能以满足行业中日益增长与复杂的设备互联需求。值得一提的是,RFM25A12还支持Wi-S
    华普微HOPERF 2025-02-28 09:06 143浏览
  • 美国加州CEC能效跟DOE能效有什么区别?CEC/DOE是什么关系?美国加州CEC能效跟DOE能效有什么区别?CEC/DOE是什么关系?‌美国加州CEC能效认证与美国DOE能效认证在多个方面存在显著差异‌。认证范围和适用地区‌CEC能效认证‌:仅适用于在加利福尼亚州销售的电器产品。CEC认证的范围包括制冷设备、房间空调、中央空调、便携式空调、加热器、热水器、游泳池加热器、卫浴配件、光源、应急灯具、交通信号模块、灯具、洗碗机、洗衣机、干衣机、烹饪器具、电机和压缩机、变压器、外置电源、消费类电子设备
    张工nx808593 2025-02-27 18:04 120浏览
  •         近日,广电计量在聚焦离子束(FIB)领域编写的专业著作《聚焦离子束:失效分析》正式出版,填补了国内聚焦离子束领域实践性专业书籍的空白,为该领域的技术发展与知识传播提供了重要助力。         随着芯片技术不断发展,芯片的集成度越来越高,结构也日益复杂。这使得传统的失效分析方法面临巨大挑战。FIB技术的出现,为芯片失效分析带来了新的解决方案。它能够在纳米尺度上对芯片进行精确加工和分析。当芯
    广电计量 2025-02-28 09:15 116浏览
  • 更多生命体征指标风靡的背后都只有一个原因:更多人将健康排在人生第一顺位!“AGEs,也就是晚期糖基化终末产物,英文名Advanced Glycation End-products,是存在于我们体内的一种代谢产物” 艾迈斯欧司朗亚太区健康监测高级市场经理王亚琴说道,“相信业内的朋友都会有关注,最近该指标的热度很高,它可以用来评估人的生活方式是否健康。”据悉,AGEs是可穿戴健康监测领域的一个“萌新”指标,近来备受关注。如果站在学术角度来理解它,那么AGEs是在非酶促条件下,蛋白质、氨基酸
    艾迈斯欧司朗 2025-02-27 14:50 400浏览
  • 在2024年的科技征程中,具身智能的发展已成为全球关注的焦点。从实验室到现实应用,这一领域正以前所未有的速度推进,改写着人类与机器的互动边界。这一年,我们见证了具身智能技术的突破与变革,它不仅落地各行各业,带来新的机遇,更在深刻影响着我们的生活方式和思维方式。随着相关技术的飞速发展,具身智能不再仅仅是一个技术概念,更像是一把神奇的钥匙。身后的众多行业,无论愿意与否,都像是被卷入一场伟大变革浪潮中的船只,注定要被这股汹涌的力量重塑航向。01为什么是具身智能?为什么在中国?最近,中国具身智能行业的进
    艾迈斯欧司朗 2025-02-28 15:45 221浏览
  • 一、VSM的基本原理震动样品磁强计(Vibrating Sample Magnetometer,简称VSM)是一种灵敏且高效的磁性测量仪器。其基本工作原理是利用震动样品在探测线圈中引起的变化磁场来产生感应电压,这个感应电压与样品的磁矩成正比。因此,通过测量这个感应电压,我们就能够精确地确定样品的磁矩。在VSM中,被测量的样品通常被固定在一个震动头上,并以一定的频率和振幅震动。这种震动在探测线圈中引起了变化的磁通量,从而产生了一个交流电信号。这个信号的幅度和样品的磁矩有着直接的关系。因此,通过仔细
    锦正茂科技 2025-02-28 13:30 100浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦