web-dev-qa-db-ja.com

C ++クラスをCythonでラップするにはどうすればよいですか?

私はC++クラスを持っています。これは、1つの.ccpファイルと1つの.hファイルで構成されています。コンパイルします(c ++で正常に使用するmainメソッドを記述できます)。このクラスをCythonでラップして、Pythonで使用できるようにするにはどうすればよいですか?

ドキュメントを読みましたが、フォローしていません。彼らはcppファイルの生成について話します。ドキュメントをフォローしようとすると、既存のcppが吹き飛ばされてしまいます...

Pyxファイルに入れるとはどういう意味ですか?クラスの定義を聞いたことがありますが、どれくらいですか?パブリックメソッドだけですか?

.pxdファイルが必要ですか?このファイルがいつ必要なのか、不要なのかわかりません。

#python IRCチャネルでこれらの質問をしようとしましたが、答えが得られません。

22
Endophage

それで、たくさんの突っ込み、試行錯誤、悲鳴を上げて髪を引き裂いた後、私はついにこれを機能させることができました。ただし、最初に、C++をCに書き直す必要がありました。これには、すべてのstd::string変数をchar*に変換し、いくつかの長さを追跡するだけでした。

完了すると、.hファイルと.cファイルができました。 Cコードから1つの関数をPythonで利用できるようにしたかったのです。 CythonはCファイルを拡張子にコンパイルし、すべてのライブラリを一度にリンクできることがわかったので、私のsetup.pyから始めると、次のようになりました。

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

ext_modules=[
  Extension("myext",
    ["myext.pyx", "../stuff.c"],
    libraries=["ssl", "crypto"]
  )
]

setup(
  name = "myext",
  cmdclass = {"build_ext": build_ext},
  ext_modules = ext_modules
)

ご覧のとおり、拡張子の2番目の引数には、コンパイルする必要のあるすべてのファイルがリストされています。Cythonは、私が知る限り、ファイル拡張子に応じてファイルをコンパイルする方法を考え出します。ライブラリ配列は、リンクする必要があるものをCythonコンパイラに通知します(この場合、既存のPython libs)を介して直接模倣できないように見えるいくつかの暗号をラップしていました。

私のC関数を実際に.pyxファイルで使用できるようにするには、.pxdに小さなラッパーを記述します。私のmyext.pxdは次のようになりました:

cdef extern from "../stuff.h":
    char* myfunc(char* arg1, char* arg2, char* arg3)

次に、.pyxで、cimport宣言を使用してこの関数をインポートします。この関数は、他のPython関数:

cimport myext

def my_python_func(arg1, arg2, arg3):
    result = myext.myfunc(arg1, arg2, arg3)
    return result

これを(少なくともMacで)ビルドすると、.soを取得して、pythonにインポートし、.pyxから関数を実行できます。これを取得するためのより良い、より正しい方法があるかもしれません。すべてが機能していますが、それは経験によるものであり、これは私がうまくいくことができた最初の出会いでした。

更新:

Cythonをさらに使用した後、自分が何をしているのかがわかれば、C++と統合するのも非常に簡単であることがわかりました。 C++のstringを利用可能にするのは、pyx/pydのfrom libcpp.string cimport stringと同じくらい簡単です。 C++クラスの宣言は、次のように簡単です。

cdef extern from "MyCPPClass.h":
    cdef cppclass MyCPPClass:
        int foo;
        string bar;

確かに、基本的にクラスの.h定義をPythonic形式で再宣言する必要がありますが、それは、既に作成されたC++関数にアクセスするための少額の費用です。

5
Endophage

Cythonでさえ、一般的に[〜#〜] c [〜#〜]で使用しますが、C++コードも生成できます。コンパイル時に、--cplusフラグを追加します。

現在、クラスのラッパーの作成は単純であり、構造のラップと大差ありません。これは主にexternの宣言とは異なりますが、それほど大きな違いはありません。

mycppclass.hにクラスMyCppClassがあるとします。

cdef extern from "mycppclass.h":
    cppclass MyCppClass:
        int some_var

        MyCppClass(int, char*)
        void doStuff(void*)
        char* getStuff(int)

cdef class MyClass:

    # the public-modifier will make the attribute public for cython,
    # not for python. Maybe you need to access the internal C++ object from
    # outside of the class. If not, you better declare it as private by just
    # leaving out the `private` modifier.
    # ---- EDIT ------
    # Sorry, this statement is wrong. The `private` modifier would make it available to Python,
    # so the following line would cause an error es the Pointer to MyCppClass
    # couldn't be converted to a Python object.
    #>> cdef public MyCppClass* cobj
    # correct is:
    cdef MyCppClass* obj

    def __init__(self, int some_var, char* some_string):
        self.cobj = new MyCppClass(some_var, some_string)
        if self.cobj == NULL:
            raise MemoryError('Not enough memory.')

    def __del__(self):
        del self.cobj

    property some_var:
        def __get__(self):
            return self.cobj.some_var
        def __set__(self, int var):
            self.cobj.some_var = var

newキーワードは、--cplusフラグが設定されている場合にのみ使用可能であることに注意してください。それ以外の場合は、<stdlib.h>からmallocを外部で使用してください。

また、メソッドを呼び出すためにポインター(->)を逆参照する必要がないことにも注意してください。 Cythonはオブジェクトのタイプを追跡し、適合するものを適用します。

.pxdファイルは、宣言を実装から分離するため、または名前空間の衝突を回避するためのものです。 C++クラスのようなPythonラッパーという名前を付けたいと想像してみてください。 .pxdファイルにextern宣言を入れ、cimportを.pyxにpxdファイルに入れるだけです。

cimport my_pxd
cdef my_pxd.SomeExternedType obj

.pxdファイルに実装を書き込むことはできないことに注意してください。

16
Niklas R

Cythonは主にC開発用であり、C++をPython Boost.Python 。彼らの優れたドキュメントは、あなたがかなり早く始めるのに役立つはずです。

3
drrlvn