使用 ctypes 返回值
Posted
技术标签:
【中文标题】使用 ctypes 返回值【英文标题】:Working with ctypes return values 【发布时间】:2015-11-20 23:16:16 【问题描述】:我正在研究从 Python 调用 C++ 代码。我刚刚了解了 ctypes,并在网上看到了一些示例。我正在尝试调用如下所示的 C++ 函数:
get_data(re * data_record, char * string)
对c++一无所知,我要问一些基础知识。我如何应该使用 ctypes 从 python 调用这样的函数。 我决定退后一步,因为我认为我在这里遗漏了一些基础知识。
事实上,对于任何接受字符串类型参数并返回整数的 C++ 函数,请参阅一些示例
struct db_obj
char db_name[50],
int_32 db_guid,
char db_path[100];
;
get_database_running_status(db *db_obj,int_8 db_id,int_8 *retvalue);
get_database_running_status_string(db *db_obj,int_8 db_id,char *retvalue);
此函数将返回一个状态值(retvalue),我可以对其进行解码以了解数据库的状态,例如 10=running fine、11=stropped、12=pause、404=fault 等。
而 get_database_running_status_string 将返回字符串而不仅仅是数字
我将如何调用这些?虽然我希望它有所帮助,但不确定那个 * 符号是什么意思。
也许一旦我了解了如何从 python 中正确调用 C++ 函数,那么我的问题可能会自行解决。
【问题讨论】:
函数怎么调用? (请包括代码 - 我也对函数参数感兴趣)。您是否尝试过从 c 程序调用它?您是否也尝试过简化问题(例如,调用一个没有 arg 的函数,它只返回一个char*
)并从 python 调用它(restype
在这种情况下有效)。
【参考方案1】:
发布一个工作 C++ 代码的实际示例会有所帮助,但我将创建类似于演示的内容。
这里是简短的 C++ DLL 源代码。由于 ctypes
包装了 C 函数,而不是 C++,因此函数被声明为 extern "C"
以正常工作。我在 Windows 上执行此操作,因此使用__declspec(dllexport)
将函数标记为从 DLL 导出:
#include <stdio.h>
struct record
int x,y;
;
typedef int int_32;
typedef char int_8;
struct db_obj
char db_name[50];
int_32 db_guid;
char db_path[100];
;
extern "C"
__declspec(dllexport) void get_data_int(record* pRec, int* pResult)
*pResult = pRec->x + pRec->y;
;
__declspec(dllexport) void get_data_string(record* pRec, char* pResult, size_t size)
sprintf_s(pResult,size,"%d + %d = %d\n", pRec->x, pRec->y, pRec->x + pRec->y);
__declspec(dllexport) void get_database_running_status(db_obj* db, int_8 db_id, int_8 *retvalue)
printf("db_name = %s\ndb_guid = %d\ndb_path = %s\ndb_id = %d\n", db->db_name, db->db_guid, db->db_path, db_id);
*retvalue = 42;
这是ctypes
包装器:
#!python2
import ctypes as c
class Record(c.Structure):
_fields_ = [('x',c.c_int),('y',c.c_int)]
class db_obj(c.Structure):
_fields_ = [('db_name',c.c_char * 50),
('db_guid',c.c_int),
('db_path',c.c_char * 100)]
dll = c.CDLL('x')
get_data_int = dll.get_data_int
get_data_int.argtypes = [c.POINTER(Record), c.POINTER(c.c_int)]
get_data_int.restype = None
get_data_string = dll.get_data_string
get_data_string.argtypes = [c.POINTER(Record), c.POINTER(c.c_char), c.c_size_t]
get_data_string.restype = None
get_database_running_status = dll.get_database_running_status
get_database_running_status.argtypes = [c.POINTER(db_obj), c.c_int8, c.POINTER(c.c_int8)]
get_database_running_status.restype = None
result = c.c_int()
rec = Record(5,7)
get_data_int(c.byref(rec),c.byref(result))
print(result.value)
result = c.create_string_buffer(50)
rec = Record(5,7)
get_data_string(c.byref(rec),result,c.sizeof(result))
print(result.value)
result = c.c_int8()
db = db_obj('database name',1234,'database path')
get_database_running_status(c.byref(db),7,c.byref(result))
print(result.value)
输出:
12
5 + 7 = 12
db_name = database name
db_guid = 1234
db_path = database path
db_id = 7
42
有关详细信息,请参阅ctypes 文档。
【讨论】:
谢谢。我尝试添加更真实的代码示例,请参阅更新的问题。我仍然不清楚如何将字符串传递给函数,我看到了 argtypes c.POINTER(c.c_char),但是如何将“hello world”作为输入参数传递? 是不是也意味着当C函数有一个*表示python argtypes必须有ctypes.POINTER(适当的CTYPE)? @user595985,对于输入字符串,使用c_char_p
并传递输入字符串。对于输出字符串,使用POINTER(c_char)
,分配内存以保存create_string_buffer
的字符串,并传递缓冲区。 C++ 定义中的*
表示一个指向值的指针,这就是我使用POINTER
的原因。请注意,输入字符串的c_char_p
已经是一个指针,不需要包装在POINTER
中。我的印象是你不懂 C/C++?这将使使用ctypes
变得困难:)
谢谢您,您最后的评论和示例确实为我做了。你说得对,我不懂 C/C++,我第一次不得不以任何有意义的方式与 C 交互。类型..谁需要它们(开个玩笑)。谢谢以上是关于使用 ctypes 返回值的主要内容,如果未能解决你的问题,请参考以下文章