【问题标题】:How to return a function pointer from CPython to Python then call a C function with ctypes?如何将函数指针从 CPython 返回到 Python,然后使用 ctypes 调用 C 函数?
【发布时间】:2011-03-01 18:33:12
【问题描述】:

我有 CPython 函数:

void my_cfunc(int arg)
{
  printf("Hello from C; arg=%d\n", arg);
}

PyObject *get_fptr(PyObject * /*self*/, PyObject * /*args*/)
{
    return PyCObject_FromVoidPtr(my_cfunc, NULL);
}

然后,在 Python 中我有:

import mymodule

ptr = mymodule.get_fptr() # will return a PyCObject wrapping the C function pointer

然后:

from ctypes import *

SOMEFUNC_T = CFUNCTYPE(c_void, c_int)
somefunc = SOMEFUNC_T(ptr) # <-- bad!

现在,如果我将 get_fptr 更改为返回:PyLong_FromSize_t(size_t(my_cfunc)) 那么 'somefunc' 将是有效的。

但我不想将函数指针强制转换为 size_t。

请指教

【问题讨论】:

    标签: python function pointers casting ctypes


    【解决方案1】:

    首先,我不明白为什么你想从 Python 扩展返回一个 C 函数指针,只是为了从 Python 调用它(通过 ctypes),而合乎逻辑的事情是通过调用 C 函数Python 扩展(除非我遗漏了什么)。

    其次,看起来 ctypes 根本不支持 PyCObject。您使用 PyCObject 参数调用 CFUNCTYPE(None, c_int) [我将 c_void 替换为 None] 失败,因为 CFUNCTYPE 需要一个整数,并且不知道如何处理 PyCObject。

    为什么不为 my_cfunc 编写一个 Python 包装器,您可以从 Python 调用它而无需 ctypes 麻烦?例如:

    PyObject *call_fptr(PyObject *self, PyObject *args)
    {
        int arg;
        if (!PyArg_ParseTuple(args, "i", &arg))
            return NULL;
    
        my_cfunc(arg);
        Py_RETURN_NONE;
    }
    

    作为奖励,如果您喜欢 ctypes,那么 my_func 的 Python 包装器可用于实例化 ctypes 外部函数(与 PyCObject 不同)!

    from ctypes import *
    import foo
    
    SOMEFUNC_T = CFUNCTYPE(None, c_int)
    cfunc = SOMEFUNC_T(foo.call_fptr)
    cfunc(1)
    

    编辑: 指定 C 函数应采用可变数量的参数会使您的问题变得更加困难......考虑到 CFUNCTYPE 需要一组已知的参数,您将如何用 ctypes 包装可变参数 C 函数?

    这种情况下的问题实际上归结为将 Python 元组转换为 C 中的变量参数列表,这显然不是微不足道的。事实上,SWIG 已经专门为这个问题提供了一个 section of its documentation,例如说:大多数其他包装器生成工具都明智地选择避免这个问题

    不过,它确实给出了建议,即可以在 libffi 的帮助下以可移植的方式动态构造变量参数列表。

    最终,我的建议是用SWIG 包装你的变量参数函数,这样可以省去你的痛苦。

    【讨论】:

    • 好吧,我应该是准确的。实际上 void my_cfunc(int arg) 应该类似于:“void my_cfunc(int n1, ...)” .... 所以,我有两个解决方案:(1)我在做什么(2)我不知道怎么做在 Python 中包装一个 va_arg 函数。这就是为什么我求助于返回它的函数指针,然后用 ctypes 再次调用它
    • 不,使用 ctypes 您可以轻松地包装一个 va_arg 函数 :) - 只需将其地址传递给 ctypes(如我的示例所示)。 - 使用无限 args 调用它,例如 cfunc(1, 2, 3, 4, 5, 6) ... - 更好的是,您可以将其称为:cfunc(*args) 这就是我想要获取 C 函数的原因指针并将其提供给 Python,然后将其提供给 ctypes,然后我可以调用 va_arg 函数。
    • 在实际方面,我想提一下,我很好地包装了一个 vaarg 函数并且它可以工作......我想要的只是避免将函数指针转换为 size_t 而将它提供给 ctypes。但现在这似乎有效并且应该继续有效,因为 size_t 将具有与指针相同的大小。
    • @Elias Bachalaany:所以可以用变量参数实例化 CFUNCTYPE 吗? ctypes 文档暗示这是不可行的,wrt。打印/扫描。无论如何,现在您知道为什么必须转换为整数了 :)
    猜你喜欢
    • 2013-01-30
    • 1970-01-01
    • 2021-10-01
    • 2016-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-14
    • 1970-01-01
    相关资源
    最近更新 更多