42

C++ アプリで C ライブラリを使用しようとしていますが、次のような状況に陥っています (C は知っていますが、C++ は初めてです)。C 側には、関数ポインターを引数として取る関数のコレクションがあります。C++ 側では、C 関数が必要とする関数ポインターと同じシグネチャを持つファンクターを持つオブジェクトがあります。C 関数に渡す関数ポインタとして C++ ファンクタを使用する方法はありますか?

4

10 に答える 10

57

C コード (または C++ コード) への関数ポインターとして、C++ ファンクター オブジェクトへのポインターを直接渡すことはできません。

さらに、C コードにコールバックを移植可能に渡すには、少なくともextern "C"非メンバー関数として宣言する必要があります。少なくとも、一部の API では特定の関数呼び出し規約が必要なため、追加の宣言修飾子が必要です。

多くの環境では、C と C++ の呼び出し規約は同じで、名前マングリングのみが異なるため、グローバル関数または静的メンバーはすべて機能します。operator()ただし、呼び出しを通常の関数でラップする必要があります。

  • ファンクタに状態がない場合 (形式的な要件などを満たすためだけのオブジェクトです):

    class MyFunctor {
      // no state
     public:
      MyFunctor();
      int operator()(SomeType &param) 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 &param) 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" 関数に渡すことです。これはコードでできることを制限し、醜いですが動作します。

于 2009-12-03T17:01:57.927 に答える
7

私はグーグルを使ってこの「宝石」を見つけました。どうやら可能ですが、お勧めしません。サンプル ソース コードへの直接リンク。

于 2009-12-03T14:11:29.237 に答える
5

いいえ、もちろんです。C 関数のシグネチャは、引数を関数として受け取ります。

void f(void (*func)())
{
  func(); // Only void f1(), void F2(), ....
}

ファンクターを使用したすべてのトリックは、テンプレート関数で使用されます。

template<class Func>
void f (Func func)
{
    func(); // Any functor
}
于 2009-12-03T14:08:48.667 に答える
3

C++ で記述された AC コールバック関数は、関数として宣言する必要がありますextern "C"。そのため、ファンクタを直接使用することはできません。そのコールバックとして使用するある種のラッパー関数を作成し、そのラッパーにファンクターを呼び出させる必要があります。もちろん、コールバック プロトコルには、関数にコンテキストを渡す何らかの方法が必要です。そうしないと、タスクが非常に複雑になります。ほとんどのコールバック スキームにはコンテキストを渡す方法がありますが、私はそうでない脳死状態のものをいくつか使用しました。

詳細については、この回答を参照してください (コールバックがextern "C"単なる静的メンバー関数ではなく、コールバックでなければならないという逸話的な証拠については、コメントを参照してください)。

于 2009-12-03T16:16:58.830 に答える
2

私はあなたができるとは思わない.operator()関数オブジェクトは実際にはメンバー関数であり、Cはそれらについて何も知らない.

使用できるのは、無料の C++ 関数、またはクラスの静的関数です。

于 2009-12-03T14:08:21.083 に答える
2

GCC では、メンバー関数ポインターをプレーンな関数ポインターに変換できます (プレーンな関数ポインターによって呼び出される関数の最初の引数は ですthis)。

マニュアルのそれぞれのリンクを確認してください。

これには-Wno-pmf-conversions、明らかに非標準の機能に対するそれぞれの警告を黙らせるために、フラグが必要です。C スタイルのライブラリと C++ スタイルのプログラミングとのインターフェイスに非常に便利です。メンバー関数ポインターが定数の場合、コードを生成する必要さえありません。API はとにかくその引数の順序を使用します。

すでにファンクターがある場合、その方法でファンクターをフラット化することは、その をフラット化することを意味する可能性が高くoperator()、ファンクター クラス ポインター自体を最初の引数として呼び出す必要がある関数が得られます。これは必ずしもそれほど役立つわけではありませんが、少なくともCリンケージがあります。

しかし、少なくともファンクターを使用していない場合、これは役に立ち、 from のナンセンスな C リンケージ置換を提供しstd::mem_fnます<functional>

于 2014-09-01T10:43:48.220 に答える
0

関数ポインタコールバックを受け取る多くのCAPIには、ユーザー状態のvoid*パラメータがあります。それらの1つを持っている場合は、幸運です。ユーザーデータをある種の参照またはキーとして扱うexterm C関数を使用して、ファンクターを検索し、実行することができます。

そうでなければ、いいえ。

于 2009-12-03T16:22:43.293 に答える
0

これが静的メソッドかインスタンス メソッドかによって異なります。静的メソッドの場合は、関数を className::functionName として渡すことができます。インスタンス メソッドの場合は、明らかに特定のインスタンスに関連付ける必要があるため、かなり複雑になります。ただし、C# などでデリゲートを使用する場合と同じ方法では実行できません。

私が見つけた最良の方法は、オブジェクトのインスタンスと関数ポインターでインスタンス化される保持クラスを作成することです。保持クラスは関数を直接呼び出すことができます。

于 2009-12-03T14:08:24.423 に答える
0

C++ファンクターにはメンバー関数()であるオーバーロードされた演算子があり、メンバー関数ポインターが必要になるため、私はノーと言います。これは、クラスのインスタンスなしでは呼び出すことができないため、通常の C 関数ポインターとはまったく異なるデータ型です。通常の関数または静的メンバー関数を C ライブラリに渡す必要があります。オーバーロードされた演算子は静的にできないため、静的にすることはできません。C ライブラリに通常の非メンバー関数または静的メンバー関数を渡す必要があり、そこから C++ ファンクターを呼び出すことができます。()

于 2009-12-03T14:09:16.947 に答える
0

うーん、関数オブジェクトをラップする無料のテンプレート関数を書くことができるかもしれません。それらがすべて同じ署名を持っている場合、これは機能するはずです。このように(テストされていません):

template<class T>
int function_wrapper(int a, int b) {
    T function_object_instance;

    return funcion_object_instance( a, b );
}

これは、2 つの int を取り、int を返すすべての関数に当てはまります。

于 2009-12-03T14:10:06.697 に答える