2016-12-16 77 views
1

我有用swig包裝的類的C++代碼。我無法修改代碼或包裝。 在python中,我使用ctypes指向所述C++類的實例。 如何在這個指針周圍創建一個swig包裝器?Python swig - 從ctypes指針創建swig包實例

我知道swig對象擁有內部指向包裝對象的'this'屬性,但我找不到一種方法將它設置爲我手頭上的指針。

感謝您的幫助!

回答

1

可以這樣做,但它的工作量很大,而且修復使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條件相同)。

在這一點上,我們已經足夠了,我們可以交換代理對象的這個屬性,並且如果我們能夠構建捐助者就可以完成。 (雖然會泄漏)。有兩種方法,我們可以使這個更好:

  1. 猴子補丁__init__test.Foo工作。這是最好的,如果你真的有%nodefaultctor的SWIG界面中你不想重新編譯:

    def patched_init(self, ptr): 
        self.this = ptr 
    test.Foo.__init__ = patched_init 
    
  2. 創建一個新的類,它只有一個__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一起運行,在這裏似乎不適用,但需要對此答案進行一些實質性更改才能發揮作用)。

+0

我可以在Windows或Linux所需的逆向工程上添加更多細節。可能還有另外一種方法來做這件事,這可能會更冒險,但可能使用ctypes本身來直接佈局SWIG Python對象本身。 – Flexo

+0

非常感謝,這是一個非常詳細和有用的答案。我今天大部分時間都在研究這個問題,並且有相似的概念。我仍然有一個swig_type_info小問題,我不能使用你的建議,因爲我有多個版本的庫。我選擇重新構建swig_type_info並使用基於包裝類名稱填充它(有點討厭,但似乎工作)。再一次感謝你的幫助。 – Shain

+0

是的,這也是一個很好的方法,但比使用查找幫助函數更困難。 – Flexo