当前位置: 首页 > 面试题库 >

Python Swig-从ctypes指针创建swig包装的实例

汝弘深
2023-03-14
问题内容

我有用swig包装的类的C 代码。我无法修改代码或包装。在python中,我具有使用ctypes的指向所述C

类实例的指针。如何围绕该指针创建一个Swig包装器?

我知道swig对象拥有一个’this’属性,该属性在内部指向包装的对象,但是我找不到一种将其设置为我手头的指针的方法。

谢谢您的帮助!


问题答案:

可以
执行此操作,但是这需要大量工作,并且解决使ctypes或SWIG接口完整且可用的根本问题比强制互换性要容易得多。(还值得注意的是,从SWIG创建一个ctypes对象比执行您要尝试的工作(从ctypes一个创建SWIG对象)要容易得多。

为了说明这一点,我创建了下面的头文件,将使用SWIG进行包装:

struct Foo {
  double d;
  char msg[20];
};

然后,我用以下接口包装它:

%module test

%{
#include "test.h"
%}

// Prove I'm not cheating here - we can't even instantiate this class
%nodefaultctor;

%include "test.h"

我还添加了一个测试函数供我们从ctypes调用,该函数未进行SWIG包装:

#include "test.h"

// This function returns a Foo, but is only accessible to ctypes
extern "C" Foo *fun1() {
    static Foo f = { 1.234, "Hello" };
    return &f;
}

this对SWIG封装类的此属性的猜测是一个很好的起点,但它并不只是更改它那么简单-
您插入的对象的类型必须与SWIG期望的匹配。它不仅仅是一个表示为int的指针:

repr(test.Foo().this) # You'll need to drop the nodefaultctor directive to see this
"<Swig Object of type 'Foo *' at 0x7f621197d1e0>"

如果检查SWIG生成的源代码,我们可以看到有一个函数带有指针,一些类型信息并为我们创建了这些对象:

SWIGRUNTIME PyObject *
SwigPyObject_New(void *ptr, swig_type_info *ty, int own);

让我们忽略目前默认情况下SWIGRUNTIME定义的事实static,为了让我们开始尝试运行时,我们可以将其重新定义为extern。稍后,我们将介绍无法解决问题的解决方法。

因此,我们的目标是获取“仅ctypes”函数的输出,并通过更多的ctypes调用传递它,SwigPyObject_New以创建可以与SWIG模块的this属性交换的东西。

为了进行调用,我们通常会调用SWIG_TypeQuery以查找swig_type_info要使用的正确方法。但是,实际上这是一个宏,它可以扩展以传递一些始终是静态的静态变量。因此,我们将使用以下功能

SWIGRUNTIME swig_type_info *
SWIG_Python_TypeQuery(const char *type)

(具有相同的SWIGRUNTIME条件)。

至此,我们已经足够可以交换代理对象的此属性,并且只要能够构造供体就可以完成。(尽管那会泄漏)。我们可以通过两种方法使它更好:

  1. __init__里面的猴子补丁test.Foo可以工作。如果您确实希望%nodefaultctor在SWIG界面中进行重新编译,那么这是最好的选择:

    def patched_init(self, ptr):
    self.this = ptr
    

    test.Foo.init = patched_init

  2. 创建一个仅具有的新类,然后在修改属性之前将__init__其设置为this属性__class__,而改用该类:

    class FooCtypesSwigInterop(object):
    def __init__(self, ptr):
        self.this = ptr
        self.__class__ = test.Foo
    

当您不想破坏test.Foo现有的__init__实现时,此选项最有意义。

这样,我们现在可以通过以下方式实现我们的初始目标:

import ctypes
import test

# This *must* happen after the import of the real SWIG module
# 0x4 is RTLD_NOLOAD which ensures that we get the same handle as the Python 
# import did, even if it was loaded with RTLD_LOCAL as Python is prone to.
swig_module = ctypes.PyDLL('./_test.so',ctypes.RTLD_LOCAL|0x4)
# Note that we used PyDLL instead of CDLL because we need to retain the GIL

# Setup SWIG_Python_TypeQuery to have the right argument/return types
# (Using void* as a substitute for swig_type_info*)
SWIG_Python_TypeQuery = swig_module.SWIG_Python_TypeQuery
SWIG_Python_TypeQuery.argtypes = [ctypes.c_char_p]
SWIG_Python_TypeQuery.restype = ctypes.c_void_p

# Likewise SwigPyObject_New, using ctypes.py_object though for return
SwigPyObject_New = swig_module.SwigPyObject_New
SwigPyObject_New.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int]
SwigPyObject_New.restype = ctypes.py_object

# Actually do the type query for the type our ctypes function returns:
SWIGTYPE_p_Foo = SWIG_Python_TypeQuery('Foo*')
print(hex(SWIGTYPE_p_Foo))

# Now the ctypes function itself that returns the type we want SWIG managed
fun1 = swig_module.fun1
fun1.argtypes = []
fun1.restype = ctypes.c_void_p

# Make the actual ctypes call we care about here
result = fun1()
print(hex(result))

# And then create a SwigPyObject for it from the void* return type
# Note that 0 means not owned by SWIG
sresult = SwigPyObject_New(result, SWIGTYPE_p_Foo, 0)
print(repr(sresult))

# This is how we jimmy it back into the this attribute of a SWIG type
class FooCtypesSwigInterop(object):
    def __init__(self, ptr):
        self.this = ptr
        self.__class__ = test.Foo

c = FooCtypesSwigInterop(sresult)

# Finally a usable SWIG object from the ctypes call 
print(c.msg)

所有这些都可以编译并使用:

swig3.0 -python -c++ -Wall test.i
g++ -shared -o _test.so test_wrap.cxx fun1.cc -Wall -Wextra -fPIC -I/usr/include/python2.7/ -std=c++11 -DSWIGRUNTIME=extern
LD_LIBRARY_PATH=.  python run.py

并给我们:

0x7fb6eccf29e0
0x7fb6eccf2640
<Swig Object of type 'Foo *' at 0x7fb6ee436720>
Hello

要解决SWIGRUNTIME定义为的问题,static您需要再执行一步。请使用调试符号或对已获得的SWIG二进制模块进行反向工程,但无法进行修改以找到我们需要的两个函数的地址,这些地址相对于已导出的符号未导出。然后,您可以使用它们来构造ctypes函数指针,而不用按名称查找它们。当然,购买/查找/重写SWIG模块或将缺少的功能添加到ctypes接口可能会更容易。

(最后,值得注意的是,尽管SWIG运行-builtin时需要进行一些实质性更改,但似乎在这里并不适用),才能使此答案有效。



 类似资料:
  • 我有一个库。它是通过SWIG进行包装的。我想创建一个插件来扩展它。插件需要一个来自已经包装的库的类来运行具有类似的东西。库从Java和c#中使用。现在这个插件也将从那里使用。库和插件是分开的dll的。如何高SWIG,我已经有那个类型在创建插件绑定时进行包装?

  • 问题内容: 我在C库中有这样的结构。DataFn中的函数指针指向静态函数。 。H 使用objdump,该表仅包含DATAFUNC和gcc的其他内容。 这在C语言中很好,在其中调用fn1就像DATAFUNC.fn1(…,…),但是这样的东西如何包装起来,以便可以在python w / ctypes中调用fn1呢? python示例 结果是 这是相似的,但是没有工厂功能。 问题答案: [Python

  • 我有一些C语言的代码,其中一个方法有一个函数指针作为参数。我正在尝试在我的Android应用程序中使用C代码。 我决定使用SWIG来完成生成所需java文件的所有工作。常规函数(没有函数指针作为参数的函数)一切都很好。 但是我不确定如何将我的JAVA方法作为回调传递给C函数。 下面是一个例子: 这是我的密码。h文件 这是我的密码。c文件 这是我的接口文件multiply-swigi 然后我运行以下

  • 我目前正在进行一个对象结构练习,在这个练习中我实现了一个简单的Twitter界面。我有一个TwitterAccount对象和一个Tweet对象。TwitterAccount对象有一个包含所有tweet的堆栈。在我的getTweet(index)方法中,我希望用getTweet(1)返回最新的tweet,用getTweet(2)...等等。 我通过创建一个临时tweet-stack,从临时stack

  • 问题内容: 我正在使用一个包含单个调用的.dll,该调用返回一个函数指针数组。GetMyApi()返回指向结构的指针,该结构是函数指针的数组。函数本身具有不同的单独输入和输出。到目前为止我尝试过的是: 我无法轻易更改的C代码: C中的代码: { int (__cdecl IsValidInt)(int i); int (__cdecl InvalidInt)(); int (__cdecl *Is

  • 问题内容: 我正在尝试为C库编写SWIG包装器,该包装器使用指向其结构中函数的指针。我不知道如何处理包含函数指针的结构。下面是一个简化的示例。 test.i: 样本会议: 有人知道是否有可能让 t.my_func(1) 返回2吗? 谢谢! 问题答案: 我找到了答案。如果我将函数指针声明为SWIG“成员函数”,则它似乎可以按预期工作: 会议: 我希望不需要编写任何特定于SWIG的自定义代码(我希望仅