C++ アプリで C ライブラリを使用しようとしていますが、次のような状況に陥っています (C は知っていますが、C++ は初めてです)。C 側には、関数ポインターを引数として取る関数のコレクションがあります。C++ 側では、C 関数が必要とする関数ポインターと同じシグネチャを持つファンクターを持つオブジェクトがあります。C 関数に渡す関数ポインタとして C++ ファンクタを使用する方法はありますか?
10 に答える
C コード (または C++ コード) への関数ポインターとして、C++ ファンクター オブジェクトへのポインターを直接渡すことはできません。
さらに、C コードにコールバックを移植可能に渡すには、少なくともextern "C"
非メンバー関数として宣言する必要があります。少なくとも、一部の API では特定の関数呼び出し規約が必要なため、追加の宣言修飾子が必要です。
多くの環境では、C と C++ の呼び出し規約は同じで、名前マングリングのみが異なるため、グローバル関数または静的メンバーはすべて機能します。operator()
ただし、呼び出しを通常の関数でラップする必要があります。
ファンクタに状態がない場合 (形式的な要件などを満たすためだけのオブジェクトです):
class MyFunctor { // no state public: MyFunctor(); int operator()(SomeType ¶m) const; }
ファンクターを作成し、その operator() を実行する通常の extern "C" 関数を作成できます。
extern "C" int MyFunctorInC(SomeType *param) { static MyFunctor my_functor; return my_functor(*param); }
あなたのファンクターに状態がある場合、例えば:
class MyFunctor { // Some fields here; public: MyFunctor(/* some parameters to set state */); int operator()(SomeType ¶m) const; // + some methods to retrieve result. }
C コールバック関数は、ある種のユーザー状態パラメーター (通常は void *) を取ります。
void MyAlgorithmInC(SomeType *arr, int (*fun)(SomeType *, void *), void *user_state);
状態パラメーターをファンクター オブジェクトにキャストする通常の extern "C" 関数を作成できます。
extern "C" int MyFunctorInC(SomeType *param, void *user_state) { MyFunctor *my_functor = (MyFunctor *)user_state; return (*my_functor)(*param); }
次のように使用します。
MyFunctor my_functor(/* setup parameters */); MyAlgorithmInC(input_data, MyFunctorInC, &my_functor);
それ以外の場合の唯一の通常の方法 (「実行時にマシン コードを生成しない」などの通常の方法) は、静的 (グローバル) またはスレッド ローカル ストレージを使用して、ファンクターを extern "C" 関数に渡すことです。これはコードでできることを制限し、醜いですが動作します。
いいえ、もちろんです。C 関数のシグネチャは、引数を関数として受け取ります。
void f(void (*func)())
{
func(); // Only void f1(), void F2(), ....
}
ファンクターを使用したすべてのトリックは、テンプレート関数で使用されます。
template<class Func>
void f (Func func)
{
func(); // Any functor
}
C++ で記述された AC コールバック関数は、関数として宣言する必要がありますextern "C"
。そのため、ファンクタを直接使用することはできません。そのコールバックとして使用するある種のラッパー関数を作成し、そのラッパーにファンクターを呼び出させる必要があります。もちろん、コールバック プロトコルには、関数にコンテキストを渡す何らかの方法が必要です。そうしないと、タスクが非常に複雑になります。ほとんどのコールバック スキームにはコンテキストを渡す方法がありますが、私はそうでない脳死状態のものをいくつか使用しました。
詳細については、この回答を参照してください (コールバックがextern "C"
単なる静的メンバー関数ではなく、コールバックでなければならないという逸話的な証拠については、コメントを参照してください)。
私はあなたができるとは思わない.operator()
関数オブジェクトは実際にはメンバー関数であり、Cはそれらについて何も知らない.
使用できるのは、無料の C++ 関数、またはクラスの静的関数です。
GCC では、メンバー関数ポインターをプレーンな関数ポインターに変換できます (プレーンな関数ポインターによって呼び出される関数の最初の引数は ですthis
)。
マニュアルのそれぞれのリンクを確認してください。
これには-Wno-pmf-conversions
、明らかに非標準の機能に対するそれぞれの警告を黙らせるために、フラグが必要です。C スタイルのライブラリと C++ スタイルのプログラミングとのインターフェイスに非常に便利です。メンバー関数ポインターが定数の場合、コードを生成する必要さえありません。API はとにかくその引数の順序を使用します。
すでにファンクターがある場合、その方法でファンクターをフラット化することは、その をフラット化することを意味する可能性が高くoperator()
、ファンクター クラス ポインター自体を最初の引数として呼び出す必要がある関数が得られます。これは必ずしもそれほど役立つわけではありませんが、少なくともCリンケージがあります。
しかし、少なくともファンクターを使用していない場合、これは役に立ち、 from のナンセンスな C リンケージ置換を提供しstd::mem_fn
ます<functional>
。
関数ポインタコールバックを受け取る多くのCAPIには、ユーザー状態のvoid*パラメータがあります。それらの1つを持っている場合は、幸運です。ユーザーデータをある種の参照またはキーとして扱うexterm C関数を使用して、ファンクターを検索し、実行することができます。
そうでなければ、いいえ。
これが静的メソッドかインスタンス メソッドかによって異なります。静的メソッドの場合は、関数を className::functionName として渡すことができます。インスタンス メソッドの場合は、明らかに特定のインスタンスに関連付ける必要があるため、かなり複雑になります。ただし、C# などでデリゲートを使用する場合と同じ方法では実行できません。
私が見つけた最良の方法は、オブジェクトのインスタンスと関数ポインターでインスタンス化される保持クラスを作成することです。保持クラスは関数を直接呼び出すことができます。
C++ファンクターにはメンバー関数()
であるオーバーロードされた演算子があり、メンバー関数ポインターが必要になるため、私はノーと言います。これは、クラスのインスタンスなしでは呼び出すことができないため、通常の C 関数ポインターとはまったく異なるデータ型です。通常の関数または静的メンバー関数を C ライブラリに渡す必要があります。オーバーロードされた演算子は静的にできないため、静的にすることはできません。C ライブラリに通常の非メンバー関数または静的メンバー関数を渡す必要があり、そこから C++ ファンクターを呼び出すことができます。()
うーん、関数オブジェクトをラップする無料のテンプレート関数を書くことができるかもしれません。それらがすべて同じ署名を持っている場合、これは機能するはずです。このように(テストされていません):
template<class T>
int function_wrapper(int a, int b) {
T function_object_instance;
return funcion_object_instance( a, b );
}
これは、2 つの int を取り、int を返すすべての関数に当てはまります。