代码之家  ›  专栏  ›  技术社区  ›  Xantium

返回函数python-c-api

  •  0
  • Xantium  · 技术社区  · 6 年前

    我正在为C库创建Python绑定。

    在C语言中,使用函数的代码如下:

    Ihandle *foo;
    foo = MethFunc();
    SetArribute(foo, 's');
    

    我想把这个放到蟒蛇身上。我在哪里 MethFunc() SetAttribute() 可以在我的python代码中使用的函数:

    import mymodule
    foo = mymodule.MethFunc()
    mymodule.SetAttribute(foo)
    

    到目前为止,我返回函数的C代码如下:

    static PyObject * _MethFunc(PyObject *self, PyObject *args) {
        return Py_BuildValue("O", MethFunc());
    }
    

    但失败的是崩溃(没有错误)

    我也试过了 return MethFunc(); 但是失败了。

    如何返回函数 foo (或者如果我试图达到的目标是完全错误的,我该怎么通过呢? 甲基芬克() 设置属性() )?

    1 回复  |  直到 6 年前
        1
  •  2
  •   abarnert    6 年前

    这里的问题是 MethFunc() 返回 IHandle * 但是你要告诉巨蟒把它当作 PyObject * . 大概那些是完全不相关的类型。

    复制对象* (或任何 struct 您或python定义以适当的 HEAD macro)从指向refcount和类型的指针开始,python要处理的第一件事就是处理这些指针。所以,如果你给它一个物体,它从,比如,两个开始 int s,python最终会尝试访问 0x00020001 或者类似的,几乎可以肯定是SegFault。

    如果需要传递指向某个C对象的指针,则必须将其包装在一个Python对象中。有三种方法可以做到这一点,从黑客到最可靠。


    首先,你可以把 伊汉德尔* 到A size_t 然后 PyLong_FromSize_t 它。

    这非常容易实现。但这意味着这些对象将与Python端的数字完全相同,因为它们就是这样。

    显然,您不能将方法附加到此数字;相反,您的API必须是一个接受数字的自由函数,然后将该数字强制转换回 IHandle* 并调用一个方法。

    它更像,例如,C's stdio ,你必须不断传球 stdin f 作为一个论点 fread ,而不是python的 io ,在其中调用方法 sys.stdin f .

    但更糟糕的是,由于没有类型检查(静态或动态)来保护您免受某些Python代码意外传递给您的数字的影响 42 . 然后将其强制转换为 伊汉德尔* 尝试去引用,导致一个segfault

    如果您希望Python的垃圾收集器能够帮助您了解对象何时仍然被引用,那么您就走运了。您需要让您的用户手动跟踪号码并拨打一些 CloseHandle 完成后就可以使用。

    实际上,这并不比从 ctypes 希望这能激励你继续阅读。


    更好的解决方案是 伊汉德尔* 到A void * 然后 PyCapsule_New 它。

    如果你没读过 capsules 你至少需要略读一下主要章节。但最基本的想法是 void* 作为python对象。

    所以,它几乎和传递数字一样简单,但解决了大多数问题。胶囊是不透明的值,您的python用户不能意外地对其进行算术;它们不能发送给您 四十二 代替胶囊;您可以附加一个函数,当对胶囊的最后一个引用消失时调用该函数;您甚至可以给它起一个好名字来显示在 repr .

    但是你仍然不能将任何行为附在胶囊上。

    因此,您的API仍然必须是 MethSetAttribute(mymodule, foo) 而不是 mymeth.SetAttribute(foo) 如果 mymodule 是一个胶囊,就像是一个 int . (除了现在它是类型安全的。)


    最后,可以为包含 伊汉德尔* .

    这是更多的工作。如果你还没有阅读 Defining Extension Types 你需要通读整章。

    但这意味着您有一个实际的Python类型,以及与之配套的所有内容。

    你可以给它一个 SetAttribute 方法,而python代码可以调用该方法。你可以给它任何东西 __str__ __repr__ 你想要的。你可以给它一个 __doc__ . python代码可以 isinstance(mymodule, MyMeth) . 等等。


    如果你愿意使用C++,或者D,或者锈,而不是C,有一些很棒的库(pycxx,Booo::python,pyd,锈蚀python等)可以为你做大部分的样板。您只需声明您想要一个Python类,以及如何将其属性和方法绑定到C属性和方法中,并且可以得到像C++类一样使用的东西,除了它实际上是一个 复制对象* 在盖子下面。(它甚至可以通过raii为您处理所有的重新计数问题,这将为您节省无休止的周末调试segfaults和内存泄漏)

    或者你可以用 Cython 它允许您用基本上是Python的语言编写C扩展模块,但扩展到与C代码的接口。所以你的包装类只是一个 class 但是有一个特别的私人 cdef 保存的属性 伊汉德尔* 和您的 SetAttribute(self, s) 可以打电话给C 设置属性 具有该私有属性的函数。

    或者,根据用户的建议,您也可以使用 SWIG 为您生成C绑定。对于简单的情况,它是非常琐碎的,只需将它作为C API提供,它就可以返回代码来构建Python。 .so . 对于一些不太简单的案例,我个人觉得它比PyCxx之类的东西要痛苦得多,但是如果你还不知道C++的话,它肯定有一个更低的学习曲线。