
▍很懒很操心 有一次,我在项目开发中想监控某段空间数据的大小,即这段空间在MCU中非常有限,希望每个版本在集成软件的时候都想获取其使用了多少空间,防止某些愣头青不珍惜内存,乱塞东西。而这段空间,我定义了一个神一样的结构体映射到这个空间,即其他开发人员只要在结构体增加元素即可(我使用洪荒之力将宏定义发挥到淋漓尽致才做到的,至于怎么实现的细节就不在这个文章讨论了,后续再写篇文章装装X)。 计算这个结构体空间,要求:
总之,做这件事的目的是:每次集成的时候自动输出结果(很懒),也不行交给其他小伙伴去手工计算,或者更改原来的结构代码去计算这个空间,怕其乱来搞坏了原来的代码(很操心)。 再总之:能让电脑干的活,干嘛要让人去干! 于是,我就突发奇想,写个脚本呗。 那么啥子脚本可以计算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的版本位数要跟GCC的对应,例如都选32位的。 方法1:Python调用exe方式 步骤:
接上代码看看 // struct.htypedef 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类似:
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。另外,关于这个话题,请参考:http://stackoverflow.com/questi ... ng-ctypes-in-python 方法3:Python调用C源码方式 调用exe和调用lib,都觉得很low,怎么办,能不能直接插入C源码呢? 那就用pyembedc吧,Python可以访问C的变量,C也可以访问Python的变量,是不是炫酷吊炸天。 例1,访问C内部变量 # callstruct_inline1.pyfrom 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.pyfrom 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
pyembedc.embed_c(string) -> cdllpyembedc.embed_c_precompile(string) -> cdll
▍将C/C++代码嵌入Ruby
能把C代码塞进Python,当然也能塞进Ruby。对于在Ruby上插入C源码,给大家安利一个库RubyInline。
RubyInline还有以下Features:
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的简洁直观。该用哪种方法,看实际需要吧。更多内容,详见:http://github.com/seattlerb/rubyinline ▍总结 想将C/C++塞进脚本,需要借助GCC,它才是让你装逼让你飞的前提条件。 |