我硬生生地把C代碼塞進了Python和Ruby!
▍很懶很操心
有一次,我在項目開發(fā)中想監(jiān)控某段空間數(shù)據(jù)的大小,即這段空間在MCU中非常有限,希望每個版本在集成軟件的時候都想獲取其使用了多少空間,防止某些愣頭青不珍惜內(nèi)存,亂塞東西。而這段空間,我定義了一個神一樣的結(jié)構(gòu)體映射到這個空間,即其他開發(fā)人員只要在結(jié)構(gòu)體增加元素即可(我使用洪荒之力將宏定義發(fā)揮到淋漓盡致才做到的,至于怎么實現(xiàn)的細節(jié)就不在這個文章討論了,后續(xù)再寫篇文章裝裝X)。
計算這個結(jié)構(gòu)體空間,要求:
在軟件集成階段就獲得這個結(jié)構(gòu)體大小,而不是MCU運行的時候;(自動執(zhí)行)
計算這個結(jié)構(gòu)體大小,不要增加刪除原程序代碼;(悄無聲息)
方便集成工程師使用,不增加使用難度;(簡單易用)
不因為人操作的原因,而導(dǎo)致計算結(jié)果不準確,即自動化執(zhí)行輸出結(jié)果。(無人為干擾)
總之,做這件事的目的是:每次集成的時候自動輸出結(jié)果(很懶),也不行交給其他小伙伴去手工計算,或者更改原來的結(jié)構(gòu)代碼去計算這個空間,怕其亂來搞壞了原來的代碼(很操心)。
再總之:能讓電腦干的活,干嘛要讓人去干!
于是,我就突發(fā)奇想,寫個腳本唄。
那么啥子腳本可以計算C語言的結(jié)構(gòu)體大???
身為優(yōu)秀的“嵌入式”工程師,對這種將C語言“嵌入”到腳本中的事情肯定是要研究一番的。
Note:為了方便描述,我將具體項目細節(jié)和裝X的過程隱去,并將這個神一樣的結(jié)構(gòu)體簡化為:
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
溫馨提示,使用以下方法,請?zhí)崆鞍惭b:
-
Python -
GCC(例如Windows上的MinGW) -
用pip安裝pyembedc( pip install pyembedc)
注意:Python的版本位數(shù)要跟GCC的對應(yīng),例如都選32位的。
方法1:Python調(diào)用exe方式
步驟:
在一個臨時C文件里,編寫臨時
main函數(shù);用GCC構(gòu)建編譯,生成exe;
通過腳本(此處選擇Python)調(diào)用運行輸出結(jié)果;
刪除臨時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調(diào)用lib方式
總覺得用Python調(diào)用exe的方式有點low,再進一步,那就調(diào)用lib吧,例如調(diào)用.so內(nèi)的函數(shù)或者變量。
步驟跟方法1類似:
在一個臨時C文件里,編寫臨時
main函數(shù);用GCC構(gòu)建編譯,生成lib(
.so);通過Python調(diào)用運行輸出結(jié)果;
刪除臨時C文件。
Python調(diào)用lib庫有個好處,可以調(diào)用其里面的具體函數(shù)等。
// 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文件,會出現(xiàn)問題,因為沒有辦法unload這個.so。另外,關(guān)于這個話題,請參考:https://stackoverflow.com/questions/359498/how-can-i-unload-a-dll-using-ctypes-in-python
方法3:Python調(diào)用C源碼方式
調(diào)用exe和調(diào)用lib,都覺得很low,怎么辦,能不能直接插入C源碼呢?
那就用pyembedc吧,Python可以訪問C的變量,C也可以訪問Python的變量,是不是炫酷吊炸天。
例1,訪問C內(nèi)部變量
# 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內(nèi)部函數(shù)
# 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())
實際上,以上的操作,也是這個庫偷偷地調(diào)用了GCC來編譯C代碼的(只是不是顯式讓你看到而已),你不安裝對應(yīng)版本的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.
更多內(nèi)容,請見:https://github.com/ftrias/pyembedc
▍將C/C++代碼嵌入Ruby
生活詩意,我用Ruby
能把C代碼塞進Python,當(dāng)然也能塞進Ruby。對于在Ruby上插入C源碼,給大家安利一個庫RubyInline。
Inline允許您在Ruby代碼中編寫外部代碼。它會自動確定相關(guān)代碼是否已更改,并僅在必要時進行構(gòu)建。然后將擴展自動加載到定義擴展的類/模塊中。您甚至可以編寫額外的構(gòu)建器,使您可以用任何語言編寫Inline代碼。使用
Inline :: C作為Module,并在Module#inline中查找所需的API。
RubyInline還有以下Features:
快速,輕松地內(nèi)嵌在ruby腳本中的C或C ++代碼。
可擴展以與其他語言一起使用。
紅寶石和C基本類型之間的自動轉(zhuǎn)換
* char,unsigned,unsigned int,char *,int,long,unsigned longinline_c_raw用于自動轉(zhuǎn)換不充分時。
僅當(dāng)內(nèi)聯(lián)代碼已更改時才重新編譯。
假裝是安全的。
僅需要標準的ruby庫,無需下載其他內(nèi)容。
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上搞起來了。應(yīng)了網(wǎng)上某句話:想學(xué)Ruby,就用Linux吧,別在Windows上瞎折騰。話說回來,這個嵌入式C源碼的用法,個人感覺Ruby的比Python的簡潔直觀。該用哪種方法,看實際需要吧。更多內(nèi)容,詳見:https://github.com/seattlerb/rubyinline
▍總結(jié)
想將C/C++塞進腳本,需要借助GCC,它才是讓你裝逼讓你飛的前提條件。
本文授權(quán)轉(zhuǎn)載自公眾號“嵌入式軟件實戰(zhàn)派”,作者實戰(zhàn)派師姐
-END-
推薦閱讀
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!






