我有用swig包裝的類的C++代碼。我無法修改代碼或包裝。 在python中,我使用ctypes指向所述C++類的實例。 如何在這個指針周圍創建一個swig包裝器?Python swig - 從ctypes指針創建swig包實例
我知道swig對象擁有內部指向包裝對象的'this'屬性,但我找不到一種方法將它設置爲我手頭上的指針。
感謝您的幫助!
我有用swig包裝的類的C++代碼。我無法修改代碼或包裝。 在python中,我使用ctypes指向所述C++類的實例。 如何在這個指針周圍創建一個swig包裝器?Python swig - 從ctypes指針創建swig包實例
我知道swig對象擁有內部指向包裝對象的'this'屬性,但我找不到一種方法將它設置爲我手頭上的指針。
感謝您的幫助!
您可以這樣做,但它的工作量很大,而且修復使ctypes或SWIG接口完全可用而不是強制可互換性的根本問題要簡單得多。 (還值得注意的是,從SWIG創建一個ctypes對象比從事你想做的事情更容易,也就是從ctypes創建一個SWIG對象)。
爲了說明這一點,我已經創建,我們將與SWIG包裹下面的頭文件:
%module test
%{
#include "test.h"
%}
// Prove I'm not cheating here - we can't even instantiate this class
%nodefaultctor;
%include "test.h"
我還添加了:
struct Foo {
double d;
char msg[20];
};
然後我用下面的接口把它包我們可以從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;
}
你猜abou這個SWIG包裝類的this
屬性是一個很好的起點,但它不像改變那樣簡單 - 您插入的對象的類型必須與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模塊的這個屬性進行交換的東西。
爲了打電話,我們通常會撥打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__
其修改__class__
屬性,使用前設置this
的屬性,而不是:
class FooCtypesSwigInterop(object):
def __init__(self, ptr):
self.this = ptr
self.__class__ = test.Foo
此選項,當你不想要打破test.Foo
現有__init__
實現最有意義。
隨着中說,我們現在可以像這樣實現我們最初的目標:
這一切都編譯並與工作原理:
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
一起運行,在這裏似乎不適用,但需要對此答案進行一些實質性更改才能發揮作用)。
我可以在Windows或Linux所需的逆向工程上添加更多細節。可能還有另外一種方法來做這件事,這可能會更冒險,但可能使用ctypes本身來直接佈局SWIG Python對象本身。 – Flexo
非常感謝,這是一個非常詳細和有用的答案。我今天大部分時間都在研究這個問題,並且有相似的概念。我仍然有一個swig_type_info小問題,我不能使用你的建議,因爲我有多個版本的庫。我選擇重新構建swig_type_info並使用基於包裝類名稱填充它(有點討厭,但似乎工作)。再一次感謝你的幫助。 – Shain
是的,這也是一個很好的方法,但比使用查找幫助函數更困難。 – Flexo