2012-04-26 67 views
1

我定義了一個struct a { }。我的C函數通過引用獲取一個數組struct a,然後填充它中的數據。所以它接受一個參數struct a **。我想使用SWIG接口從Python中調用此函數。有沒有辦法做到這一點?包裝一個需要指針數組的函數

回答

4

你可以用SWIG和Python來做到這一點。我已經設置了以下test.h文件deomstrate:

struct a { 
    int val; 
}; 

static void populate(struct a **list) { 
    int count = 0; 
    // Terminate when NULL entry found 
    while (*list) { 
    (*list)->val = count++; 
    ++list; 
    } 
} 

的填入功能需要你描述一個struct a**,假設列表爲空終止,並做一些事情到列表中的每個元素。

我選擇公開此對Python的辦法是爲取整數,即列表的大小一起工作,然後返回結果列表的功能,因爲這是映射的語義的最好方式「通過論點返回」在我看來是成立的。

我建立一個基本模塊的文件:

%module test 

%{ 
#include "test.h" 
%} 

然後加入基於指定給Python函數的大小準備的輸入表類型表:

%typemap(in,numinputs=1) struct a ** (int len=0) { 
    len = (int)PyInt_AsLong($input); 
    $1 = malloc(sizeof(struct a*)*(len+1)); 
    $1[len] = NULL; 
    for (int i = 0; i < len; ++i) { 
    $1[i] = malloc(sizeof(struct a)); 
    } 
} 

基本上它將該參數作爲整數,爲指針數組分配內存,然後爲元素自己指向的對象分配內存。 (我分別分配它們,以便SWIG/Python可以單獨處理每個項目的引用計數,這比爲所有元素分配一個塊更簡單)

隨着該類型地圖寫入,我添加了另一個類型地圖,它負責同時調用函數的結果,並將其轉換回成PyList:

%typemap(argout) struct a ** { 
    // Push into PyList for return 
    $result = PyList_New(len$argnum); 
    for (int i = 0; i < len$argnum; ++i) { 
    PyObject *element = SWIG_NewPointerObj(SWIG_as_voidptr($1[i]), SWIGTYPE_p_a, SWIG_POINTER_OWN); 
    PyList_SET_ITEM($result, i, element); 
    } 
} 

這需要我們在在類型映射創建的len變量的優勢,但因爲它的NULL結束,我們可能只是重新計數長度。然後我們使用包裝對象(由SWIG/Python擁有)填充每個struct a的Python列表中的每個項目,我們將其傳遞給populate函數。請注意,如果類型名稱不同,則需要將SWIGTYPE_p_a更改爲合適的SWIGTYPE。 (這些可以從生成的包裝源中找到)。

最後,我們需要去分配我們不得不對C面列表中的內存,搭配:

%typemap(freearg) struct a ** { 
    free($1); 
} 

,然後問SWIG包住頭文件本身,使用這些typemaps:

%include "test.h" 

我編這個有:

 
swig -python -Wall test.i 
gcc -Wall -Wextra test_wrap.c -I/usr/include/python2.6 -o _test.so -std=c99 -shared 

,然後運行以下Python來檢查:

import test 

r=test.populate(100) 
print r[0].val 

幾點需要從這個例子說明:

  • 沒有錯誤檢查,例如test.populate("Hello world")會做壞事,PyList_New可能會失敗,如果沒有失敗,我們需要釋放的各個元素,不只是名單
  • 如果內存所有權的語義是不同的,那麼你就需要改變SWIG_POINTER_OWN適當
  • 如果你不希望有對Python的interfa指定列表的大小(例如,它是已知的或以其他方式修正的),您可以將numinputs=1更改爲0,並相應地在typemap中設置0​​。
  • 如果你的函數將列表和一個整數,它的長度,而不是使用NULL結束列表,你可以這樣延伸到multi-argument typemap

編譯/運行這個例子所需的所有代碼都包含在這個答案,但如果你想在一個方便的壓縮包,我也把它on my site。即使該鏈接應該突破,答案仍然有效。

+0

感謝您的回答。我希望兩天前我已經找到了這個問題/答案。我不得不開發類似的東西,並提出與您幾乎相同的解決方案。另一個教訓是,大多數技術問題已經在之前解決了...... – 2014-08-07 09:27:12

相關問題