5

私はコールバックのための関数ポインタの構造体を使用するCライブラリを持っています。コールバックはCコードから呼び出されます。C関数ポインタにはどのような種類のC++関数を置くことができますか?

extern "C" { 
typedef struct callbacks_t { 
    void (*foo) (const char*); 
    int (*bar) (int); 
} callbacks_t; 
}// extern C 

C++の機能はどのような私は、Cライブラリから呼び出されるこれらの関数ポインタで安全に場所ことができますか?静的メンバー関数?完全に指定されたテンプレート関数?ノンキャプチャラムダ?

g ++と思われますが、CやC++の関数に異なる呼び出し規約や言語バインディングが使用されている場合は、安全性に疑問を呈します。

+1

_ "CとC++の関数に異なる呼び出し規約と言語バインディングが使用されている場合、安全に疑問があります。" - すべてがターゲット環境用に一貫してコンパイルされている場合、これらのような違いはありません。 –

+3

スタティックメンバ関数、特殊なテンプレート関数、非キャプチャラムダにextern "C"呼び出し規約を与えることはできません。しかし、実際にはコンパイラによって異なります。実際に問題として、主な問題は動作しないのではなく、一部のコンパイラが正式版について愚かな警告を発することがあるということです。 –

+2

@ Cheersandhth.-Alf、それは問題ではありません。 Cライブラリは、問題なくC++コンパイラによって名前が変更された関数を呼び出すことができるはずです。 –

答えて

3

私はの構造体を使用するCライブラリを持っていますコールバックのための関数ポインタ。コールバックはCコードから呼び出されます。

あなただけではなく、明示的にでき++ Cのいずれかの機能の種類の呼び出し規約の定義が存在しないので、明示的にC.

でサポートされていると理解されていると考えているバック渡すことができますのでACライブラリのみC.を理解C++関数を返す。 C関数(明示的にはextern "C"と宣言された関数)を渡し、互換性があることを保証するだけです。

すべての未定義の動作と同様に、これは正常なC++関数または静的メンバーを返すように動作するようです。しかし、すべての定義されていない動作のように動作することができます。あなたはちょうどそれを保証することはできません。私は安全にCライブラリから呼び出されるこれらの関数ポインタに置くことができますC++の機能はどのようなもの

extern "C" { 
    typedef struct callbacks_t { 
     void (*foo) (const char*); 
     int (*bar) (int); 
    } callbacks_t; 

    // Any functions you define in here. 
    // You can set as value to foo and bar. 

}// extern C 

スタティックメンバ関数?

いいえ。しかし、これは多くのプラットフォームで動作します。これは一般的なバグであり、一部のプラットフォームでは人々を苦しめます。

完全に指定されたテンプレート機能?

非捕捉ラムダ?一見、私は上記のすべてを使用することができます++

グラム、

はい。しかし、C++コンパイラで構築されたオブジェクトに渡していることを前提としています(これは正当なものです)。 C++コードは正しい呼び出し規約を使用します。問題は、これらのものをCライブラリに渡すときです(pthreadsが頭に浮かぶ)。

+0

何年も前、あなたは、Cの呼び出し規約とは異なる静的メンバーの呼び出し規則を持つC++コンパイラに遭遇したと言いました:http://stackoverflow.com/questions/1738313/c-using-class-method -as-a-function-pointer-type/1738425#comment-1618920私はそれがずっと前だと知っていますが、あなたがそれについて思い出すことができるかもしれない詳細は非常に興味深いと思います。 –

+1

これは私の時間のVERITAS(4ジョブ前)です。私は25のコンパイラ/ OSバリエーションでソフトウェアを構築したビルドシステムを担当していました。残念ながら私はそれがどれだったのか覚えていません。しかし、その経験は、基本的に、私が不特定のまたは未定義の行動に近いものから離れないようにするものです。 –

2

一般的に、キャストを使用しない限り、g ++を信頼する必要があります。

あなたが言及している関数型のどれもC言語から使用するためにエクスポートすることはできませんが、これはあなたが求めているものではありません。関数ポインタとして渡すことができる関数について質問しています。

あなたが渡すことができるものに答えるには、あなたが渡せないものを理解することがより建設的だと思います。引数リストに明示的に記載されていない追加の引数を必要とするものは渡すことはできません。

したがって、非静的メソッドはありません。彼らには暗黙の "this"が必要です。 Cはそれを渡すことを知らない。次に、コンパイラはあなたにあなたを許しません。

キャプチャラムダはありません。それらは実際のラムダ本体と暗黙の引数を必要とします。

あなたが渡すことができるのは、暗黙のコンテキストを必要としない関数ポインタです。実際のところ、先に進み、それらをリストしました:

  • ファンクションポインタ。テンプレートが完全に解決されている限り、それが標準機能であるかテンプレートであるかは関係ありません。これは問題ではありません。関数ポインタを使用する構文は、テンプレートを自動的に完全に解決します。
  • ノンキャプチャラムダ。これは、C++ 11がlambdaを導入したときに導入された特別な回避策です。これを行うことが可能なので、コンパイラはそれを実現するために必要な明示的な変換を行います。
  • 静的メソッド。静的なので暗黙的にthisが渡されることはないため、大丈夫です。

最後に拡大しています。多くのCコールバック・メカニズムは、関数ポインタとvoid * opaqを取得します。

class Something { 
    void callback() { 
    // Body goes here 
    } 

    static void exported_callback(void *opaq) { 
    static_cast<Something*>(opaq)->callback(); 
    } 
} 

そして実行します:

編集を追加する
Something something; 

register_callback(Something::exported_callback, &something); 

次はC++クラスでそれらを使用しての標準とかなり安全であるこの作品 唯一の理由は、C++で暗黙の引数が渡されない場合、呼び出し規約とC呼び出し規約は同一です。ネームマングリングには違いがありますが、ネームマングリングの唯一の目的はリンカが正しいファンクションのアドレスを見つけることができるためです。

このトリックでは、たとえばstdcallやpascalの呼び出し規約を想定してコールバックを試していたのですが、このスキームは面倒になります。

これは静的メソッド、ラムダ、およびテンプレート関数に固有のものではありません。たとえ標準的な機能であっても、そのような状況では失敗します。悲しいことに

、あなたはstdcallの型に関数ポインタを定義するとき、gccはあなたを無視します:

#define stdcall __attribute__((stdcall)) 
typedef stdcall void (*callback_type)(void *); 

結果で:

test.cpp:2:45: warning: ‘stdcall’ attribute ignored [-Wattributes] 
typedef stdcall void (*callback_type)(void *); 
+1

これは非常に実践的な見解です。そのように間違っているわけではありません。しかし、形式的には、呼び出し規約を明確な振る舞いと混在させることはできません。私はあなたも正式な言及をすべきだと思います。それについて知る価値がある。 –

+0

あなたは正しいです。私はそれに応じて私の答えを編集しました。 –

+1

これはすべて間違っています。関数ポインタを介してC++関数をCコードに渡すことはできません。これは、両方の言語の呼び出し規則が標準によって指定されていないためです。 C++からCに渡して動作を保証できるのはC関数(明示的に 'extern" C "')だけです。あなたのプラットフォーム上で完全に動作するように見える未定義の振る舞いを気に入らないかもしれません。 –

関連する問題