web-dev-qa-db-ja.com

オブジェクト指向C ++コード用のCラッパーAPIの開発

既存のC++ APIをラップしてコアロジック(オブジェクト指向C++で記述)にアクセスする一連のC APIを開発したいと考えています。これは基本的に、C++ロジックを他の言語で使用できるようにする接着APIです。オブジェクト指向C++をCでラップすることに関する概念を紹介する優れたチュートリアル、書籍、またはベストプラクティスは何ですか?

73
theactiveactor

手作業で行うのはそれほど難しくありませんが、インターフェイスのサイズに依存します。私がそれを行ったのは、純粋なCコード内からC++ライブラリを使用できるようにすることでした。したがって、SWIGはあまり役に立ちませんでした。 (まあ、これを行うためにSWIGを使用できますが、私はSWIGの第一人者ではなく、簡単ではないように見えました)

私たちがやったことはすべて:

  1. すべてのオブジェクトは、Cで不透明なハンドルに渡されます。
  2. コンストラクターとデストラクターは純粋な関数でラップされます
  3. メンバー関数は純粋な関数です。
  4. 他のビルトインは、可能な場合はCの同等物にマップされます。

したがって、このようなクラス(C++ヘッダー)

class MyClass
{
  public:
  explicit MyClass( std::string & s );
  ~MyClass();
  int doSomething( int j );
}

次のようなCインターフェイスにマップします(Cヘッダー):

struct HMyClass; // An opaque type that we'll use as a handle
typedef struct HMyClass HMyClass;
HMyClass * myStruct_create( const char * s );
void myStruct_destroy( HMyClass * v );
int myStruct_doSomething( HMyClass * v, int i );

インターフェイスの実装は次のようになります(C++ソース)

#include "MyClass.h"

extern "C" 
{
  HMyClass * myStruct_create( const char * s )
  {
    return reinterpret_cast<HMyClass*>( new MyClass( s ) );
  }
  void myStruct_destroy( HMyClass * v )
  {
    delete reinterpret_cast<MyClass*>(v);
  }
  int myStruct_doSomething( HMyClass * v, int i )
  {
    return reinterpret_cast<MyClass*>(v)->doSomething(i);
  }
}

キャストが不要になるように、元のクラスから不透明なハンドルを派生させます。 (これは私の現在のコンパイラでは動作しないようでした)。 Cはクラスをサポートしていないため、ハンドルを構造体にする必要があります。

したがって、基本的なCインターフェイスが提供されます。例外処理を統合できる1つの方法を示すより完全な例が必要な場合は、githubでコードを試すことができます。 https://Gist.github.com/mikeando/5394166

楽しいのは、必要なすべてのC++ライブラリをより大きなライブラリに正しくリンクできるようにすることです。 gcc(またはclang)の場合、g ++を使用して最終リンクステージを実行するだけです。

63

マイケル・アンダーソンの答えは正しい軌道に乗っていると思うが、私のアプローチは異なるだろう。あなたはもう一つのことを心配する必要があります:例外。例外はC ABIの一部ではないため、C++コードを超えて例外をスローすることはできません。したがって、ヘッダーは次のようになります。

#ifdef __cplusplus
extern "C"
{
#endif
    void * myStruct_create( const char * s );
    void myStruct_destroy( void * v );
    int myStruct_doSomething( void * v, int i );
#ifdef __cplusplus
}
#endif

そして、ラッパーの.cppファイルは次のようになります。

void * myStruct_create( const char * s ) {
    MyStruct * ms = NULL;
    try { /* The constructor for std::string may throw */
        ms = new MyStruct(s);
    } catch (...) {}
    return static_cast<void*>( ms );
}

void myStruct_destroy( void * v ) {
    MyStruct * ms = static_cast<MyStruct*>(v);
    delete ms;
}

int myStruct_doSomething( void * v, int i ) {
    MyStruct * ms = static_cast<MyStruct*>(v);
    int ret_value = -1; /* Assuming that a negative value means error */
    try {
        ret_value = ms->doSomething(i);
    } catch (...) {}
    return ret_value;
}

さらに良いこと:MyStructの単一インスタンスとして必要なものすべてがわかっている場合は、APIに渡されるvoidポインターを処理するリスクを冒さないでください。代わりに次のようなことをしてください。

static MyStruct * _ms = NULL;

int myStruct_create( const char * s ) {
    int ret_value = -1; /* error */
    try { /* The constructor for std::string may throw */
        _ms = new MyStruct(s);
        ret_value = 0; /* success */
    } catch (...) {}
    return ret_value;
}

void myStruct_destroy() {
    if (_ms != NULL) {
        delete _ms;
    }
}

int myStruct_doSomething( int i ) {
    int ret_value = -1; /* Assuming that a negative value means error */
    if (_ms != NULL) {
        try {
            ret_value = _ms->doSomething(i);
        } catch (...) {}
    }
    return ret_value;
}

このAPIははるかに安全です。

しかし、Michaelが述べたように、リンクはかなり難しいかもしれません。

お役に立てれば

14
figurassa

C++コードをCに公開するのは難しくありません。Facadeデザインパターンを使用するだけです

私はあなたのC++コードがライブラリに組み込まれていると仮定しています、あなたがする必要があるのは、純粋なCヘッダファイルとともにあなたのライブラリへのファサードとしてC++ライブラリに1つのCモジュールを作ることだけです。 Cモジュールは関連するC++関数を呼び出します

これを行うと、Cアプリケーションとライブラリは、公開したC APIに完全にアクセスできるようになります。

たとえば、ここにサンプルのFacadeモジュールがあります

#include <libInterface.h>
#include <objectedOrientedCppStuff.h>

int doObjectOrientedStuff(int *arg1, int arg2, char *arg3) {
      Object obj = ObjectFactory->makeCppObj(arg3); // doing object oriented stuff here
      obj->doStuff(arg2);
      return obj->doMoreStuff(arg1);
   }

次に、このC関数をAPIとして公開し、心配することなくCライブラリとして自由に使用できます。

// file name "libIntrface.h"
extern int doObjectOrientedStuff(int *, int, char*);

これは明らかに不自然な例ですが、これはC++ライブラリをCに公開する最も簡単な方法です

9
hhafez

私はあなたが方向に関するいくつかのアイデアを得ることができ、そして/またはおそらく直接利用できると思うでしょう [〜#〜] swig [〜#〜] 。いくつかの例を検討することで、少なくとも、あるAPIを別のAPIにラップする際にどのようなことを考慮すべきかがわかると思います。運動は有益である可能性があります。

SWIGは、CおよびC++で記述されたプログラムをさまざまな高レベルプログラミング言語と接続するソフトウェア開発ツールです。 SWIGは、Perl、PHP、Python、Tcl、Rubyなどの一般的なスクリプト言語を含むさまざまな種類の言語で使用されます。サポートされる言語のリストには、C#、Common LISP(CLISP、Allegro CL、CFFI、UFFI)、Java、Lua、Modula-3、OCAML、Octave、Rなどの非スクリプト言語も含まれます。 Guile、MzScheme、Chicken)がサポートされています。 SWIGは、高レベルのインタープリターまたはコンパイルされたプログラミング環境、ユーザーインターフェイスを作成するため、およびC/C++ソフトウェアをテストおよびプロトタイピングするためのツールとして最も一般的に使用されます。 SWIGは、XMLおよびLISPのS式の形式で解析ツリーをエクスポートすることもできます。 SWIGは、営利目的および非営利目的で自由に使用、配布、および変更できます。

6
harschware

オブジェクトの概念をvoid *(C指向ライブラリでは不透明な型と呼ばれることが多い)に置き換えて、C++から知っているすべてを再利用するだけです。

4
Hassan Syed

SWIGを使用することが最良の答えだと思います。ホイールの再発明を避けるだけでなく、信頼性があり、問題を解決するよりも開発の継続性を促進します。

高周波の問題は、長期的な解決策で対処する必要があります。

2
danbo