移植性の高いプログラムの書き方
「基本 〜コンパイラの識別〜」編

しのはらのC++実験室トップページ
「移植性の高いプログラムの書き方」トップページ

移植性の問題

 どんなにANSI C++に準拠した正しいプログラムを書くことを心がけても、 コンパイラの方にANSIに準拠していない動作があれば、 うまく行かないことがあります。 また状況によっては、どうしても標準ライブラリに存在しない関数 (OS依存や機種依存する関数など)を使用したいことがありますが、 その関数の仕様がコンパイラによって微妙に異なることもあり得ます。
 そのような場合にはコンパイラ毎に問題を解決するためのコードを書いておいて、 それらをコンパイラ毎に使い分けることになります。

 コンパイラによる違いには以下のものが考えられます。
  • 最新のANSI C++のサポート状況

  • 組込み型のサイズなど、処理系依存部の仕様

  • 標準ライブラリの実装

  • (複雑な)ANSI C++仕様の解釈

  •  さて、コンパイラ毎に異なるコードを書くにはどうすればよいか。
     別々のソースファイルにして使い分けるというやり方は、 ちょっとの違いのためにほとんど中身の同じソースファイルを作ることになるか、 無駄に多くの小さなソースファイルを作る結果のどちらかになり、 どっちにしてもメンテナンス性が悪くて不便です。

    条件コンパイルの出番

     そこで「条件コンパイル」の登場です。 条件コンパイルとは、ソースコードの一部を条件付きで使用するように指定する方法です。
     例えば次のように#ifdef#endifなどのプリプロセッサ指令を使って実現します。
    #ifdef UseMain
    int main()
    {
    	/* .... */
    }
    #endif
    
    このコードでは、 このコードよりも前でUseMain というマクロが定義されていた場合に限って、 このmain関数が定義されます。 このコードはdefined()を使って次のように書くこともできます。
    #if defined(UseMain)
    int main()
    {
    	/* .... */
    }
    #endif
    

    事前定義マクロ

     さて、問題はコンパイラによってコードを分ける方法です。 実は各コンパイラには、まさにこのような用途のために用意された コンパイラ識別用事前定義マクロというものが有るのです。 (ただし仕様ではないので、これがないコンパイラもありえます)

    コンパイラ識別用の事前定義マクロの例
    事前定義マクロ処理系
    __BORLANDC__Turbo C++/Borland C++/C++Builder
    __WATCOMC__Watcom C/C++
    _MSC_VERMicrosoft C/Visual C++
    __CINT__C++インタプリタCINT
    __CYGWIN__CygwinのGCC
    コンパイラの種類によって分岐する方法は、例えば次のようになります。
    #if defined(__CINT__)
    	/* ...CINTのためのコード... */
    #elif defined(__BORLANDC__)
    	/* ...Borland製コンパイラのためのコード... */
    #elif defined(__WATCOMC__)
    	/* ...Watcom C/C++のためのコード... */
    #elif defined(_MSC_VER)
    	/* ...Microsoft C/Visual C++のためのコード... */
    #elif defined(__CYGWIN__)
    	/* ...cygwinのためのコード... */
    #else
    	/* ...その他のコンパイラのためのコード... */
    #endif