【发布时间】:2016-11-11 22:27:30
【问题描述】:
我正在学习如何将C 编写的代码包含在Python 中的各种方法,因为我有一个用于 Microchip 设备的 API,它非常......使用起来很乏味,我想让我的生活通过为其添加Python 包装器,将来会更容易,这将使我能够更快地测试东西。一种方法是使用cffi 模块,它甚至为用户提供verify(),它基本上调用C 编译器来检查提供的cdef(...) 是否正确。
我写了一个小项目,以便我可以首先学习如何正确使用cffi。它由两部分组成
-
库 - 用 C 编写。我使用
cmake和make相应地编译它的代码:CMakeLists.txtproject(testlib_for_cffi) cmake_minimum_required(VERSION 2.8) set(CMAKE_BUILD_TYPE Release) set(CMAKE_CXX_FLAGS "-fPIC ${CMAKE_C_FLAGS}") # Debug build set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -g -O0") # Release build set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Os") aux_source_directory(. SRC_LIST) add_library(testcffi SHARED ${SRC_LIST}) # Not required for the library but needed if I want to check for memory leaks with Valgrind set(SRC main.c) add_executable(${PROJECT_NAME} ${SRC}) target_link_libraries(${PROJECT_NAME} PUBLIC testcffi)testcffi.htypedef struct { double x; double y; double z; char *label; } point_t; // Creation, printing and deletion point_t* createPoint(double x, double y, double z, char *label); void printPoint(point_t *point); void deletePoint(point_t *point);testcffi.c#include "testcffi.h" #include <stdio.h> #include <malloc.h> point_t* createPoint(double x, double y, double z, char *label) { point_t *p = malloc(sizeof(point_t)); p->x = x; p->y = y; p->z = z; p->label = label; return p; } void printPoint(point_t *point) { if(point == NULL) return; printf("Data:\n\tx : %f\n\ty : %f\n\tz : %f\n\tmsg : \"%s\"\n", point->x, point->y, point->z, point->label); } void deletePoint(point_t *point) { if(point == NULL) return; free(point); point = NULL; } -
Python 中的测试代码 - 该代码演示了
struct以及上述库中的三个函数的用法:#!/usr/bin/python3 from cffi import FFI import random ffi = FFI() # Add library's header ffi.cdef(''' typedef struct { double x; double y; double z; char * label; } point_t; // Creation, printing and deletion point_t * createPoint(double x=0., double y=0., double z=0., char *label="my_label"); void printPoint(point_t *point); void deletePoint(point_t *point); ''') # Load shared object from subdirectory `build` CLibTC = ffi.dlopen('build/libtestcffi.so') def createList(length=5): if len: lst = [] for i in range(0, length): lst.append(CLibTC.createPoint( float(random.random()*(i+1)*10), float(random.random()*(i+1)*10), float(random.random()*(i+1)*10), b'hello' # FIXME Why does ONLY this work? # ('point_%d' % i).encode('utf-8') # NOT WORKING # 'point_{0}'.format(str(i)).encode('utf-8') # NOT WORKING # ffi.new('char[]', 'point_{0}'.format(str(i)).encode('utf-8')) # NOT WORKING )) return lst return None def printList(lst): if lst and len(lst): for l in lst: CLibTC.printPoint(l) list_of_dstruct_ptr = createList(10) printList(list_of_dstruct_ptr)
问题来自我必须将Python 字符串转换为的字节数组,以便将数据传递到C 代码中的相应位置。
上面的代码正在运行,但是我想使用类似于b'hello' 的其他字符串。这就是为什么我尝试在Python 中使用format()(连同它的缩写%)来组合一堆字母和一个数字但是。它没有成功。我要么得到 "" 作为我的 point_t struct 的 label 参数的值,要么得到一个奇怪的交替垃圾数据(主要是既不是字母也不是数字的奇怪字符)。
我认为我错误地使用了 encode() 函数,但是当我在 Python 交互式 shell 中对其进行测试时,我得到了与使用 b'...' 相同的输出。
知道这里发生了什么吗?
一个很好知道的问题: 从我目前所读到的内容看来,cffi 使用 Python 中的垃圾收集来释放 C 中动态分配的内存代码。我已经用一堆点测试了它,但我想确保这实际上总是如此。
更新:
好的,所以看起来没有new(...) 的东西确实有效,但是在这种情况下,所有值都与循环中的最后一个值相同。例如,如果循环达到 10,那么所有 struct Python 对象的标签中都会包含 10。这似乎是一个参考问题。当我使用new(...) 时,我得到了垃圾数据。
【问题讨论】:
标签: python c python-cffi