2016-07-12 15 views
2

私はC++ライブラリをCでラップすることに取り組んでいます.C++ライブラリはデータベースサーバのライブラリです。シリアライズされたデータを渡すためのラッパークラスを使用します。私はCで直接そのクラスを使用することはできませんので、私は、このようなCのコードで使用することができます構造体定義された:スタックに不完全な型を割り当てよう

extern "C" { 
    typedef struct Hazelcast_Data_t Hazelcast_Data_t; 

    Hazelcast_Data_t *stringToData(char *str); 
    void freeData(Hazelcast_Data_t *d); 
} 
(これは私のCラッパーのクライアントが含めているラッパーである) include/c-wrapper/c-wrapper.h

impl.pp

extern "C" struct Hazelcast_Data_t { 
    hazelcast::client::serialization::pimpl::Data data; // this is the C++ class 
}; 

Hazelcast_Data_t *stringToData(char *str) { 
    Data d = serializer.serialize(str); 

    Hazelcast_Data_t *dataStruct = new Hazelcast_Data_t(); 
    dataStruct->data = d; 

    return dataStruct; 
} 

... 

さて、これは私のCライブラリのクライアントのみtypedef struct Hazelcast_Data_t Hazelcast_Data_t;を見て、動作します。問題は、前述のタイプがスタックに割り当てることができないこと、のような私はこのようなAPIを提供したいと考えている場合:

// this is what I want to achieve, but Hazelcast_Data_t is an incomplete type 
#include <include/c-wrapper/c-wrapper.h> 

int main() { 
    char *str = "BLA"; 
    Hazelcast_Data_t d; 
    stringToData(str, &d); 
} 

コンパイラはHazelcast_Data_tが不完全な型であるというエラーがスローされます。私はまだHazelcast_Data_tのスタック割り当て参照を直列化関数に渡すことができるAPIを提供したいと考えていますが、Hazelcast_Data_tにはC++クラスへのポインタがあるため、これはほとんど不可能です。しかし、スタックに割り当てられた参照を渡すオプションがあると、Cライブラリのクライアントのコードが大幅に簡略化されます(new構造を解放する必要はありません)。

Hazelcast_Data_tタイプを再定義してCで使用できるようにしても、スタックに割り当てられるのは何とかできますか?

+0

Hazelcast_Data_tには実際にC++クラスへのポインタが含まれていますか、または実際のC++構造体またはクラスオブジェクトが含まれていますか?実際にはポインタだけの場合、ポインタのサイズは不完全な型であってもよく知られているので、多くの問題はありません(システムによっては、常に4または8バイトです)。 Cコンパイラが理解しやすいようにポインタを(void *)として保存しなければならないかもしれませんが、その外ではなぜ問題になるのかわかりません。 –

+0

@JeremyFriesner Hazelcast_Data_tには実際のデータが含まれています。また、voidポインタを使うこともできると思っていましたが、 'Hazelcast_Data_t data'を' void * 'にキャストすることはできず、ローカルのスタック変数であるためアドレスを取得できません。 – Max

+0

閉じるように投票した人:私の質問で何が尋ねられているのか明確でない理由を理解するのを助けてください。私はまだC/C++プログラミングの新人で、問題を解消するために間違った言葉を使ったかもしれません。理由が「不明」で終わると投票することは、まったく役に立たない。 – Max

答えて

3

あなたが考えているハックの大部分は、構造体が作成されたときにCが含まれているオブジェクトのC++コンストラクタを呼び出さず、構造体がなくなったときにC++デストラクタを呼び出さないため、範囲。それを動作させるには、正しいサイズのバッファとinit関数内のそのバッファに新しいバッファを入れるためにstructを必要とし、完了したらそのバッファ上のデストラクタを呼び出します。これは、コードを意味します(何も投げていないと仮定して - あなたは、例外処理や翻訳を追加する必要があり、その場合、...)のようになります

struct wrapper { 
    char buffer[SIZE_OF_CXX_CLASS]; 
} 

void wrapper_init() { 
    new (buffer) Wrapped(); 
} 

void wrapper_destroy() { 
    ((Wrapper*)buffer)->~Wrapper(); 
} 

{ 
    struct wrapper wrapped; 
    wrapper_init(&wrapped); 
    // ... use it ... 
    wrapper_destroy(&wrapped); 
} 

を使用すると、未定義のbehvaiourの土地に入るwrapper_initすべてを呼び出すことを忘れてしまった場合。 wrapper_destroyに電話することを忘れた場合、UBも手に入ると思います。

しかし、これはあなたの呼び出し元がinitとdestroy関数を呼び出すことを強いるので、ポインタを使うことで得られる利益はほとんどありません。私は、ポインターではなく構造体の使用がAPIユーザーに対して、初期化は簡単でなければならず、破壊が不要であることを示唆していると主張しています。私。 APIの利用者として、私はいつもがヒープイディオム

にそれを割り当てるに固執したい(あなたのような)これが不可能な場合には

{ 
    struct wrapper wrapped = WRAPPER_INIT; //Trivial initialisaton macro 
    // .. use it .. 
    // No need to do anything it is a trivial object. 
} 

を行うことができることを期待したいです

{ 
    struct wrapper* wrapped = wrapper_create(); 
    // ... use it ... 
    wrapper_destroy(wrapped); 
} 
+0

私はライブラリがデータベースとのやりとりに使用されるので、多くのヒープ割り当てを避けることができると思っていました。しかし、結局のところ、私はAPIも安全で使いやすいものにしたいと考えています。 – Max

+1

これらのオブジェクトの割り当てが後でボトルネックになることが判明した場合は、プールを使用するなどの異なる割り当てスキームを使用して、そのコストの大半を最適化できます(wrapper_create内)。 –

+0

非常に良い洞察力、私は同意します。私は今、単純なバージョンを構築し、実際に問題が発生するかどうかを確認します。マイケルに感謝しています。時にはプロジェクトに取り組んでいるだけで、特定のアイデアにこだわらないのは少し難しいです。 – Max

1

ヘッダーファイルに構造体の定義を指定する必要があります。そうすることで、クライアントはスタックに割り当てるスペースの量を知ることができます。しかし、これは、extern "C"によって公開されないC++クラスの基礎となる表現が難しい場合に発生します。

解決策は、実際のクラスではなくC++クラスへのポインタです。ポインターは同じサイズであるため、C++についての知識がなくても、Cクライアントで動作します。ヘッダ

typedef struct Hazelcast_Data_t { 
     void *data 
} Hazelcast_Data_t 

そして、C++ファイルにあなたはこのポインタを経由してC++クラスにアクセスするためにstatic_castを使用することができますでこのように

+0

これは元の不完全な型へのポインタを渡すことと変わりありません。 –

0

C++型を格納するのに十分なサイズの配列を単純に含むラッパー構造体を作成します。プレースメント - 新しいC++タイプ

おそらく、SIZEOF_HAZELCAST_TとALIGNOF_HAZELCAST_Tが適切に定義されたCヘッダーファイルを生成する小さなC++実行可能ファイルをビルドする必要があります。

+0

私はこの方法についてはわかりませんが、それをやりたいとは思っていませんが、SIZEOF_HAZELCAST_TとALIGNOF_HAZELCAST_Tを適切に使ってCヘッダファイルを生成する小さなC++実行可能ファイルを構築する必要があるかもしれませんそのような実行可能ファイルはどのように見えますか?参照するクラスは比較的簡単に見えますが、実際にはそのサイズを取得する方法はわかりません。https://github.com/hazelcast/hazelcast-cpp-client/blob/master/hazelcast/include/hazelcast/client/ serialization/pimpl/Data.h – Max

+0

いずれにしても、Michael Andersonが指摘しているように、私はdestroy関数が必要です。そうしないと、CPPクラスのデストラクタは呼び出されません。 – Max

+1

あなたのクラスにデストラクタが必要な場合は、ユーザーがクリーンアップ機能を何らかの方法で呼び出さなければならないので、この考え方全体を忘れてしまうかもしれません。 –

関連する問題