【python环境下调用C代码---附件】学习笔记

目录文件结构
project
│   cffi_test.py
│   make_cdef.py
│
└───source_files
     │   call_back.h
     │   call_back.lib
     │   hello_world.c
     │   hello_world.h
cffi_test.py:
# -*- coding: utf-8 -*-
import cffi

ffi = cffi.FFI()  # 实例化ffi
from _test_cffi import ffi, lib

string_data = ffi.new("char[]", b"hello")  # 使用C的数据类型
print("字符串长度:", len(string_data), "  字符串第一个字符:", string_data[0], "  字符串内容:", ffi.string(string_data))

# "Process finished with exit code -1073741819 (0xC0000005)" 为错误使用指针
num = ffi.new("int *", 520)  # 指针
lib.HelloWorld(num)  # 调用C函数

struct_data = ffi.new("struct_test *", [1, 2])  # 使用库里的数据类型
print(f"struct_data.y: {struct_data.y}")

lib.PrintTest()


# 定义回调函数
@ffi.callback("void (*CallBack)(int num)")
def PrintNum(num):
    print(f"\r\nThe Num Is: {num}", num)


# 将回调函数作为参数传给调用函数
lib.call(PrintNum, 520)

make_cdef.py(自动编译 .h 文件):
# -*- coding: utf-8 -*-
import cffi
from pathlib import Path

SCRIPT_DIR = Path(__file__).parent  # 获取当前路径
ffi = cffi.FFI()  # 实例化ffi


def mkcdef(headerfile: str):
    import tempfile
    temp_file = tempfile.mktemp(".c")  # mktemp用于返回一个临时文件的路径,但并不创建该临时文件。
    open(temp_file, 'w').write(headerfile)  # 将源文件写入临时文件,避免对源文件的直接操作

    import re
    LINE_REGEX = re.compile(r'[\s]*#')
    import distutils.ccompiler

    comp = distutils.ccompiler.new_compiler()
    ccname = distutils.ccompiler.get_default_compiler()  # 得到默认编译器类型

    cflags = {'msvc': ['/P'], 'unix': ['-E']}[ccname]  # 额外编译指令
    # 依赖的文件路径
    call_back_dir = SCRIPT_DIR / 'source_files'

    tmpdir = tempfile.mkdtemp()  # mktemp用于返回一个临时文件的父级路径,但并不创建该临时文件。

    # 设置编译的源文件(temp_file),(debug)是一个布尔值;如果为true,编译器将输出调试符号,
    # 生成文件路径(output_dir),文件搜索路径(include_dirs),用于在编译器命令前加/后加的命令行参数(extra_postargs)
    comp.compile([temp_file], debug=1, output_dir=tmpdir, include_dirs=[call_back_dir], extra_postargs=cflags)
    cdefList = []
    if ccname == 'msvc':  # MSVC,就是微软(MS)的VC运行库。
        IFile = f'{Path(temp_file).stem}.i'
    elif ccname == 'unix':
        IFile = list(Path(tmpdir).glob('**/*.*'))[0]

    # 对 C编译器处理过后的文件 按行对 python编译器无法处理 的对应行进行处理
    for line in open(IFile, 'r'):
        # 每行中带有如下字符串的(python无法处理的)不放入编译文件
        if not LINE_REGEX.match(line) and not line.strip() == '' and '__pragma' not in line:
            cdefList.append(line)
    # 移除 C编译器处理过后的文件
    import os
    os.remove(IFile)

    # # 去掉 python编译器 编译不支持的语言
    # cdefList.remove()
    # 增加额外的声明函数
    cdefList.append("void PrintTest(void);\n")

    cDef = ''.join(cdefList)

    # 生成一个 "CDEF.txt" 文件,方便了解可以调用的内容
    with open("CDEF.txt", "w") as f:
        f.write(cDef)
    return cDef


# try:
#     from _test_cffi import ffi, lib  # 先尝试加载库,加载不到才去编译,若已经存在则避免重复编译,节省时间
# except Exception as e:
cdef = mkcdef(open('.//source_files//hello_world.h', 'r', encoding='utf-8').read())
ffi.cdef(cdef)  # 声明xxx.h里的函数和全局变量

# 设置输出库名,C源文件
ffi.set_source("_test_cffi", ''' 
#include <stdio.h>
#include "./source_files/hello_world.c" 
void PrintTest(void)
{
    printf("Your_name666");
}
''', libraries=["./source_files/call_back"])
# ffi.set_source("_test_cffi", ''' #include "hello_world.c" ''',
#                libraries=["lib_name"], include_dirs=["path_1", "path_2"],
#                extra_compile_args=['/MT'])    # 添加库(不含后缀),指定包含路径, 额外编译参数
ffi.compile(verbose=True)  # 编译

# 删除临时生成的 ".i" 与 ".c" 文件
import os
# 输出 SCRIPT_DIR 路径下的所有文件
all_file = os.listdir(SCRIPT_DIR)
for index in range(len(all_file)):
    file = all_file[index]
    # 将 以'.i' 文件全部删除
    if file[-2:] == '.i':
        os.remove(SCRIPT_DIR / all_file[index])
call_back.h
// 声明回调函数
typedef void (*CallBack)(int num);

// 声明回调函数的调用函数
void call(CallBack func, int num);
call_back.c(生成call_back.lib)
#include "call_back.h"

// 定义回调函数的调用函数
void call(CallBack func, int num)
{
    func(num);
}
hello_world.c
#include "hello_world.h"
#include <stdio.h>
 
void HelloWorld(int *num)
{
    printf("%d.Hello World\r\n", *num);
}
hello_world.h
# include "call_back.h"
typedef struct { int x, y; } struct_test;

void HelloWorld(int *num);
运行 make_cdef.py 生成的 CDEF.txt 内容如下:
typedef void (*CallBack)(int num);
void call(CallBack func, int num);
typedef struct { int x, y; } struct_test;
void HelloWorld(int *num);
void PrintTest(void);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值