使用 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 返回值的主要内容,如果未能解决你的问题,请参考以下文章

使用 -O 优化时 ctypes 返回错误值

<ctype.h> - isgraph()

<ctype.h> - iscntrl()

<ctype.h> - isgraph()

<ctype.h> - iscntrl()

<ctype.h> - isdigit()