質問

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

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

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

役に立ちましたか?

解決

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

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

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

他のヒント

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

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

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

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

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)に設定する と、c_void_pオブジェクト(単なる整数ではない)を作成するという2つのことが欠けていました。

コメントを書けたらよかったのに、27担当者がまだ不足しています。

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

AudaAeroの拡張および Gabriel Devillers の回答クラスオブジェクトインスタンスの作成を完了するには: stdc = c_void_p(cdll.LoadLibrary(&quot; libc.so.6&quot;)) ctypes c_void_p データ型を使用すると、Python内のクラスオブジェクトポインターの適切な表現が保証されます。

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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top