2016-07-28 74 views
0

我正在研究一個項目,我有一個可用的大型C++代碼庫,我想用cython包裝並在Python中提供。在這種情況下,我正面臨着一些C++函數返回Vector對象或簡單對象(具有多個屬性)的情況。我想將這個對象返回給Python,以便可以訪問它的值。Cython - 將由C++函數返回的C++(向量和非向量)對象公開給Python

做這件事,我幾乎完全以下這篇文章:How to expose a function returning a C++ object to Python without copying the object?

我有一個非常類似的要求。請參閱移動在上述論壇中實施/使用的構造。

以下是我的代碼,我想實現的簡單(非矢量情況下):

test_header.pxd

from libcpp.vector cimport vector 
from libcpp.string cimport string 
from libcpp.map cimport map 

cdef extern from "main_class.h": 

    cdef cppclass main_class: 
     int ID1 
     double ID2 


cdef extern from "class2.h": 

    cdef cppclass class2: 
     class2() except + 
     class2(const double& T) except + 
     void Add(const main_class& ev) 
     const vector[main_class]& GetEvs() 

#Process Class 
cdef extern from "Pclass.h": 

    cdef cppclass Pclass: 
     Pclass(const unsigned& n, const unsigned& dims) except + 
     unsigned GetDims()   
     double processNext(const class2& data, const unsigned& num_iter) 


cdef extern from "main_algo.h": 

    #TODO: Check if inheritance works correctly, virtual functions, objects, std::vector 
    cdef cppclass main_algo: 
     main_algo(const unsigned& dims) except + 
     main_class getNext(Pclass& pr, const class2& d) 

test.pyx

from header cimport main_class, class2, Pclass, main_algo 
from libcpp.vector cimport vector 
from libcpp.string cimport string 
from libcpp.map cimport map 


cdef extern from "<utility>": 
    vector[class2]&& move(vector[class2]&&) 
    main_class&& move(main_class&&) 

cdef class main_class_2: 
    cdef main_class* thisptr 
    cdef main_class_3 evs 

    def __cinit__(self,main_class_3 evs): 
     self.evs = evs 
     self.thisptr = &evs.evs 

cdef class main_class_3: 

    cdef main_class evs 

    cdef move_from(self, main_class&& move_this): 
     self.evs = move(move_this) 


cdef class implAlgo: 

    cdef: 
     main_algo *_thisptr 

    def __cinit__(implAlgo self): 
     self._thisptr = NULL 

    def __init__(implAlgo self, unsigned dims): 
     self._thisptr = new main_algo(dims) 

    def __dealloc__(implAlgo self): 
     if self._thisptr != NULL: 
      del self._thisptr 


    cdef int _check_alive(implAlgo self) except -1: 
     if self._thisptr == NULL: 
      raise RuntimeError("Wrapped C++ object is deleted") 
     else: 
      return 0  

    cdef getNext(implAlgo self, Pclass& p, const class2& s): 
     self._check_alive() 
     cdef main_class evs = self._thisptr.getNext(p, sq) 
     retval = main_class_3() 
     retval.move_from(move(evs)) 

     return retval 

在這裏,類別main_algo實施方法getNext()返回ns類main_class的對象。

從test.pyx中,我想將此對象返回到可以訪問其值的純python文件。

當我嘗試編譯上面的代碼時,我在幾處使用該方法的地方得到了多個 實例,並且出現了像')'或'*'這樣的不同令牌的類似錯誤。一些示例錯誤是:

sources.cpp:5121:70: error: expected primary-expression before ‘*’ token 
    __pyx_vtable_11Cython_test_7sources_main_class_3.move_from = (PyObject *(*)(struct __pyx_obj_11Cython_test_7sources_main_class_3 *, main_class &&))__pyx_f_11Cython_test_7sources_8main_class_3_move_from; 
                    ^
sources.cpp:5121:73: error: expected primary-expression before ‘)’ token 
    __pyx_vtable_11Cython_test_7sources_main_class_3.move_from = (PyObject *(*)(struct __pyx_obj_11Cython_test_7sources_main_class_3 *, main_class &&))__pyx_f_11Cython_test_7sources_8main_class_3_move_from; 
                     ^
sources.cpp:5121:75: error: expected primary-expression before ‘struct’ 
    __pyx_vtable_11Cython_test_7sources_main_class_3.move_from = (PyObject *(*)(struct __pyx_obj_11Cython_test_7sources_main_class_3 *, main_class &&))__pyx_f_11Cython_test_7sources_8main_class_3_move_from; 
                     ^
sources.cpp:5121:133: error: expected primary-expression before ‘&&’ token 
    __pyx_vtable_11Cython_test_7sources_main_class_3.move_from = (PyObject *(*)(struct __pyx_obj_11Cython_test_7sources_main_class_3 *, main_class &&))__pyx_f_11Cython_test_7sources_8main_class_3_move_from; 

但所有這些令牌與我創建的C++對象移動到蟒蛇的對象。任何其他聲明都沒有錯誤。

有人可以幫助告訴我我錯在哪裏嗎?

+0

這可能是有用的:http://intermediate-and-advanced-software-carpentry.readthedocs.io/en/latest/c++-wrapping.html – Alexander

+0

你確定它使用C++ 11模式(或更高版本)編譯時。對於gcc,你可能會添加像'extra_link_args = [' - std = C++ 11']'到'setup.py' http://stackoverflow.com/questions/1676384/how-to-pass-flag-to- gcc-in-python-setup-py-script – DavidW

+0

如果一個cython方法能夠返回一個內存視圖,那麼就不會複製(也許這是[question](http://stackoverflow.com/questions/36357024/wrapping) -stdarray-in-cython-and-exposeing-memory-views-views)) –

回答

0

如果C++方法返回指向對象的指針(或者如果可以傳輸數據的所有權),並且可以訪問底層數據,則內存視圖應該可用。

以下示例適用於整數爲vectorint)。類test_view具有對包含數據和視圖對象的對象的引用,因此這兩者的生命週期應該相同。

test.pxd

from libcpp.vector cimport vector 

cdef public class test [object cTest, type cTest]: 
    cdef vector[int] * test 

test.pyx

from libcpp.vector cimport vector 

class test_view: 
    def __init__(self, test obj): 
     cdef ssize_t N = obj.test[0].size() 
     cdef int[::1] v = <int[:N]>obj.test[0].data() 

     self._obj = obj 
     self._view = v 

    def get(self): 
     return self._view 

cdef class test: 
    def __cinit__(self): 
     self.test = NULL 

    def __init__(self, seq): 
     self.test = new vector[int]() 

     cdef int i 

     for i in seq: 
      self.test[0].push_back(i) 

    def __dealloc__(self): 
     print("Dealloc") 
     if self.test != NULL: 
      del self.test 

    # Expose size method of std::vector 
    def size(self): 
     return self.test[0].size() 

    def view(self): 
     # return an instance of test_view, object should stay alive 
     # for the duration of test_view instance 
     return test_view(self) 

設置。PY

from distutils.core import setup 
from distutils.extension import Extension 
from Cython.Distutils import build_ext 

ext_modules = [ Extension('test', ['test.pyx'], language='c++') ] 

setup(name = 'test', 
     ext_modules = ext_modules, 
     cmdclass = {'build_ext': build_ext}) 

run.py

import test 

a = test.test([1, 2, 3, 4, 5, 6]) 
v = a.view() 

print('Try to cause deallocation') 
a = None 

print('Print view') 
for i in v.get(): 
    print('\t{:-12d}'.format(i))  

nv = v._view 
v = None 

print('Not good, should print junk numbers') 
for i in nv: 
    print('\t{:-12d}'.format(i)) 

當執行run.py

Try to cause deallocation 
Print view 
        1 
        2 
        3 
        4 
        5 
        6 
Dealloc 
Not good, should print junk numbers 
      11966656 
        0 
        3 
        4 
        5 
        6 
0

一個複製您的問題一個很簡單的例子(當不當編譯)並演示如何通過更改編譯選項來修復它。

cdef extern from "<utility>" namespace "std": 
    int&& move(int&&) 

cdef class someclass: 
    cdef int i 

    cdef move_from(self, int&& i): 
     self.i = move(i) 

(註定義move當我添加namespace "std",這是缺少在你的代碼,可能是因爲我錯過了它的答案,你在此基礎上的代碼)。

setup.py如下

from distutils.core import setup 
from distutils.extension import Extension 
from Cython.Distutils import build_ext 

setup(
    cmdclass = {'build_ext': build_ext}, 
    ext_modules = [Extension('test_move', 
       sources=['test_move.pyx'], 
       extra_compile_args=['-std=c++11'], 
       language='c++')] 

如果我刪除「extra_compile_args」然後我看你的報告(因爲編譯器是假設您正在使用的不支持右值舊的C++標準同樣的錯誤引用)。如果我像上面那樣編譯它,那麼它編譯正確。

這並不意味着代碼中沒有其他問題。它比展示問題所需的時間長得多,並且不完整(它至少依賴於3個您不提供的C++頭文件)。因此它是不可能測試的。