ロバとのビーバーの戦い、またはgccでのMSVCコードの適応

この記事では、古いWindows専用プロジェクト(MT4サーバーへのプラグイン)の1つをLinuxでのクロスコンパイル(CI、静的分析、自動テスト、その他の流行語)に適合させようとしたときに遭遇したいくつかの問題について説明します。 より正確には、MSVCによって簡単に食い尽くされるコード内の多くの構成要素がありましたが、mingw / gccを使用したコンパイルを断固として拒否しました。







画像







cat 7では、MSVCをコンパイルするがgccを使用しない最も一般的なコード例と、これを解決する方法。







免責事項



この記事の目的は、一部のコンパイラーが他のコンパイラーより優れていると言うことではなく、他のコンパイラーにコードを適合させるときに発生する可能性のある問題を指摘することです(特にMSVCのみを使用した場合)。 また、コンパイルフラグを調整すると、一部の(すべてではないにしても)動作要素を1つに減らすことができますが、コードを(少なくともsedで)修正する方が良いでしょうか?







初心者の方は、 こちらを読むことをお勧めします。







タスク条件



中規模のプロジェクト(ライブラリを除く約15,000のSLOC)があり、ほぼデフォルトのコンパイルフラグでCM​​akeを使用しています。 MSVCはバージョン14を使用し、mingw-gccは6.3を使用します。







見つかった問題



メソッド名の装飾



プロジェクト内には、プラグインがサーバーによって認識されるためにCメソッドとして呼び出される必要があるいくつかのメソッドがあります。 もともと、コードでは次の構成体が使用されていました。







__declspec(dllexport) void SomeMethod() {}
      
      





gccをコンパイルすると、関数名が修飾され、サーバーがプラグインでメソッドを定義しなかったという事実につながりました。 より正確な(実用的な)ソリューション:







 extern "C" __declspec(dllexport) void SomeMethod() {}
      
      





ファイルパスとインクルード



システムごとのパス区切り文字の違いも、コンパイル段階でエラーにつながります。 コード







 #include "directory\\include.h"
      
      





Linux / gccではコンパイルを拒否しますが、Windows / MSVCでは問題ありません。 これはまったく間違いではありませんが、移植性を簡単にするために、 ほとんどのシステムで受け入れられているため、通常のスラッシュを使用することをお勧めします 。 パスにも別の問題があります...







登録して含める



おそらくご存知のように、Windowsのsome/path



SoMe/pATh



some/path



違いはありませんが、これはプログラマーがヘッダーファイルで大文字と小文字を区別せずにパスを指定した場合にエラーにつながる他のシステムではそうではありません。 例:







 #include <Winsock2.h>
      
      





Linuxのgccでは、指定されたファイルが見つからないため、エラーが発生します。 同様の問題は、たとえばWs2_32



ws2_32



などのライブラリ名でも見られます。







ターゲットプラットフォームを決定する方法



プロジェクトは、異なるシステムでコンパイルおよび実行されるはずのQuickFIXを積極的に使用します。 QuickFIXの現在のバージョンは、次の設計を使用しています。







 #ifndef _MSC_VER #include <unistd.h> #endif
      
      





これをする必要はありません。 mingwを使用する場合、 _MSC_VER



定義されません。代わりに、 _WIN32



をチェックしてターゲットプラットフォームを決定し、MSVCに固有のコードを含める場合にのみ_MSC_VER



を使用する方が適切です。







純粋仮想メソッド



コード







 class SomeClass { virtual void someMethod() = NULL; };
      
      





gccをコンパイルしようとすると喜んで言うでしょう







無効な純粋指定子(「= 0」のみが許可されます)

ただし、MSVCでエラーは発生しません。 その理由は簡単です__null



は、0ではなく__null



(一般的には禁止されていません)でNULL



マクロを展開しNULL



。 解決策:当然、 NULL



の使用を放棄して純粋な仮想メソッドを指定し、use = 0



ます。







ヘッダーファイル内のメソッドの定義



コード







 class SomeClass { SomeClass::SomeClass() {}; };
      
      





gccを使用すると、







メンバー「SomeClass」の追加資格「SomeClass ::」

正しい答えには明らかにSomeClass::



が含まれていてはなりません。 一般に、ドラフトC ++ 14標準(8.3項)には次のように書かれています。







宣言は、修飾子が参照するクラスまたは名前空間の以前に宣言されたメンバーを参照するものとします


変数を指定しない変数宣言



クリップボードインターフェイスを使用して記述されたコード







 void someMethod() { SomeClass; SomeClass class; }
      
      





MSVCによって無視されるエラーが含まれていますが、gccコンパイル段階でエラーが発生します。







宣言は何も宣言しません

どの動作がここにあるのかわからないので、おそらくMSVCは信号を与えずに未使用の行を切り取るだけです。







あとがきの代わりに



私がリストしたエラーのほとんどは、この記事を読まなくても明らかに簡単に修正できます。コンパイラーのコメントだけで導かれます。 ただし、ソースコードの認識における「2つの世界」の違いを明確に示しており、コンパイラーを変更する場合に自然なことは決してありません。








All Articles