web-dev-qa-db-ja.com

ctypesでC ++クラスを使用する方法は?

私はctypesを使い始めたばかりで、Ctypesを使用してpythonからdllファイルにエクスポートしたC++クラスを使用したいと考えています。C++コードは次のようになります。

class MyClass {
  public:
    int test();
...

このクラスを含む.dllファイルを作成してから、ctypesを使用してpythonに.dllファイルをロードします。MyClass型のオブジェクトを作成して、そのテスト関数を呼び出す方法は?それはctypesでも可能ですか?あるいは、SWIGまたはBoost.Pythonの使用を検討しますが、ctypesは小さなプロジェクトにとって最も簡単なオプションのようです。

51
jörg

簡単な話は、CのようにC++の標準的なバイナリインターフェイスがないことです。名前のマングリングとライブラリ関数呼び出し間のスタックの処理方法が異なるため、同じC++ダイナミックライブラリに対して異なるコンパイラが異なるバイナリを出力します。

そのため、残念ながら、一般的にはC++ライブラリにアクセスする移植可能な方法はありません。しかし、一度に1つのコンパイラーであれば、問題はありません。

このブログ投稿 には、これが現在機能しない理由の概要も記載されています。 C++ 0xがリリースされた後、C++用の標準のABIができるでしょうか?それまでは、おそらくPythonのctypesを介してC++クラスにアクセスする方法はないでしょう。

36
Mark Rushakoff

Boost.Python(おそらく、C++クラスをpythonクラス)に1対1でマッピングする必要がある大規模プロジェクト向けのより使いやすいソリューションです)に加えて、C++側でCインターフェイスを提供できます。多くのソリューションの1つであるため、独自のトレードオフがありますが、この手法に慣れていない人のために紹介します。完全な開示のために、このアプローチでは、C++をpythonにインターフェースすることはありません。 C++ to C to Python。以下に、C++コンパイラのextern "c"機能の一般的な考え方を示すための要件を満たす例を示しました。

//YourFile.cpp (compiled into a .dll or .so file)
#include <new> //For std::nothrow
//Either include a header defining your class, or define it here. 

extern "C"  //Tells the compile to use C-linkage for the next scope.
{
    //Note: The interface this linkage region needs to use C only.  
    void * CreateInstanceOfClass( void )
    {
        // Note: Inside the function body, I can use C++. 
        return new(std::nothrow) MyClass;
    }

    //Thanks Chris. 
    void DeleteInstanceOfClass (void *ptr)
    {
         delete(std::nothrow) ptr; 
    }

    int CallMemberTest(void *ptr)
    {

        // Note: A downside here is the lack of type safety. 
        // You could always internally(in the C++ library) save a reference to all 
        // pointers created of type MyClass and verify it is an element in that
        //structure. 
        //
        // Per comments with Andre, we should avoid throwing exceptions.  
        try
        {
            MyClass * ref = reinterpret_cast<MyClass *>(ptr);
            return ref->Test();
        }
        catch(...)
        {
           return -1; //assuming -1 is an error condition. 
        }
    }

} //End C linkage scope.

このコードをコンパイルするには

gcc -shared -o test.so test.cpp
#creates test.so in your current working directory.

pythonコードでは、次のようなことができます(2.7の対話型プロンプトを表示):

>>> from ctypes import cdll
>>> stdc=cdll.LoadLibrary("libc.so.6") # or similar to load c library
>>> stdcpp=cdll.LoadLibrary("libstdc++.so.6") # or similar to load c++ library
>>> myLib=cdll.LoadLibrary("/path/to/test.so")
>>> spam = myLib.CreateInstanceOfClass()
>>> spam
[outputs the pointer address of the element]
>>> value=CallMemberTest(spam)
[does whatever Test does to the spam reference of the object] 

Boost.Pythonは内部で同様のことを行うと確信していますが、おそらく低レベルの概念を理解することが役立つでしょう。 C++ライブラリの機能にアクセスしようとしていて、1対1のマッピングが不要な場合は、この方法にもっと興奮します。

C/C++インタラクションの詳細については、Sunの次のページをご覧ください。 http://dsc.Sun.com/solaris/articles/mixing.html#cpp_from_c

39
AudaAero

AudaAeroによる回答 は非常に優れていますが、完全ではありません(少なくとも私にとっては)。

私のシステム(Debian Stretch x64 with GCC and G ++ 6.3.0、Python 3.5.3)にアクセスするメンバー関数を呼び出すとすぐにセグメンテーション違反が発生します)クラスのメンバー値。ラッパーの64ビットでコーディングされたvoid *ポインターがPythonの32ビットで表されていることをポインター値をstdoutに出力することで診断しました。メンバー関数ラッパー。

私が見つけた解決策は変更することです:

spam = myLib.CreateInstanceOfClass()

Class_ctor_wrapper = myLib.CreateInstanceOfClass
Class_ctor_wrapper.restype = c_void_p
spam = c_void_p(Class_ctor_wrapper())

そのため、戻り値の型をc_void_p(デフォルトはint)andに設定してから、c_void_pオブジェクト(単なる整数ではない)を作成します。

コメントを書けたらよかったのに、27 repポイントがまだ足りない。

5

これは、PythonでC_typesでcおよびc ++を使用する方法についての簡単な説明です。 Python用C++でDLL/SOを記述する方法

0
ifryed

AudaAero'sGabriel Devillers を拡張すると、クラスオブジェクトインスタンスの作成は次のように完了します:stdc=c_void_p(cdll.LoadLibrary("libc.so.6")) using ctypesc_void_pデータ型は、Python内のクラスオブジェクトポインターの適切な表現を保証します。

また、dllのメモリ管理がdllによって処理されることを確認します(dllに割り当てられたメモリは、pythonではなく、dllでも割り当て解除される必要があります)!

0
Elektrone Motyo