2016-01-27 78 views
0

這似乎是一個應該有明顯答案的問題,但由於某種原因,我在網上找不到任何示例。使用Cython擴展模塊來包裝std :: vector - 如何編程__setitem __()方法?

我正在使用Cython在Python類中包裝C++對象的向量。我也有一個已經編碼的C++類的Cython包裝器。我可以得到幾種方法,如__len__(),__getitem__()resize()正常工作,但__setitem__()方法給我的問題。

爲了簡單起見,我用int s的矢量編碼了一個小例子。我想,如果我能得到這個代碼的工作,那麼我可以建立在這個,以獲得我的C++類的解決方案。

MyPyModule.pyx

# distutils: language = c++ 

from libcpp.vector cimport vector 
from cython.operator cimport dereference as deref 

cdef class MyArray: 
    cdef vector[int]* thisptr 
    def __cinit__(self): 
     self.thisptr = new vector[int]() 

    def __dealloc__(self): 
     del self.thisptr 

    def __len__(self): 
     return self.thisptr.size() 

    def __getitem__(self, size_t key): 
     return self.thisptr.at(key) 

    def resize(self, size_t newsize): 
     self.thisptr.resize(newsize) 

    def __setitem__(self, size_t key, int value): 
     # Attempt 1: 
     # self.thisptr.at(key) = value 

     # Attempt 2: 
     # cdef int* itemptr = &(self.thisptr.at(key)) 
     # itemptr[0] = value 

     # Attempt 3: 
     # (self.thisptr)[key] = value 

     # Attempt 4: 
     self[key] = value 

當我試圖嘗試使用1〜cythonize,我得到了錯誤Cannot assign to or delete this。當我試圖嘗試2,.cpp文件被創建,但編譯器抱怨說:

error: cannot convert ‘__Pyx_FakeReference<int>*’ to ‘int*’ in assignment 
    __pyx_v_itemptr = (&__pyx_t_1); 

嘗試次數3,用Cython不會因爲建在Cannot assign type 'int' to 'vector[int]'文件。 (當我用C++對象而不是int來嘗試這種風格時,它抱怨是因爲我有一個左值的引用。)嘗試4編譯,但是當我嘗試使用它時,出現段錯誤。

Cython docs表示不支持返回一個引用作爲左值,這很好 - 但我該如何解決它,以便我可以爲我的矢量元素之一分配一個新值?

回答

2

有通過指針來訪問向量兩種方式,

def __setitem__(self, size_t key, int value): 
    deref(self.thisptr)[key] = value 
    # or 
    # self.thisptr[0][key] = value 

用Cython平移那些兩種情況如下:

Python: deref(self.thisptr)[key] = value 
C++: ((*__pyx_v_self->thisptr)[__pyx_v_key]) = __pyx_v_value; 

Python: self.thisptr[0][key] = value 
C++: ((__pyx_v_self->thisptr[0])[__pyx_v_key]) = __pyx_v_value; 

這是等價的,即訪問相同的矢量對象。

1

而不是試圖處理來自用Cython代碼的指針,可以讓用Cython本身爲你做它:

cdef class MyArray: 
    cdef vector[int] thisptr 

    def __len__(self): 
     return self.thisptr.size() 

    def __getitem__(self, size_t key): 
     return self.thisptr[key] 

    def __setitem__(self, size_t key, int value): 
     self.thisptr[key] = value 

    def resize(self, size_t newsize): 
     self.thisptr.resize(newsize) 

是否有使用這種方法的任何問題?

+0

你的方法做的工作,但我有,因爲在大多數情況下指針工作,我將用C++創建對象並將其封裝在Python中使用。不過謝謝你的建議! –

+0

然後我認爲這個鏈接可以幫助:http://docs.cython.org/src/userguide/wrapping_CPlusPlus.html#add-public-attributes –

0

我已經接受J.J.哈卡拉的回答(非常感謝!)。我調整了該方法,包括一個徹頭徹尾的邊界檢查,因爲它使用了[]運營商,而不是at()方法:

cdef class MyArray: 
    (....) 
    def __setitem__(self, size_t key, int value): 
     if key < self.thisptr.size(): 
      deref(self.thisptr)[key] = value 
     else: 
      raise IndexError("Index is out of range.")