你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

将C/C++代码嵌入Python

[复制链接]
gaosmile 发布时间:2020-6-28 18:58
▍很懒很操心
有一次,我在项目开发中想监控某段空间数据的大小,即这段空间在MCU中非常有限,希望每个版本在集成软件的时候都想获取其使用了多少空间,防止某些愣头青不珍惜内存,乱塞东西。而这段空间,我定义了一个神一样的结构体映射到这个空间,即其他开发人员只要在结构体增加元素即可(我使用洪荒之力将宏定义发挥到淋漓尽致才做到的,至于怎么实现的细节就不在这个文章讨论了,后续再写篇文章装装X)。. s0 e; N& n: u' u
计算这个结构体空间,要求:
  • 在软件集成阶段就获得这个结构体大小,而不是MCU运行的时候;(自动执行)
  • 计算这个结构体大小,不要增加删除原程序代码;(悄无声息)
  • 方便集成工程师使用,不增加使用难度;(简单易用)
  • 不因为人操作的原因,而导致计算结果不准确,即自动化执行输出结果。(无人为干扰)

    ; O; ^: K' Z" p# ]4 E
总之,做这件事的目的是:每次集成的时候自动输出结果(很懒),也不行交给其他小伙伴去手工计算,或者更改原来的结构代码去计算这个空间,怕其乱来搞坏了原来的代码(很操心)。
再总之:能让电脑干的活,干嘛要让人去干!
于是,我就突发奇想,写个脚本呗。
那么啥子脚本可以计算C语言的结构体大小?
身为优秀的“嵌入式”工程师,对这种将C语言“嵌入”到脚本中的事情肯定是要研究一番的。
Note:为了方便描述,我将具体项目细节和装X的过程隐去,并将这个神一样的结构体简化为:
typedef struct
! k0 A: N4 `, G: R2 [ {
7 Z' r) n* h' l     unsigned char item_a[2];3 _1 J+ D( ^  Q; l
     unsigned char item_b[3];
: x! _3 }& Y( W# H8 z* Q4 O3 O3 W     unsigned char item_c[5];
" L- ^7 h* l7 m' |( K     unsigned char item_d[8];4 z5 d4 r2 |  I6 @8 K( r! G) Q
     unsigned char item_e[33];
& y+ k! t) F" I; c8 j% b1 Y     unsigned char item_fxxk[1];
0 Z0 ^5 {6 i$ H5 g6 O4 d' r/ e     unsigned char item_fxxxk[2];
4 t# |2 T% l" u0 @- ^     unsigned char item_fxxk_any[55];
' p* Y! O& x& y/ W8 Z! [     // add items here...
& K& h; }3 q: o' r8 b5 _5 W# x; d }typeStructData;

▍将C/C++代码嵌入Python
) ]" y6 e9 t: w& K2 V5 K4 f
人生苦短,我用Python
温馨提示,使用以下方法,请提前安装:
  • Python
  • GCC(例如Windows上的MinGW)
  • 用pip安装pyembedc(pip install pyembedc)" a8 h, {( B$ n6 k8 H2 K" {
注意:Python的版本位数要跟GCC的对应,例如都选32位的。
方法1:Python调用exe方式
步骤:
  • 在一个临时C文件里,编写临时main函数;
  • 用GCC构建编译,生成exe;
  • 通过脚本(此处选择Python)调用运行输出结果;
  • 删除临时C文件和exe文件。
    # O4 s9 i6 P$ A* K; F" Y) b
接上代码看看
// struct.h
7 C3 M4 z/ C: m , ]! q2 Y) y6 v; l2 M
typedef struct
! O  w# Y; i1 M {# Z, @+ x0 G. A2 ^6 M
     unsigned char item_a[2];  P! i+ N+ E/ w
     unsigned char item_b[3];
" Q6 q$ `! `0 \/ J* A& _2 Z0 d) E     unsigned char item_c[5];
( `: H, ^# u$ o3 A( K     unsigned char item_d[8];0 s, f, ~; R9 O
     unsigned char item_e[33];
: ^* y" U! Z4 e- H' \, z8 T3 K     unsigned char item_fxxk[1];( @8 {; b- p1 p  ^
     unsigned char item_fxxxk[2];
, ~. U! i* D2 Y     unsigned char item_fxxk_any[55];' J: m9 S; G% V# O+ p% n# I8 E& _) P4 e
}typeStructData;
import os
# w; x( K! U, A0 C4 `
. y$ D4 D# s7 O, g c_main = r'''  n7 U6 o" L2 p+ `8 P
#include <stdio.h>& V3 K! G- ~( g- U( z  `) m3 j  Z; _
#include "struct.h"
. m3 F1 O$ q! y- O+ \$ v+ k$ u! ] int main(void)% I- k5 W5 N$ Q/ S& T" K1 G3 H$ }
{9 H( g/ o4 [1 v, Z$ w. n, g  ~
    printf("size: %d\n", sizeof(typeStructData));
' @6 c3 J+ J, n& Z# r5 X: Q4 u    return 0;
% {: ?2 K$ H8 p4 `" u$ A }9 a( v# d% j  n# ]+ p1 T& v! J
'''
" `4 l+ W/ M/ q8 i5 a4 r0 n def cal_struct_size():  G& {% K* B; S0 F; u: I
     f_c_main = 'xxxxsizeofstructxxxx.c'/ h2 A9 U& b( P0 p* ]
     f_run = 'xxxxsizeofstructxxxx.exe'
& d, c0 A* g8 x+ W( U$ ]     with open(f_c_main, 'w') as f: f.write(c_main)" T; `# n0 w- E1 h5 l- O% n" M" _
     gcc_compile = "gcc %s -o %s"%(f_c_main, f_run)4 m4 I: t+ e0 t5 [+ _" G5 _
: T% P% i0 \2 o+ i; I
     os.system(gcc_compile)1 b! _7 ]# v6 I: i# C# b  j3 B
     os.system(f_run)9 \8 o( _" }  a  O# `
     if os.path.exists(f_c_main): os.remove(f_c_main)
: [7 Z/ V$ H, S" I! W* r5 P" o* e, }: ~     if os.path.exists(f_run): os.remove(f_run)+ v% q+ V& u: e. i" w

8 t( s7 `+ q) p% }' s. e if __name__ == "__main__":, C2 y' }" `, ^% B; F6 Z
     cal_struct_size()
方法2:Python调用lib方式
总觉得用Python调用exe的方式有点low,再进一步,那就调用lib吧,例如调用.so内的函数或者变量。
步骤跟方法1类似:
  • 在一个临时C文件里,编写临时main函数;
  • 用GCC构建编译,生成lib(.so);
  • 通过Python调用运行输出结果;
  • 删除临时C文件。
    8 M; n4 q# t0 i% }$ ]( [8 k
Python调用lib库有个好处,可以调用其里面的具体函数等。
// struct.c
$ O0 i: W/ z% f1 k% }$ Q% u
& q+ e; J7 p& h% N1 |* L% J4 G  C #include <stdio.h>
1 R# j% u; h! O# F6 L1 g/ |; x2 p ! ~$ z1 j8 y$ Q  G( j' q1 `
typedef struct
) C9 f, o9 E  ]. [  S) A$ o" c" B* C0 k1 o {
. Z5 {% t- J' q3 ]- q     unsigned char item_a[2];  K# G6 L0 Y5 v* P* D
     unsigned char item_b[3];
/ e, i: J# y' Y' T     unsigned char item_c[5];( D6 Z; \# @% d0 |6 S, d. L0 O7 v
     unsigned char item_d[8];4 D) }  u. P' j9 N
     unsigned char item_e[33];- o' E8 j5 F- z
     unsigned char item_fxxk[1];) A6 o+ D, ~; d) d: Q
     unsigned char item_fxxxk[2];
+ o% Z6 C# U. J& o9 i0 p7 W, I     unsigned char item_fxxk_any[55];
* ^- d7 @& a( @  y+ V" I$ o }typeStructData;  s/ }* x1 {! E2 ^3 j0 g( s
$ }* F8 Z8 M% T* r. B& N4 w
int get_struct_size(void)
; e2 Q& u+ o$ ]9 U5 f! a {
3 {) K. X+ q. o9 G     return sizeof(typeStructData);
+ y) @: f- e! t; i2 F }
import ctypes# C; K) f2 I7 t; j3 J# l
import os
4 r% _+ O6 w3 F+ H$ F' F( I 4 T  D7 F( v& d% W0 O% o6 O6 v  n
os.system('gcc -shared -Wl,-soname,struct -o struct.so -fPIC struct.c')
9 D# [9 m" x( U$ t2 P7 f- @; W 2 [# a6 P! L! o4 j) n# G1 v
struct_size = ctypes.cdll.LoadLibrary('./struct.so')! F4 o- i) t6 w1 a& c/ e0 t
5 Y+ A) Q$ S2 f; I& a& X
def cal_struct_size():
) {& d/ W6 _: r; @$ d8 M' @     s = struct_size.get_struct_size()
' B0 a* o; k) j. q0 d
+ Y  V( {0 h0 r' |' r     print("size: %d"%s). F' K* e6 b# x! E4 ?

. p  w! f' z3 M( j5 s, [+ L if __name__ == "__main__":+ F5 c9 o9 k) }" g* f
     cal_struct_size()5 [7 s1 H/ t8 x' T5 ~
     # 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.py
7 b% Z! \  y. p  ^  M: a
& t* L& K9 e8 y! o- a( ^ from pyembedc import C- t" J# Y! n+ ~  C9 @! C
5 T7 ~2 N* e4 t( h7 Z6 E! w
struct_str = r'''& A& i5 Z1 H3 X" M
    typedef struct
, a6 a! _: H+ ^% g2 U    {  B1 r( a, V4 E5 m7 t4 J
        unsigned char item_a[2];/ U' R/ g! D& R5 ]) b+ j/ |
        unsigned char item_b[3];% E+ t+ W) Y4 T/ A( s
        unsigned char item_c[5];1 e/ N) H7 d4 @' L$ A
        unsigned char item_d[8];1 z  I" k! Z; ], l' i
        unsigned char item_e[33];. x# b4 w6 Z) C0 p) g
        unsigned char item_fxxk[1];- q0 P5 H  ^) m* `" G8 ^
        unsigned char item_fxxxk[2];' m/ s& }3 b  ^1 ]
        unsigned char item_fxxk_any[55];
2 S' I$ @6 _! [' ^    }typeStructData;9 \1 L1 Z- D1 p9 @9 @
    struct_size = sizeof(typeStructData);
3 C2 Q# ]* M6 l2 A2 a
. h/ C9 _* }8 i! T/ }* s6 M '''
/ h( m; P$ y7 ?$ [8 m; ~ struct_size = 0
( f( F  K) |" O4 G' Z* J. B struct_f = C(struct_str)
$ R9 |8 t% o$ m9 z! { print('size: %d\n'%struct_size)
例2,访问C内部函数
# callstruct_inline2.py- E2 J$ D' X  `# G, K: p: \
4 |- B+ h3 \( z, `
from pyembedc import embed_c8 e! F4 ^; g. V( ^/ Z6 N

6 Z0 Z' Q# S. `2 V6 t  r1 S struct_str2 = r'''
) @" n3 n# K: u6 `5 o    typedef struct9 b3 m! \  Q5 V
    {* [& z9 K6 K( g6 e) x5 a3 p" S2 }
        unsigned char item_a[2];
& J) g+ i+ i; T, g2 g. ~        unsigned char item_b[3];
: _$ c/ t+ n; K0 h1 W( x        unsigned char item_c[5];& G" K( U* P+ l" |& |" c+ m' A
        unsigned char item_d[8];5 o4 ^) `4 U* Y6 B$ q2 J' {) z
        unsigned char item_e[33];
( l- S) C! D' I0 C( ~        unsigned char item_fxxk[1];
# R* ^5 [+ E- {3 p% \9 {' ?        unsigned char item_fxxxk[2];9 w  K9 ?+ D' `1 x
        unsigned char item_fxxk_any[55];& [0 F) Z/ d0 f0 D. Z; M
    }typeStructData;
; D. `( ~- k! s! o4 ~& V     
" H/ Q3 O% [! `4 A$ s    int get_struct_size(void)
8 A8 G  D" ~$ ]" L( Y0 T4 X    {0 R) G: w3 _% g
        return sizeof(typeStructData);
  m0 v) s- r- Z/ g    }7 M# j6 K7 B4 {3 O. O8 s
'''
9 L+ V  [; V) C2 O5 r3 D! q) H" ~ struct_c = embed_c(struct_str2)
: p' l/ s. S% M$ I& \. X 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.
更多内容,请见:http://github.com/ftrias/pyembedc

▍将C/C++代码嵌入Ruby
9 h$ D% f% z$ W7 P" T7 q/ Q3 _
生活诗意,我用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库,无需下载其他内容。
    2 p$ O+ y4 u. I* _
require "inline"6 J# y# h7 q( {! s, V
class MyTest
( D7 R0 `3 V+ G6 O     inline do |builder|" M) P# [" f! e
         builder.c "
; u2 l: ?# H- @            long factorial(int max) {! P4 \( W1 k4 s6 G9 V( V4 g! j
                int i=max, result=1;
. Q8 e6 M0 D) M$ }% a* W) Y                while (i >= 2) { result *= i--; }0 A, r% X% |3 {  f, [
                return result;
( v2 Q) f; w. Y' I1 q            }"* H1 u) B6 a* `! b( U1 s
         end
/ A) Z& ^3 ~, V- C3 Z3 D end
5 H& z- j% O* [' P t = MyTest.new()
- s/ X, f/ k! D7 A, d% c) x factorial_5 = t.factorial(5)
require 'inline': e. x" D) d% l- f' p: @
class MyTest" o5 p; F7 y' X
     inline(:C) do |builder|4 z! b/ U* e5 ]$ D* y
         builder.include '<iostream>'. v3 q( z* ]5 i7 J
         builder.add_compile_flags '-x c++', '-lstdc++'
* h' {2 {" W) R         builder.c '
( E7 y# {! L. w1 P            void hello(int i) {
9 ]) Q" o; q& X( O3 d/ [6 o% U                while (i-- > 0) {) f$ M( M; m4 F! ^9 H- g0 w. f+ W
                    std::cout << "hello" << std::endl;& u0 j# d. o; h6 ^1 N4 s$ d
                }$ F$ N' `. u8 F) F$ C3 z6 q' ]
            }'
- _: L8 g5 @1 u8 u! {) r     end
. a" T! h9 \% i! j2 U5 ~) o end
) ^9 w5 o% I# {8 c: r t = MyTest.new()) E/ P, c2 W6 v  X( V# }
t.hello(3)
* ~* |" S# H7 f, Q% P; t: r: D6 {
是不是很好玩,是不是很想试试?但是我告诉你,我在Windows上没搞成功,但在Linux上搞起来了。应了网上某句话:想学Ruby,就用Linux吧,别在Windows上瞎折腾。话说回来,这个嵌入式C源码的用法,个人感觉Ruby的比Python的简洁直观。该用哪种方法,看实际需要吧。更多内容,详见:http://github.com/seattlerb/rubyinline
+ ~6 M$ p- k( F& i& F) E2 j

▍总结
5 w. B! ^5 _- B7 J
想将C/C++塞进脚本,需要借助GCC,它才是让你装逼让你飞的前提条件。

/ t# [6 _3 ~, f2 r3 \& N
赞 收藏 1 评论0 发布时间:2020-6-28 18:58

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版