既存のCコードベースをC++コンパイラでコンパイルする場合、どのような問題が発生すると予想できますか?たとえば、列挙型の値に整数を割り当てると、C++では失敗しますが、Cでは(少し厄介な場合は)正当であると思います。
すべてのCファイルをextern C { ... }
でラップしない場合、予想外の場所で名前のマングリングが発生しますか?私が本当にこれをしてはいけない理由はありますか?
背景として、Cで書かれた非常に大きなコードベースがあります。数年の間、C++を介して自然に発生すること(homebrewe継承など)を実行するためにフープを飛ばしてきました。 C++への移行を開始したいと思いますが、段階的に行います。 CORBAに似たフレームワークをサポートし、C++が提供するより自然なアプローチを利用するためにモジュールをリファクタリングします。
このようなことを一度やったことがある。問題の主な原因は、あなたが疑ったように、C++は型に関してより厳しいということでした。 void *が他の型のポインターと混在しているキャストを追加する必要があります。メモリの割り当てのように:
Foo *foo;
foo = malloc(sizeof(*foo));
上記は典型的なCコードですが、C++ではキャストが必要です。
Foo *foo;
foo = (Foo*)malloc(sizeof(*foo));
「class」、「and」、「bool」、「catch」、「delete」、「explicit」、「mutable」、「namespace」、「new」、「operator」など、C++には新しい予約語があります。 「or」、「private」、「protected」、「friend」など。これらは、たとえば変数名として使用できません。
上記は、C++コンパイラを使用して古いCコードをコンパイルするときの最も一般的な問題です。非互換性の完全なリストについては、 ISO CとISO C++の間の非互換性 を参照してください。
名前のマングリングについても尋ねます。 extern "C"ラッパーがない場合、C++コンパイラーはシンボルを破壊します。 C++コンパイラをonly使用し、ライブラリからシンボルをプルするためにdlsym()などに依存しない限り、これは問題ではありません。
すべての非互換性のvery詳細リストについては、 ISO CとISO C++の間の非互換性 を参照してください。コンパイラエラーですぐに現れない問題を含む、多くの微妙な問題があります。たとえば、問題になる可能性がある問題の1つは、文字定数のサイズです。
// In C, prints 4. In C++, prints 1
printf("%d\n", sizeof('A'));
すべてのCファイルを "extern C {...}"でラップしない場合、最も予期しない場所で名前のマングリングが発生しますか?
CとC++をリンクしようとすると、噛み付いてしまいます。
以下を含む多くのヘッダーファイルを作成しました。
#ifdef __cplusplus
extern "C" {
#endif
// rest of file
#ifdef __cplusplus
}
#endif
しばらくすると、既存の複数インクルードボイラープレートにマージされ、表示されなくなります。しかし、あなたはそれをどこに置くか注意する必要があります-通常それは属しますafterヘッダーが含むどんなインクルードも。
私が本当にこれをしてはいけない理由はありますか?
CとC++を組み合わせるつもりがないことが確実にわかっている場合は、私が知っているようにする理由はありません。しかし、段階的な移行については、CコンポーネントとC++コンポーネントの両方を使用する必要がある、公開されたインターフェイスを持つすべてのものにとって不可欠です。
これを行う大きな理由notは、(少なくともそれらのヘッダーで)関数をオーバーロードできないようにするためです。すべてのコードをC++に移行し、それを維持/リファクタリング/拡張し始めたら、そうしたいと思うかもしれません。
通常、問題はまったく発生しません。はい、CとC++の間にはいくつかの非互換性がありますが、上記のmallocキャストを除いて、それほど頻繁には発生しないようです。
次のオープンソースCライブラリをC++としてコンパイルして使用しました。
最も難しいのは、名前空間ラッパーを追加することでした。これには、主にコードに深く埋め込まれた#includeステートメントがC++名前空間の外になければならなかったため、数時間かかりました。
なぜ私はこれをしたのですか?なぜなら、人々が彼らのアプリに直接リンクしている商用ライブラリを販売しているからです。また、アプリがExpat、FreeTypeなどの他のバージョンにリンクされていることもあります。これにより、多重定義シンボルエラーが発生しました。最もクリーンなことは、すべてをライブラリ内に移動して、名前空間に隠すことでした。
しかし、私が使用しているすべてのオープンソースライブラリを使用してそれを実行したわけではありません。いくつかはまだ衝突を引き起こしていない、そして私はそれらを修正するためのラウンドを持っていません、それはトラブルのないことはかなり退屈です。興味深い例外はSQLiteで、C++でコンパイルできませんでした。そこで私は大規模な検索と置換を行い、外部から見えるすべてのシンボルに接頭辞(私の製品の名前)を追加しました。これでクライアントの問題は解決しました。
別の例:C++にはintからenumへの暗黙的な変換はありませんが、Cにはあります。本当にC++で実行する場合は、キャストが必要です。
MSVCを使用する前にこれを実行しましたが、MSVCを使用する場合の適切な戦略は次のとおりです。
foo =(Foo *)malloc(sizeof(* foo));
なる
foo = malloc<Foo>();
そしてもちろん、Foo + nバイトが必要な場合はオーバーロードすることができます
また、可能な場合はRAIIを使用するようにメモリ割り当てを切り替えることもお勧めします。一部の機能はかなり複雑であり、RAIIへの切り替えはリスクが高すぎることがわかりました。ほとんどの場合、それは非常に簡単です。
C++にはより厳密な型チェックがあるため、malloc/realloc/callocの各呼び出しにキャストを追加する必要がある場合があります。
c ++コンパイラでコンパイルしてみます。
typedef enum{ false = 0, true = 1} bool;