当前位置:首页 > 公众号精选 > 嵌入式微处理器
[导读]▍很懒很操心 有一次,我在项目开发中想监控某段空间数据的大小,即这段空间在MCU中非常有限,希望每个版本在集成软件的时候都想获取其使用了多少空间,防止某些愣头青不珍惜内存,乱塞东西。而这段空间,我定义了一个神一样的结构体映射到这个空间,即其他开


很懒很操

有一次,我在项目开发中想监控某段空间数据的大小,即这段空间在MCU中非常有限,希望每个版本在集成软件的时候都想获取其使用了多少空间,防止某些愣头青不珍惜内存,乱塞东西。而这段空间,我定义了一个神一样的结构体映射到这个空间,即其他开发人员只要在结构体增加元素即可(我使用洪荒之力将宏定义发挥到淋漓尽致才做到的,至于怎么实现的细节就不在这个文章讨论了,后续再写篇文章装装X)。

计算这个结构体空间,要求:

  1. 在软件集成阶段就获得这个结构体大小,而不是MCU运行的时候;(自动执行)

  2. 计算这个结构体大小,不要增加删除原程序代码;(悄无声息)

  3. 方便集成工程师使用,不增加使用难度;(简单易用)

  4. 不因为人操作的原因,而导致计算结果不准确,即自动化执行输出结果。(无人为干扰)

总之,做这件事的目的是:每次集成的时候自动输出结果(很懒),也不行交给其他小伙伴去手工计算,或者更改原来的结构代码去计算这个空间,怕其乱来搞坏了原来的代码(很操心)。

再总之:能让电脑干的活,干嘛要让人去干!

于是,我就突发奇想,写个脚本呗。

那么啥子脚本可以计算C语言的结构体大小?

身为优秀的嵌入式”工程师,对这种将C语言“嵌入”到脚本中的事情肯定是要研究一番的。

Note:为了方便描述,我将具体项目细节和装X的过程隐去,并将这个神一样的结构体简化为:

 typedef struct
 {
     unsigned char item_a[2];
     unsigned char item_b[3];
     unsigned char item_c[5];
     unsigned char item_d[8];
     unsigned char item_e[33];
     unsigned char item_fxxk[1];
     unsigned char item_fxxxk[2];
     unsigned char item_fxxk_any[55];
     // add items here...
 }typeStructData;

将C/C++代码嵌入Python

人生苦短,我用Python

温馨提示,使用以下方法,请提前安装:

  1. Python
  2. GCC(例如Windows上的MinGW)
  3. 用pip安装pyembedc(pip install pyembedc

注意:Python的版本位数要跟GCC的对应,例如都选32位的。

方法1:Python调用exe方式

步骤:

  1. 在一个临时C文件里,编写临时main函数;

  2. 用GCC构建编译,生成exe;

  3. 通过脚本(此处选择Python)调用运行输出结果;

  4. 删除临时C文件和exe文件。

接上代码看看

 // struct.h
 
 typedef struct
 {
     unsigned char item_a[2];
     unsigned char item_b[3];
     unsigned char item_c[5];
     unsigned char item_d[8];
     unsigned char item_e[33];
     unsigned char item_fxxk[1];
     unsigned char item_fxxxk[2];
     unsigned char item_fxxk_any[55];
 }typeStructData;
 import os
 
 c_main = r'''
 #include <stdio.h>
 #include "struct.h"
 int main(void)
 {
    printf("size: %d\n", sizeof(typeStructData));
    return 0;
 }
 '''
 def cal_struct_size():
     f_c_main = 'xxxxsizeofstructxxxx.c'
     f_run = 'xxxxsizeofstructxxxx.exe'
     with open(f_c_main, 'w') as f: f.write(c_main)
     gcc_compile = "gcc %s -o %s"%(f_c_main, f_run)
 
     os.system(gcc_compile)
     os.system(f_run)
     if os.path.exists(f_c_main): os.remove(f_c_main)
     if os.path.exists(f_run): os.remove(f_run)
 
 if __name__ == "__main__":
     cal_struct_size()

方法2:Python调用lib方式

总觉得用Python调用exe的方式有点low,再进一步,那就调用lib吧,例如调用.so内的函数或者变量。

步骤跟方法1类似:

  1. 在一个临时C文件里,编写临时main函数;

  2. 用GCC构建编译,生成lib(.so);

  3. 通过Python调用运行输出结果;

  4. 删除临时C文件。

Python调用lib库有个好处,可以调用其里面的具体函数等

 // struct.c
 
 #include <stdio.h>
 
 typedef struct
 {
     unsigned char item_a[2];
     unsigned char item_b[3];
     unsigned char item_c[5];
     unsigned char item_d[8];
     unsigned char item_e[33];
     unsigned char item_fxxk[1];
     unsigned char item_fxxxk[2];
     unsigned char item_fxxk_any[55];
 }typeStructData;
 
 int get_struct_size(void)
 {
     return sizeof(typeStructData);
 }
 import ctypes
 import os
 
 os.system('gcc -shared -Wl,-soname,struct -o struct.so -fPIC struct.c')
 
 struct_size = ctypes.cdll.LoadLibrary('./struct.so')
 
 def cal_struct_size():
     s = struct_size.get_struct_size()
 
     print("size: %d"%s)
 
 if __name__ == "__main__":
     cal_struct_size()
     # if os.path.exists('struct.so'): os.remove('struct.so')

貌似有个小问题,如果想在脚本里面删除这个.so文件,会出现问题,因为没有办法unload这个.so。另外,关于这个话题,请参考:https://stackoverflow.com/questions/359498/how-can-i-unload-a-dll-using-ctypes-in-python

方法3:Python调用C源码方式

调用exe和调用lib,都觉得很low,怎么办,能不能直接插入C源码呢?

那就用pyembedc吧,Python可以访问C的变量,C也可以访问Python的变量,是不是炫酷吊炸天。

例1,访问C内部变量

 # callstruct_inline1.py
 
 from pyembedc import C
 
 struct_str = r'''
    typedef struct
    {
        unsigned char item_a[2];
        unsigned char item_b[3];
        unsigned char item_c[5];
        unsigned char item_d[8];
        unsigned char item_e[33];
        unsigned char item_fxxk[1];
        unsigned char item_fxxxk[2];
        unsigned char item_fxxk_any[55];
    }typeStructData;
    struct_size = sizeof(typeStructData);
 
 '''
 struct_size = 0
 struct_f = C(struct_str)
 print('size: %d\n'%struct_size)

例2,访问C内部函数

 # callstruct_inline2.py
 
 from pyembedc import embed_c
 
 struct_str2 = r'''
    typedef struct
    {
        unsigned char item_a[2];
        unsigned char item_b[3];
        unsigned char item_c[5];
        unsigned char item_d[8];
        unsigned char item_e[33];
        unsigned char item_fxxk[1];
        unsigned char item_fxxxk[2];
        unsigned char item_fxxk_any[55];
    }typeStructData;
     
    int get_struct_size(void)
    {
        return sizeof(typeStructData);
    }
 '''
 struct_c = embed_c(struct_str2)
 print('size: %d\n'%struct_c.get_struct_size())

实际上,以上的操作,也是这个库偷偷地调用了GCC来编译C代码的(只是不是显式让你看到而已),你不安装对应版本的GCC也是做不到的。

顺便说是,这个pyembedc有几个方式:

Functionspyembedc.C(string) -> intpyembedc.inline_c(string) -> intpyembedc.inline_c_precompile(string) -> int

These functions will compile string containing the C/C++ code or directives (see below) and then link dynamically and run the code.

string is C/C++ code that can be used within a function. It can contain any valid C/C++ expression that your compiler will support.

The C function will automatically provide references to all local Python variables for use in your code to read or write as if they were basic types or arrays.

The inline_c and inline_c_precompile fucntion will not provide references to local Python variables and thus is faster and consumes less memory.

pyembedc.embed_c(string) -> cdllpyembedc.embed_c_precompile(string) -> cdll

These functions are used to compile code but not execute immediately. They return a CDLL object (see the CDLL python module) that can be executed later.

更多内容,请见:https://github.com/ftrias/pyembedc


将C/C++代码嵌入Ruby

生活诗意,我用Ruby

能把C代码塞进Python,当然也能塞进Ruby。对于在Ruby上插入C源码,给大家安利一个库RubyInline。

Inline允许您在Ruby代码中编写外部代码。它会自动确定相关代码是否已更改,并仅在必要时进行构建。然后将扩展自动加载到定义扩展的类/模块中。您甚至可以编写额外的构建器,使您可以用任何语言编写Inline代码。使用Inline :: C作为Module,并在Module#inline中查找所需的API。

RubyInline还有以下Features:

  • 快速,轻松地内嵌在ruby脚本中的C或C ++代码。

  • 可扩展以与其他语言一起使用。

  • 红宝石和C基本类型之间的自动转换* char,unsigned,unsigned int,char *,int,long,unsigned long

  • inline_c_raw用于自动转换不充分时。

  • 仅当内联代码已更改时才重新编译。

  • 假装是安全的。

  • 仅需要标准的ruby库,无需下载其他内容。

 require "inline"
 class MyTest
     inline do |builder|
         builder.c "
            long factorial(int max) {
                int i=max, result=1;
                while (i >= 2) { result *= i--; }
                return result;
            }"
         end
 end
 t = MyTest.new()
 factorial_5 = t.factorial(5)
 require 'inline'
 class MyTest
     inline(:C) do |builder|
         builder.include '<iostream>'
         builder.add_compile_flags '-x c++', '-lstdc++'
         builder.c '
            void hello(int i) {
                while (i-- > 0) {
                    std::cout << "hello" << std::endl;
                }
            }'
     end
 end
 t = MyTest.new()
 t.hello(3)
 

是不是很好玩,是不是很想试试?但是我告诉你,我在Windows上没搞成功,但在Linux上搞起来了。应了网上某句话:想学Ruby,就用Linux吧,别在Windows上瞎折腾。话说回来,这个嵌入式C源码的用法,个人感觉Ruby的比Python的简洁直观。该用哪种方法,看实际需要吧。更多内容,详见:https://github.com/seattlerb/rubyinline


总结

想将C/C++塞进脚本,需要借助GCC,它才是让你装逼让你飞的前提条件。


本文授权转载自公众号“嵌入式软件实战派”,作者实战派师姐


-END-




推荐阅读



【01】嵌入式系统中常用的IIC与SPI,这两种通讯方式该怎么选?
【02】嵌入式必看:Linux内存管理那些事儿
【03】嵌入式项目是如何评估系统所需的RAM和ROM用量的?
【04】嵌入式和单片机不一样?那它们的区别在哪?
【05】嵌入式开发碰到无法解决的问题?编程的凹凸性有妙用!(附C代码


免责声明:整理文章为传播相关技术,版权归原作者所有,如有侵权,请联系删除

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

嵌入式ARM

扫描二维码,关注更多精彩内容

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

如何开始编写一个简单的单片机程序呢?接下来就来介绍一下步骤和方法以便更快更好的编写出来单片机程序。

关键字: C代码 编程序

关注、星标公众号,直达精彩内容文章来源:segmentfault作者:Ethson【导读】:树是数据结构中的重中之重,尤其以各类二叉树为学习的难点。在面试环节中,二叉树也是必考的模块。本文主要讲二叉树操作的相关知识,梳理...

关键字: C代码 BSP ORDER WHILE

【导读】:树是数据结构中的重中之重,尤其以各类二叉树为学习的难点。在面试环节中,二叉树也是必考的模块。本文主要讲二叉树操作的相关知识,梳理面试常考的内容。请大家跟随小编一起来复习吧。本文针对面试中常见的二叉树操作做个总结...

关键字: C代码 ORDER WHILE RETURN

关注「Linux大陆」,一起进步!继 300来行代码带你实现一个能跑的最小Linux文件系统 之后,我们来看看如何60行C代码实现一个shell!在实现它之前,先看看这样做的意义。美是有目共睹的。Unix之美,稍微体会,...

关键字: shell C代码

来源:公众号【编程珠玑】作者:守望先生前言如何在C代码中调用写好的C接口?你可能会奇怪,C不是兼容C吗?直接调用不就可以了?这里我们先按下不表,先看看C如何调用C代码接口。C如何调用C接口为什么会有这样的情况呢?想象一下...

关键字: C代码

今天跟大家分享三种表驱动设计的方法,都非常的精妙,值得收藏和细品。

关键字: 表驱动 静态结构体 C代码

为了优化钻井流程并降低作业成本,Baker Hughes的动力学与遥测(Dynamics & Telemetry)小组开发了一个序列预测算法,用于在钻井作业期间快速可靠的解码井下数据。这个已集成

关键字: C代码 编码 自动代码生成 马尔可夫链

  本文讲解的是飞思卡尔软件开发C语言编码规范。来自于痞子衡嵌入式公众号,下面是编码规范原文: 1.引言   制定此编码风格指导手册的目的是为了使按此规范编写出的C/C++代码极易被阅读和理解。 2.与其他编码风格对比...

关键字: 软件开发 MCU 半导体 C代码

1. 不能清晰理解“==”运算以及优先级和结核性的含义在判断i,j,k三个变量是否相等时往往会轻率地写出“i == j == k ”这样的表达式并误以为三个变量

关键字: C语言 RSQUO IP C代码

用HI-TECH C写的使用PIC12C508读写93LC46范例程式/************************************************************ * Processe...

关键字: C代码 读写 PIC HI-TECH
关闭
关闭