百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

分享一个嵌入式代码生成器编写思路!

liebian365 2025-02-26 12:41 1 浏览 0 评论

大家好,我是杂烩君。

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

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

业内知名的代码生成器有很多,如: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,实现方式可能有多种,大家可以自己思考挖掘。

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

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

  • 代码中间的不定空格数?
  • 代码增加注释?
  • ......

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

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

最后,再说一下:

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

相关推荐

20 个 2020 年软件开发趋势预测

企业上云已成不可逆的趋势,全面云计算时代宣告来临,微服务已成软件架构主流,Kubernetes将会变得更酷,2020年还有哪些技术趋势值得观察?一起来看!1.基础设施:条条道路通云端对于云厂商来...

目录发布!安徽这些紧缺人才急需

《安徽省5G产业急需紧缺人才目录(2020-2025)》(以下简称目录)近日正式发布。本次调研调查了216家代表企业、6家头部企业,获取了426份有效问卷,分析安徽省5G产业紧缺人才需求现状,其中产品...

AI树莓派——构建树莓派大脑(NCNN环境搭建)

前言镜像已经做好了,传到百度网盘中了(请大家及时保存,不定期删除!)...

把远程进程通讯grpc引入到Spring boot maven项目中

1、参考链接:gRPC官网:https://grpc.io/HTTP2:https://http2.github.io/...

面向数据的架构

在软件架构中,有一种模式虽鲜为人知的,但值得引起更多的关注。面向数据的架构(Data-OrientedArchitecture)由RajiveJoshi在RTI的2007年白皮书中首次提出,...

Go语言11岁了,网友:他喵的,终于确定出「泛型」了

金磊发自凹非寺量子位报道|公众号QbitAI比Python更快,比Java更简洁,还有C++没有的GC...

深度剖析数据库国产化迁移之路

作者|吴夏,腾讯云TDSQL高级工程师责编|唐小引头图|CSDN下载自东方IC出品|CSDN(ID:CSDNnews)随着国家有关部门近年来陆续出台相关政策指导文件,推动探索安...

一文掌握物体检测库TensorFlow 2.x Object Detection安装

...

团队协作-代码格式化工具clang-format

环境:clang-format:10.0.0前言统一的代码规范对于整个团队来说十分重要,通过git/svn在提交前进行统一的ClangFormat格式化,可以有效避免由于人工操作带来的代码格式问题。...

嵌入式大杂烩周记 第 9 期:nanopb

大家好,我是杂烩君。...

开源鸿蒙 OpenHarmony 3.1 Beta 版本发布:系统基础能力增强

IT之家1月2日消息,OpenAtom社区已于12月31日发布了OpenHarmony-v3.1-Beta版本。版本概述当前版本在OpenHarmony3.0LTS的基础...

零基础物联网开发,踩坑无数,得到这份宝典 | 原力计划

作者|Haor.L责编|王晓曼出品|CSDN博客笔者最近参加了校内的一场物联网开发竞赛,从零开始,踩坑无数,感觉很多时候事情都不像预料的一样发展,离开了美好的IDE,太多事情要在板子上一步...

gRPC:Google开源的基于HTTP/2和ProtoBuf的通用RPC框架

gRPC是一个高性能、通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf(ProtocolBuffers)序列化协议开发,且支持众多开发...

搜狗开源srpc:自研高性能通用RPC框架

今年7月底,搜狗公司开源了内部的工业级C++服务器引擎Workflow,一路收获业内许多认可和关注。9月15日,作为Workflow最重要的生态项目——srpc,一个基于其打造的轻量级RPC框架,也在...

WebSocket与Protobuf在现代网络通信中的应用实践

在现代网络通信中,WebSocket和Protobuf已成为构建高效、跨平台通信系统的关键技术。本文将详细介绍如何使用这两种技术来实现一个稳定且高效的网络通信系统。...

取消回复欢迎 发表评论: