我硬生生地把C代码塞进了Python和Ruby!
扫描二维码
随时随地手机看文章
▍很懒很操心
有一次,我在项目开发中想监控某段空间数据的大小,即这段空间在MCU中非常有限,希望每个版本在集成软件的时候都想获取其使用了多少空间,防止某些愣头青不珍惜内存,乱塞东西。而这段空间,我定义了一个神一样的结构体映射到这个空间,即其他开发人员只要在结构体增加元素即可(我使用洪荒之力将宏定义发挥到淋漓尽致才做到的,至于怎么实现的细节就不在这个文章讨论了,后续再写篇文章装装X)。
计算这个结构体空间,要求:
在软件集成阶段就获得这个结构体大小,而不是MCU运行的时候;(自动执行)
计算这个结构体大小,不要增加删除原程序代码;(悄无声息)
方便集成工程师使用,不增加使用难度;(简单易用)
不因为人操作的原因,而导致计算结果不准确,即自动化执行输出结果。(无人为干扰)
总之,做这件事的目的是:每次集成的时候自动输出结果(很懒),也不行交给其他小伙伴去手工计算,或者更改原来的结构代码去计算这个空间,怕其乱来搞坏了原来的代码(很操心)。
再总之:能让电脑干的活,干嘛要让人去干!
于是,我就突发奇想,写个脚本呗。
那么啥子脚本可以计算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
温馨提示,使用以下方法,请提前安装:
-
Python -
GCC(例如Windows上的MinGW) -
用pip安装pyembedc( pip install pyembedc)
注意:Python的版本位数要跟GCC的对应,例如都选32位的。
方法1:Python调用exe方式
步骤:
在一个临时C文件里,编写临时
main函数;用GCC构建编译,生成exe;
通过脚本(此处选择Python)调用运行输出结果;
删除临时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类似:
在一个临时C文件里,编写临时
main函数;用GCC构建编译,生成lib(
.so);通过Python调用运行输出结果;
删除临时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
stringcontaining the C/C++ code or directives (see below) and then link dynamically and run the code.
stringis C/C++ code that can be used within a function. It can contain any valid C/C++ expression that your compiler will support.The
Cfunction 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_candinline_c_precompilefucntion 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 longinline_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-
推荐阅读
免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!






