2014-01-27 39 views
10

我有這個文件foobar.h痛飲在類型表的工作原理,但argout不

class Foobar { 
public: void method(int arg[2]) {}; 
}; 

編譯SWIG接口的Python後,如果我嘗試運行此方法在Python它說

TypeError: in method 'Foobar_method', argument 2 of type 'int [2]' 

當然。所以我寫這個SWIG類型圖:

%typemap(in) int [2] {} 

當我編譯這個,Python運行這個方法沒有抱怨。所以我認爲,我知道如何編寫一個類型圖。

但是,如果我改變類型映射到argout

%typemap(argout) int [2] {} 

現在,巨蟒追溯到以前的錯誤。

我只是直接從SWIG手冊做到這一點,這應該沒有錯誤,就像in typemap一樣。

我在做什麼錯?

回答

15

怎麼了?

簡而言之,它不是這些類型映射的任一個或命題。

您錯過的關鍵信息是多種類型地圖合作包裝單個功能的方式。

argout獲取插入生成的包裝該調用發生了。這是您以合理的方式將(經過修改)輸入複製回Python的機會。

這並沒有解決在調用之前如何創建並傳入參數的問題。

您可以通過檢查通過這個接口生成的代碼,看到這個很清楚:

%module test 

%{ 
#include "test.h" 
%} 

%typemap(in) int[2] { 
    // "In" typemap goes here 
} 

%typemap(argout) int[2] { 
    // "argout" goes here 
} 

%include "test.h" 

,當test.h就是你們的榜樣產生:

// ... <snip> 
    arg1 = reinterpret_cast< Foobar * >(argp1); 
    { 
    // "In" typemap goes here 
    } 
    (arg1)->method(arg2); 
    resultobj = SWIG_Py_Void(); 
    { 
    // "argout" goes here 
    } 
    return resultobj; 
    // ... <snip> 

在這些typemaps的目標「in」typemap是在調用之前使arg2成爲一個合理的值,「argout」typemap應該對調用後的值做一些合理的處理(如果需要,可以通過更改返回值)。


什麼應該在類型映射?

通常,對於像這樣的函數,您可能希望輸入類型圖從一些Python輸入中填充臨時數組。

要做到這一點,我們要首先需要改變輸入類型映射,要求SWIG來爲我們創建一個臨時數組:

重要的是,我們得到SWIG爲我們做到這一點,使用的符號在類型之後添加括號而不是將其添加到類型映射的主體中,以便範圍對於變量是正確的。 (如果我們沒有從「argout」類型映射仍然無法訪問臨時文件,並且在調用本身進行之前會被清除)。

%typemap(in) int[2] (int temp[2]) { 
    // If we defined the temporary here then it would be out of scope too early. 
    // "In" typemap goes here 
} 

SWIG生成的代碼現在包含了我們的臨時數組,因此我們希望使用Python C API來迭代我們的輸入。這可能看起來像:

%typemap(in) int[2] (int temp[2]) { 
    // "In" typemap goes here: 
    for (Py_ssize_t i = 0; i < PyList_Size($input); ++i) { 
    assert(i < sizeof temp/sizeof *temp); // Do something smarter 
    temp[i] = PyInt_AsLong(PyList_GetItem($input, i)); // Handle errors 
    } 
    $1 = temp; // Use the temporary as our input 
} 

(我們也可以選擇使用Python iterator protocol,而不是如果我們優先考慮)。

如果我們現在編譯並運行這個接口,我們已經有足夠的可以傳入一個輸入,但是沒有回來。在我們編寫「argout」類型映射之前,還有一件事情在生成的代碼中仍然需要注意。我們生成的代碼中的臨時數組實際上看起來像int temp2[2]。這並不是錯誤,SWIG默認已將變量重命名爲參數位置派生的變量,以便允許將相同的類型映射多次應用於單個函數調用,如果需要的話每個參數應用一次。

在我的「argout」typemap中,我將返回另一個Python列表和新值。這不是長久以來唯一的理智選擇 - 如果您願意,還有其他選擇。

%typemap(argout) int[2] { 
    // "argout" goes here: 
    PyObject *list = PyList_New(2); 
    for (size_t i = 0; i < 2; ++i) { 
    PyList_SetItem(list, i, PyInt_FromLong(temp$argnum[i])); 
    } 
    $result = list; 
} 

兩個本說明的點是,首先,我們需要寫temp$argnum明確以匹配SWIG做了我們的臨時陣列上;其次,我們正在使用$result作爲輸出的轉變。

純粹的輸出參數

我們經常有,這只是用於輸出,沒有輸入參數。對於這些,強制Python用戶提供一個即將被忽略的列表是沒有意義的。

我們可以通過修改「in」類型映射表,使用numinputs=0來表示不需要Python輸入。您也需要在這裏適當地初始化臨時設備。類型映射現在變成簡單:

%typemap(in,numinputs=0) int[2] (int temp[2]) { 
    // "In" typemap goes here: 
    memset(temp, 0, sizeof temp); 
    $1 = temp; 
} 

所以,現在的「在」類型表實際上並不需要從Python的任何輸入的。它可以被看作簡單地準備對本地電話的輸入。

通過旁白,您可以避免使用SWIG應用的名稱(通過使用相同功能多次使用相同類型映射的代價,或使用另一個具有名稱衝突的類型映射的代價) 「in」類型映射中的noblock=1。儘管如此,我不會推薦。

非固定數組長度?

最後值得注意的是,我們可以將所有這些類型映射寫得更通用,並且適用於任何固定大小的數組。要做到這一點,我們在類型映射匹配改2「任何」,然後使用$1_dim0而不是2類型映射的身體裏,所以在那結束整個界面就變成了:

%module test 

%{ 
#include "test.h" 
%} 

%typemap(in,numinputs=0) int[ANY] (int temp[$1_dim0]) { 
    // "In" typemap goes here: 
    memset(temp, 0, sizeof temp); 
    $1 = temp; 
} 

%typemap(argout) int[ANY] { 
    // "argout" goes here: 
    PyObject *list = PyList_New($1_dim0); 
    for (size_t i = 0; i < $1_dim0; ++i) { 
    PyList_SetItem(list, i, PyInt_FromLong(temp$argnum[i])); 
    } 
    $result = list; 
} 

%include "test.h" 
+0

哇,這是驚人的。我必須去我的另一臺電腦,我正在做這件事,然後我會採用你所說的話。我確實發現,爲「argout」typemap添加一個「in」typemap,部分解決了這個問題--SIGG接受了這個參數,但尚未正確處理它,但至少最初的錯誤消息不再存在了。 –

+0

你的建議很好,非常有啓發性,但是,如果我照你的要求做,那麼我還有一個問題,我的包裝是非法的。請參閱其他問題http://stackoverflow.com/questions/21422148/why-does-swig-make-illegal-wrapper-from-in-and-argout-typemaps –

+0

對於最後的argout我想做一個元組返回值,所以而不是'$ result = list'我添加了'$ result = SWIG_Python_AppendOutput($ result,list);'。 – eresonance