我正在尝试使用Cython包装DLL中定义的一些函数,难点在于很多函数使用指向void *的指针,这里是函数原型的一个例子:
---------------"header.h"-----------------------
typedef void* HANDLE
int open(HANDLE* a_handle_pointer , int open_mode)
int use(HANDLE a_handle, int usage_mode )
C中的用法示例是:
---------------"main.c" -----------------
#include"header.h"
HANDLE my_handle ;
int results ;
if(open(&my_handle ,1) == 0) /* open a handle with mode 1 */
{
printf ("failed to open \n);
return 0;
}
else printf("open success \n");
use(handle , 2); /* use handle (opened with open) in mode 2 */
你可以说,除非Handle已经使用“open”函数打开,否则“Use”函数无法执行某些操作,这使得它在Python / cython中混乱
这是我如何在Cython中定义我的函数“open”(几个试验之一)
from libc.stdint cimport uintptr_t
cdef extern from "header.h":
ctypedef void* HANDLE
int open(HANDLE* a_handle_pointer , int open_mode)
def Open( uintptr_t a_handle_pointer , int open_mode)
return open(<HANDLE*> a_handle_pointer , open_mode)
有人建议我尝试将void *指针转换为uintptr_t,但仍然得到错误:
" TypeError: an integer is required " when calling the function.
>>>from my_module import open
>>>open (handle , 1)
我该怎么做才能解决这个问题?
我想知道如何使用void*
或void**
类型的参数从Python调用函数?
在Python本身编写模块/绑定是一个坏主意,特别是如果涉及指针。你宁愿在C中用这样的东西来做...警告:这是CPython 3+特有的。 CPython 2扩展的编码方式不同! BTW:将你的open
函数重命名为load
,因为它与POSIX的open(3)
冲突。
// my_module.c: My Python extension!
/* Get us the CPython headers.
*/
#include "Python.h"
/* And your function's headers, of course.
*/
#include "header.h"
/* Actual structures used to store
* a 'my_module.Handle' internally.
*/
typedef struct
{
PyObject_HEAD /* The base of all PyObjects. */
HANDLE handle; /* Our handle, great! */
} my_module_HandleObject;
/* The type 'my_module.Handle'. This variable contains
* a lot of strange, zero, and NULLified fields. Their
* purpose and story is too obscure for SO, so better
* off look at the docs for more details.
*/
static PyTypeObject my_module_HandleType =
{
PyVarObject_HEAD_INIT(NULL, 0)
"my_module.Handle", /* Of course, this is the type's name. */
sizeof(my_module_HandleObject), /* An object's size. */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ... Don't ask. */
Py_TPFLAGS_DEFAULT, /* The type's flags. There's nothing special about ours, so use the defaults. */
NULL /* No docstrings for you! */
};
/* The "wrapper" function. It takes a tuple of
* CPython PyObject's and returns a PyObject.
*/
static PyObject *my_module_load(PyObject *self, PyObject *args)
{
int load_mode;
if(!PyArg_ParseTuple(args, "i", &load_mode) != 0) { /* Parse the argument list. It should have one single integer ("i") parameter. */
return NULL;
}
/* Create a Handle object, so as to put
* in itthe handle we're about to get.
*/
my_module_HandleObject *the_object = PyObject_New(my_module_HandleObject, &my_module_HandleType);
if(the_object == NULL) {
return NULL;
}
/* Finally, do our stuff.
*/
if(load(&the_object->handle, load_mode) == -1) {
Py_DECREF(the_object);
PyErr_SetFromErrno(NULL);
return NULL;
}
return (PyObject*)the_object;
}
/* The method table. It is a list of structures, each
* describing a method of our module.
*/
static struct PyMethodDef my_module_functions[] =
{
{
"load", /* The method's name, as seen from Python code. */
(PyCFunction)my_module_load, /* The method itself. */
METH_VARARGS, /* This means the method takes arguments. */
NULL, /* We don't have documentation for this, do we? */
}, { NULL, NULL, 0, NULL } /* End of the list. */
};
/* Used to describe the module itself. */
static struct PyModuleDef my_module =
{
PyModuleDef_HEAD_INIT,
"my_module", /* The module's name. */
NULL, /* No docstring. */
-1,
my_module_functions,
NULL, NULL, NULL, NULL
};
/* This function _must_ be named this way
* in order for the module to be named as
* 'my_module'. This function is sort of
* the initialization routine for the module.
*/
PyMODINIT_FUNC PyInit_my_module()
{
my_module_HandleType.tp_new = PyType_GenericNew; /* AFAIK, this is the type's constructor. Use the default. */
if(PyType_Ready(&my_module_HandleType) < 0) { // Uh, oh. Something went wrong!
return NULL;
}
PyObject *this_module = PyModule_Create(&my_module); /* Export the whole module. */
if(this_module == NULL) {
return NULL;
}
Py_INCREF(&my_module_HandleType);
PyModule_AddObject(this_module, "Handle", (PyObject*)&my_module_HandleType);
return this_module;
}
为了构建和安装扩展,see the docs on distutils
。
您需要将指针包装在Cython / Python对象中,然后将该对象的实例传递给open和use函数的Python版本。请查看问题Wrapping custom type C++ pointer in Cython,了解如何在Cython中包装和传递指针。我在那里提供的答案也应该适用于void
-pointers。
除非您确实希望将C-API尽可能未经修改地映射到Python,否则您还应该考虑为Python接口使用不同的,可能是面向对象的设计。例如,您可以将调用open()
放在包含HANDLE
指针的Python类的构造函数中,引发错误异常并使use()
成为该类的实例方法。这样,您可以防止错误使用单位化指针和整数返回值来指示Python程序员可能意外的错误。