私は、レガシーCコードが大量に含まれるプロジェクトに取り組んでいます。私たちは C++ で書き始めましたが、最終的にはレガシー コードも変換するつもりです。C と C++ がどのように相互作用するかについて、少し混乱しています。Cコードを C++ コンパイラでラップすると、 Cコードの名前が壊れないことは理解していますが、これをどのように実装すればよいのかよくわかりません。extern "C"
したがって、各Cヘッダーファイルの先頭(インクルードガードの後)には、
#ifdef __cplusplus
extern "C" {
#endif
そして一番下には
#ifdef __cplusplus
}
#endif
2 つの間には、すべての include、typedef、関数プロトタイプがあります。これを正しく理解しているかどうかを確認するために、いくつか質問があります。
Cヘッダー ファイル Bhをインクルードする C++ ファイル A.hh があり、さらに別のCヘッダー ファイル Ch をインクルードする場合、これはどのように機能しますか? コンパイラーが Bh にステップインすると、
__cplusplus
が定義されるため、コードは でラップされますextern "C"
(__cplusplus
このブロック内では は定義されません)。したがって、Ch にステップインすると、__cplusplus
は定義されず、コードは でラップされませんextern "C"
。これは正しいですか?コードの一部を でラップすることに何か問題がありますか
extern "C" { extern "C" { .. } }
? 2 番目は何をextern "C"
しますか?このラッパーは .c ファイルには適用せず、.h ファイルのみに適用します。では、関数にプロトタイプがない場合はどうなりますか? コンパイラはそれを C++ 関数と認識するのでしょうか?
また、 Cで記述されたサードパーティのコードも使用していますが、このコードにはこのようなラッパーがありません。そのライブラリのヘッダーをインクルードするときは常に、#include を で囲んでいます
extern "C"
。これは正しい対処方法でしょうか?最後に、この設定は良いアイデアでしょうか? 他に何かすべきことはありますか? 当面はCと C++ を混在させる予定なので、万全を期したいと思っています。
ベストアンサー1
extern "C"
コンパイラがコードを読み取る方法は実際には変わりません。コードが .c ファイルにある場合は C としてコンパイルされ、.cpp ファイルにある場合は C++ としてコンパイルされます (構成に奇妙な変更を加えない限り)。
リンケージに影響するものは何extern "C"
でしょうか。C++ 関数は、コンパイル時に名前がマングルされます。これにより、オーバーロードが可能になります。関数名は、パラメータの型と数に基づいて変更されるため、同じ名前の 2 つの関数が異なるシンボル名を持つことになります。
内のコードはextern "C"
C++ コードのままです。extern "C" ブロックで実行できる内容には制限がありますが、それらはすべてリンケージに関するものです。C リンケージで構築できない新しいシンボルを定義することはできません。つまり、たとえばクラスやテンプレートは定義できません。
extern "C"
ブロックはきれいにネストされます。また、領域extern "C++"
内に絶望的に閉じ込められていることに気付いた場合もextern "C"
、クリーンさの観点からはあまり良いアイデアではありません。
さて、番号の付いた質問に関して具体的に言えば、
1 に関して: __cplusplus はブロック内で定義されたままになりますextern "C"
。ただし、ブロックはきちんとネストされる必要があるため、これは問題ではありません。
2 について: __cplusplus は、C++ コンパイラーで実行されるすべてのコンパイル ユニットに対して定義されます。通常、これは .cpp ファイルと、その .cpp ファイルによってインクルードされるすべてのファイルを意味します。同じ .h (または .hh や .hpp など) が、異なるコンパイル ユニットにインクルードされている場合、異なる時点で C または C++ として解釈される可能性があります。.h ファイル内のプロトタイプが C シンボル名を参照するようにしたい場合、extern "C"
C++ として解釈されるときはそのようにする必要があり、C として解釈されるときはそのようにすべきではありません。extern "C"
そのため、#ifdef __cplusplus
チェックが必要です。
質問 3 にお答えします。プロトタイプのない関数は、ブロック内ではなく .cpp ファイル内にある場合、C++ リンケージを持ちますextern "C"
。ただし、プロトタイプがない場合、同じファイル内の他の関数によってのみ呼び出すことができるため、これは問題ありません。また、その関数が同じコンパイル ユニットの外部から呼び出される予定はないため、リンケージがどのようになっているかは一般に気にする必要はありません。
4については、その通りです。Cリンケージを持つコード(Cコンパイラでコンパイルされたコードなど)のヘッダーをインクルードする場合は、ヘッダーを含める必要があります。そうextern "C"
すれば、ライブラリにリンクできます。(そうしないと、リンカーは次のような名前の関数を探します。_Z1hic
void h(int, char)
5: この種の混合は を使用する一般的な理由でありextern "C"
、この方法で行うことに何の問題もないと思いますが、自分が何をしているのかを必ず理解してください。