我有用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
条件)。
至此,我们已经足够可以交换代理对象的此属性,并且只要能够构造供体就可以完成。(尽管那会泄漏)。我们可以通过两种方法使它更好:
__init__
里面的猴子补丁test.Foo
可以工作。如果您确实希望%nodefaultctor
在SWIG界面中进行重新编译,那么这是最好的选择:
def patched_init(self, ptr):
self.this = ptr
test.Foo.init = patched_init
创建一个仅具有的新类,然后在修改属性之前将__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的自定义代码(我希望仅