2017-11-02 22 views
0

私はPythonとCythonでC++で書かれたlibの機能を拡張しようとしています。私は自分のライブラリに必須のC++でクラスMyClassを持っています。そして私はPythonのラッパークラスPyMyClassをたくさん使っています。だから私は関数を(PyMyClassを引数として)C++から使いたいと思っています。どのように私はこれを達成することができますか?組み込み型ではなくC++からCythonを呼び出す

私はそれをこのようなものを想像:

cdef public my_func(MyClass class): 
    *cast MyClass to PyMyClass* 
    other_py_func(PyMyClass) 

myclass.h

namespace classes { 
    class MyClass 
    { 

    public: 
     explicit MyClass(int x, int y); 
     int sum(); 
    private: 
     int a; 
     int b; 
    }; 
} 

myclass.cpp

MyClass::MyClass(int x, int y) 
{ 
    a = x; 
    b = y; 
} 

MyClass::sum() 
{ 
    return a + b 
} 

pymyclass.pyx

cdef extern from "myclass.h" namespace "classes": 
    cdef cppclass MyClass: 
     MyClass(int x, int y) except + 
     int sum() 

    cdef public my_func(MyClass var): 
     print "It is python" 

CDEFクラスPyMyClass(オブジェクト): CDEFのMyClassの* c_class

def __cinit__(self, int x, int y): 
     self.c_class = new MyClass(x,y) 

    def __dealoc__(self): 
     del self.c_class.sum() 

    def sum(self): 
     return self.c_class.sum() 

私は私がMyClassのにget_x()、get_y()とset_x(int型)、set_y(int)を追加し、ちょうどすべてコピーすることができますことを理解MyClassからPyMyClassまでのフィールド。しかし、PyMyClassは既にMyClassへのポインタを持っています。 MyClassインスタンスのアドレスをPyMyClassフィールドに割り当てることは可能ですか?私はこのような何か意味:Cython拡張を作成するには pyclass = PyMyClass(クラス) other_py_func(pyclass)

+0

あなたがしようとしていることは明確ではありません。 _それを入れるだけですか?_何を入れていますか?どこ?あなたはPythonからPyMyClassを呼び出そうとしていますか?それはどのように呼び出されますか?代わりにC++からCythonでPyMyClassをビルドすることを呼びかけていますか? – danny

+0

@danny、編集した質問があります。今より具体的になってほしい – Zhu

+0

これは参考になるかもしれません:https://stackoverflow.com/questions/33677231/how-to-expose-a-function-returning-ac-object-to-python-without-copying-the-ob/33679481#33679481 – DavidW

答えて

0

cdef class PyMyClass(object): 
     cdef MyClass *c_class 

     def __cinit__(self, int x, int y): 
      self.c_class = MyClass(x,y) 

     def __cinit__(self, void * ptr): 
      self.c_class = (Antenna *)ptr 

     def __dealoc__(self): 
      del self.c_class.sum() 

     def sum(self): 
      return self.c_class.sum() 

を次にCython機能は、パブリックmy_func(MyClassの*クラス)CDEF のようになります。クラスを既存のC++ポインタから削除するには、ファクトリ関数を使用します。例えば

:あなたは任意のデータをコピーまたは移動することなく、そのポインタを介して、C++のクラスにアクセスすることができます

cdef class PyMyClass: 
    cdef MyClass *c_class 

    def __cinit__(self): 
     # Set pointer to null on object init 
     self.c_class = NULL 

    def __dealoc__(self): 
     if self.c_class is not NULL: 
      del self.c_class 

    def sum(self): 
     return self.c_class.sum() 

cdef object PyMyClass_factory(MyClass *ptr): 
    cdef PyMyClass py_obj = PyMyClass() 
    # Set extension pointer to existing C++ class ptr 
    py_obj.c_class = ptr 
    return py_obj 

PyMyClass_factoryは、既存のC++ポインターからPython PyMyClassオブジェクトを返す場所であればどこでも使用できます。

Pythonは関数オーバーロード(またはCython)をサポートしていないので、2つの整数から新しいC++クラスを作成する2つの__cinit__シグネチャ(引数なし、1つ)を持つことはできません。

あなたがそれを行うにはCython、新工場の機能を経由して新しいC++クラスを作成する場合の例上記のとおりに必要とされています。それは次のようになり

:また

cdef object PyMyClass_factory_new(int x, int y): 
    cdef MyClass cpp_obj = new MyClass(x, y) 
    cdef PyMyClass py_obj = PyMyClass() 
    py_obj.c_class = &cpp_obj 
    return py_obj 

cdef public my_func(MyClass var)は必要ありません。 publicは、Cython関数をC/C++で利用できるようにするためのものであり、C++で同じことを意味するわけではありません。 my_funcをPython/Cython以外で使用する予定がない場合は、publicは必要ありません。

この関数をPython経由で使用する場合は、defまたはcpdefでなく、cdefでなければなりません。

最後に、明白な理由から、外部定義をnogilと定義し、C++関数を呼び出すときにはwith nogilを使用することをお勧めします。たとえば、

cdef extern from "myclass.h" namespace "classes" nogil: 
    <..> 

cdef class PyMyClass: 
    <..> 

    def sum(self): 
     cdef int ret 
     with nogil: 
      ret = self.c_class.sum() 
     return ret 
+0

素晴らしい!それはまさに私が必要とするものです! – Zhu

+0

はい、my_funcをpublicとして定義しました。なぜなら、C++からPython関数へのアクセスを使用したいからです。 PyMyClass_factoryを別の.pyxファイルで定義すると、なぜ「MyClass *」を「Pythonオブジェクト」に変換できないのですか? pymyclass.pyxからすべてのC++ MyClass宣言をコピーしても、 – Zhu

+1

定義をpxdファイルに入れて、それをcimportする必要があります。あなたが得ているエラーはPyMyClass_factoryの定義を知らないので、それが標準のPython関数だと仮定しているからです。 – DavidW

関連する問題