私は、既存のC ++ APIをラップアラウンドしてコアロジック(オブジェクト指向C ++で記述)にアクセスする一連のCAPIを開発しようとしています。これは基本的に、C ++ロジックを他の言語で使用できるようにする接着剤APIになります。オブジェクト指向C ++の周りにCをラップすることに関連する概念を紹介するいくつかの良いチュートリアル、本、またはベストプラクティスは何ですか?
私は、既存のC ++ APIをラップアラウンドしてコアロジック(オブジェクト指向C ++で記述)にアクセスする一連のCAPIを開発しようとしています。これは基本的に、C ++ロジックを他の言語で使用できるようにする接着剤APIになります。オブジェクト指向C ++の周りにCをラップすることに関連する概念を紹介するいくつかの良いチュートリアル、本、またはベストプラクティスは何ですか?
回答:
これは手作業で行うのはそれほど難しいことではありませんが、インターフェイスのサイズによって異なります。私がそれを行ったのは、純粋なCコード内からC ++ライブラリを使用できるようにすることでした。したがって、SWIGはあまり役に立ちませんでした。(おそらくSWIGを使用してこれを行うことができますが、私はSWIGの第一人者ではなく、自明ではないように見えました)
私たちがやったのは:
したがって、このようなクラス(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 ++を使用して最終リンクステージを実行することを意味します。
myStruct_destroy
とmyStruct_doSomething
関数には2つのタイプミスがあります。する必要がありますreinterpret_cast<MyClass*>(v)
。
Michael Andersonの答えは正しい方向に進んでいると思いますが、私のアプローチは異なります。あなたはもう一つのことを心配しなければなりません:例外。例外はCABIの一部ではないため、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ははるかに安全です。
しかし、マイケルが述べたように、リンクはかなりトリッキーになるかもしれません。
お役に立てれば
C ++コードをCに公開することは難しくありません。ファサード・デザイン・パターンを使用するだけです。
C ++コードがライブラリに組み込まれていると仮定します。必要なのは、純粋なCヘッダーファイルとともに、ライブラリのファサードとしてC ++ライブラリに1つのCモジュールを作成することだけです。Cモジュールは関連するC ++関数を呼び出します
これを行うと、Cアプリケーションとライブラリは公開したCAPIに完全にアクセスできるようになります。
たとえば、これはサンプルのファサードモジュールです
#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に公開する最も簡単な方法です。
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などの非スクリプト言語も含まれています。また、いくつかの解釈およびコンパイルされたScheme実装( Guile、MzScheme、Chicken)がサポートされています。SWIGは、高レベルのインタープリター型またはコンパイル済みのプログラミング環境、ユーザーインターフェイスを作成するため、およびC / C ++ソフトウェアをテストおよびプロトタイピングするためのツールとして最も一般的に使用されます。SWIGは、解析ツリーをXMLおよびLispのS式の形式でエクスポートすることもできます。SWIGは自由に使用、配布、