# MCPP-MANUAL

## == How to Use MCPP ==

#### for V.2.7.2 (2008/11)
松井 潔 (kmatsui@t3.rim.or.jp)

## == 目次 ==

1. 概要
  1.1. ＯＳや処理系を選ばない portable なソース
  1.2. 正確な Standard C モードに加えてその他の各種モードも
  1.3. このマニュアルの表記法
2. 起動時のオプションと環境設定
  2.1. mcpp 実行プログラムの２種類のビルドと５つの動作モード
  2.2. 起動時のオプションの指定法
  2.3. 共通のオプション
  2.4. mcpp の動作モードによるオプション
  2.5. 特定の処理系以外の処理系に共通のオプション
  2.6. 処理系ごとのオプション
  2.7. 環境変数
  2.8. Multi-byte character の encoding
  2.9. ワンパスコンパイラで mcpp を使うには
  2.10. 統合開発環境で mcpp を使うには
    2.10.1. Visual C++ の IDE で mcpp を使う方法
    2.10.2. Mac OS X / Xcode.app で mcpp を使う方法
3. 拡張機能と互換性
  3.1. #pragma MCPP put_defines, #pragma MCPP preprocess 等
    3.1.1. ヘッダファイルの pre-preprocess
  3.2. #pragma once
    3.2.1. ヘッダファイルに #pragma once を書き込むツール
  3.3. #pragma MCPP warning, #include_next, #warning
  3.4. #pragma MCPP push_macro, #pragma __setlocale 等
  3.5. #pragma MCPP debug, #pragma MCPP end_debug, #debug, #end_debug
    3.5.1. #pragma MCPP debug path, #debug path
    3.5.2. #pragma MCPP debug token, #debug token
    3.5.3. #pragma MCPP debug expand, #debug expand
    3.5.4. #pragma MCPP debug if, #debug if
    3.5.5. #pragma MCPP debug expression, #debug expression
    3.5.6. #pragma MCPP debug getc, #debug getc
    3.5.7. #pragma MCPP debug memory, #debug memory
    3.5.8. #pragma MCPP debug macro_call
  3.6. #assert, #asm, #endasm
  3.7. C99 の新機能（_Pragma() 演算子、可変引数マクロ等）
  3.8. 処理系ごとの特殊な仕様
    3.8.1. GCC, Visual C の可変引数マクロ
    3.8.2. GCC の 'defined' の処理
    3.8.3. Borland C の asm 文その他の特殊な構文
    3.8.4. #import その他
  3.9. GCC の問題と GCC との互換性
    3.9.1. FreeBSD 2 / kernel ソースのプリプロセス
    3.9.2. FreeBSD 2 / libc ソースのプリプロセス
    3.9.3. GCC 2 / cpp の仕様の問題
    3.9.4. Linux / glibc 2.1 ソースのプリプロセス
    3.9.5. GCC 2 で mcpp を使うには
      3.9.5.1. mcpp のウォーニングを整理するには
    3.9.6. GCC 3.2 ソースのプリプロセス
    3.9.7. GCC 3, 4 で mcpp を使うには
    3.9.8. Linux / glibc 2.4 ソースのプリプロセス
    3.9.9. Linux の stddef.h, limits.h, #include_next の問題
    3.9.10. Mac OS X / Apple-GCC とシステムヘッダの問題
    3.9.11. firefox 3.0b3pre ソースのプリプロセス
  3.10. Visual C++ のシステムヘッダの問題
    3.10.1. コメントを生成するマクロ ?
    3.10.2. Identifier 中の '$'
4. 処理系定義の仕様
  4.1. 終了時の status 値
  4.2. Include directory のサーチパス
  4.3. Header name の構築法
  4.4. #if 式の評価
  4.5. #if 式での文字定数の評価
  4.6. #if sizeof (type)
  4.7. White-space sequence の扱い
  4.8. mcpp 実行プログラムのデフォルトの仕様
5. 診断メッセージ
  5.1. 診断メッセージの形式
  5.2. Translation limits
  5.3. Fatal error
    5.3.1. mcpp 自身のバグ
    5.3.2. 物理的エラー
    5.3.3. Translation limits と内部バッファのエラー
    5.3.4. #pragma MCPP preprocessed に関するエラー
  5.4. Error
    5.4.1. 文字とトークンに関するエラー
    5.4.2. 完結しないソースファイルのエラー
    5.4.3. Preprocessing group 等の対応関係のエラー
    5.4.4. ディレクティブ行の単純な構文エラー
    5.4.5. #if 式の構文エラー等
    5.4.6. #if 式の評価に関するエラー
    5.4.7. #define のエラー
    5.4.8. #undef のエラー
    5.4.9. マクロ展開のエラー
    5.4.10. #error, #assert
    5.4.11. #include の失敗
    5.4.12. その他のエラー
  5.5. Warning (class 1)
    5.5.1. 文字、トークンおよびコメントに関するウォーニング
    5.5.2. 完結しないソースファイルのウォーニング
    5.5.3. ディレクティブ行に関する各種のウォーニング
    5.5.4. #if 式に関するウォーニング
    5.5.5. マクロ展開に関するウォーニング
    5.5.6. 行番号に関するウォーニング
    5.5.7. #pragma MCPP warning, #warning
  5.6. Warning (class 2)
  5.7. Warning (class 4)
  5.8. Warning (class 8)
  5.9. Warning (class 16)
  5.10. 診断メッセージ索引
6. バグ報告等

# 1. 概要

mcpp は Martin Minow の DECUS cpp を元に kmatsui（松井 潔）が全面的に書き直したＣプリプロセッサです。mcpp という名前は Matsui cpp という意味です。これはソースで提供するもので、各処理系で使うにはその処理系に合わせてソースに若干の変更を加えた上でコンパイルして mcpp の実行プログラムを作る必要があります。*1

このマニュアルでは、すでに特定の処理系に移植された実行プログラムの仕様を説明しています。さらに詳細を知りたい人、ソースから何らかの処理系に移植してみたい人は、ソースと mcpp-porting.html というドキュメントを参照してください。
これらのソース、ドキュメントはすべてオープンソース・ソフトウェアとして提供します。
マニュアルの内容に入る前に、まず mcpp の特徴を紹介しておきます。

注:

*1 mcpp V.2.6.3 からは、コンパイルずみの何種類かのバイナリ・パッケージも次のサイトで提供するようにした。

> http://mcpp.sourceforge.net/

## 1.1. ＯＳや処理系を選ばない portable なソース

Linux, FreeBSD, Windows 等の多くのＯＳをサポートしている portable なプリプロセッサであり、そのソースは Standard C (ANSI/ISO/JIS C) の処理系または Standard C++ の処理系でさえあればコンパイルできる広い portability を持っています。ライブラリ関数は古典的なものしか使っていません。
各処理系に移植するためには、多くの場合、ヘッダファイル中のいくつかのマクロ定義を書き替えてコンパイルするだけですみます。最悪の場合でもソースファイルに数十行書き足す程度です。

Multi-byte character（漢字）の処理は日本の EUC-JP, shift-JIS, ISO2022-JP、中国の GB-2312、台湾の Big-5、韓国の KSC-5601 (KSX 1001) に対応しており、UTF-8 も使えます。Shift-JIS, ISO2022-JP, Big-5 の場合、コンパイラ本体が漢字を認識しない処理系では、mcpp がそれを補います。

## 1.2. 正確な Standard C モードに加えてその他の各種モードも

Standard C 準拠の動作モードのほかに、K&R 1st. のモードや "Reiser" model cpp のモードもあり、さらには自称 post-Standard 仕様のモードまであります。C++ のプリプロセッサとして動作する実行時オプションもあります。
Standard C モードは既存の多くのプリプロセッサと違って、規格を完全に実装しているつもりです。C90, C95, C99, C++98 のすべてに対応しています。Standard C プリプロセスの reference model となるものを目指して作ってあります。これらの規格のバージョンは実行時オプションで指定することができます。*1

ほかにいくつかの有用な拡張機能も持っています。マクロの展開機序や #if 式の評価機序をトレースする #pragma MCPP debug もあります。ヘッダファイルを "pre-preprocess" しておくこともできます。
いくつかの有用な実行時オプションも備えています。ウォーニングのレベルを指定するオプションや、include directory を指定するオプション等です。
ソースにどんな間違いがあっても mcpp は暴走したり見当外れなメッセージを出したりせず、正確でわかりやすい診断メッセージを出して適切な処理をします。移植上で問題となる点についても警告を発します。
高品質でありながら、コードサイズは比較的小さく、メモリ消費も比較的少なくてすみます。
詳細なドキュメントも付属しています。

mcpp の欠点を強いて挙げれば、速度がやや遅いことです。GCC 3.*, 4.* / cc1 に比べると２倍から３倍の時間がかかります。しかし、Borland C 5.5 / cpp と同じくらいの速度で、ヘッダファイルの pre-preprocess の機能を使うともう少し速くなるので、特に遅いほうではありません。正確であること、portable なソースであること、少ないメモリでも動作すること等のためには、この程度の処理時間はやむをえないと考えています。

なお、プリプロセッサの Standard C 準拠度をテストするための検証セットである "Validation Suite for Standard C Preprocessing"、その解説およびそれを使ってテストした各種プリプロセッサの採点簿 cpp-test.html を mcpp とともに公開しています。これを見ると、「Standard C 準拠」と称する既存のプリプロセッサにいかに多くの問題があるかがわかります。

mcpp は V.2.3 の開発の途中で、検証セット V.1.3 とともに、情報処理推進機構(IPA) の平成１４年度「未踏ソフトウェア創造事業」に新部 裕・プロジェクトマネージャによって採択され、2002/07 - 2003/02 の間は IPA の資金援助と新部ＰＭの助言のもとに開発が進められました。英語版ドキュメントもこのプロジェクトの中で、有限会社・ハイウェルに翻訳を委託し、それに私が修正とテキスト整形を加えてできあがったものです。*2
mcpp はさらに平成１５年度にも「未踏ソフトウェア創造事業」に伊知地 宏 PM によって継続して採択され、V.2.4 への update 作業が進められました。
その後も mcpp と検証セットはさらに改良の作業が続けられています。

注:

*1 Ｃ言語の規格としては ISO/IEC 9899:1990 (JIS X 3010-1993) が長く使われてきたが、1999 年には ISO/IEC 9899:1999 が採択された。ここでは前者を C90、後者を C99 と呼ぶ。前者は ANSI X3.159-1989 が移行したものなので、一般には ANSI C または C89 と呼ばれることもある。また、ISO/IEC 9899:1990 + Amendment 1995 を C95 と呼ぶことがある。C++ の規格は ISO/IEC 14882:1998 およびその正誤訂正版である ISO/IEC 14882:2003 で、この両者をここでは C++98 と呼ぶ。

*2 「未踏ソフトウェア創造事業」(Exploratory Software Project) の概要は次のところで知ることができる。

> http://www.ipa.go.jp/jinzai/esp/

mcpp V.2.3 以降のソースおよびドキュメントと検証セット V.1.3 以降は次のところに置いてきたが、

> http://www.m17n.org/mcpp/

2006/04 に次のところに移った。

> http://mcpp.sourceforge.net/

mcpp の古いバージョンである cpp V.2.2 および検証セット V.1.2 はベクター社のサイトの次のところにある。dos/prog/c というディレクトリに入れられているが、MS-DOS 専用ではない。ソースは UNIX, WIN32/MS-DOS 等に対応している。

> http://www.vector.co.jp/soft/dos/prog/se081188.html
> http://www.vector.co.jp/soft/dos/prog/se081189.html
> http://www.vector.co.jp/soft/dos/prog/se081186.html

これらのアーカイブファイル中のテキストファイルは、Vector のものは Windows に合わせて、改行コードは [CR]+[LF]、漢字は shift-JIS で encode してある。SourceForge のものは V.2.5 までは UNIX 系に合わせて改行コードは [LF]、漢字は EUC-JP である。V.2.6 からは [CR]+[LF] / shift-JIS の zip 版と[LF] / EUC-JP の tar.gz 版の２種類のアーカイブファイルを置くようにした。

## 1.3. このマニュアルの表記法

このマニュアルはかつてはテキストファイルでしたが、V.2.6.2 からは html ファイルに変わりました。
このマニュアルでは次のようにフォントを使い分けています。

- `source`:
  `紺色`の等幅フォントは、ソースコードの断片およびコマンドラインの入力を示すのに使われます。
- `__STDC__`:
  `くり色`の等幅フォントは、標準事前定義マクロおよび何らかのマクロを示すのに使われます。
- STD:
  Italic フォントは mcpp の system.H というソースファイルで定義されるマクロを表します。このマニュアルではこれらの名前を mcpp の種々の設定を表記するために使います。これらのマクロは mcpp をコンパイルする時に使われるだけで、mcpp の実行プログラムには存在していないことに注意してください。

# 2. 起動時のオプションと環境設定

## 2.1. mcpp 実行プログラムの２種類のビルドと５つの動作モード

mcpp には、どの処理系（コンパイラ）からも独立して単独で動く compiler-independent-build と、特定の処理系のプリプロセッサに取って代わって動く処理系専用 build (compiler-specific-build) とがあります。前者はプリプロセスだけをするためのもので、その出力を何らかのコンパイラに与えても正しくコンパイルされるとは限りません。後者は特定の処理系でコンパイルとリンクまで行うためのもので、mcpp の出力はそのコンパイラの仕様に合わせてあります。*1, *2

前者ではオプション等の仕様は mcpp をコンパイルした処理系のいかんにかかわらずほぼ一定で、OS による相違が少しあるだけです。後者では共通の仕様とともに、処理系との互換のための仕様が多くあり、その部分は処理系ごとに異なっています。Compiler-independent 版のオプションは処理系専用版でもほぼ使えますが、処理系のオプションとの衝突を避けるために別のオプションになっているものもあります。
なお、このドキュメントで「GCC 用」「Visual C 版」等と表記しているのはいずれもそれぞれ GCC 専用 build, Visual C 専用 build の意味です。

Compiler-independent 版でも処理系専用版でも、mcpp にはいくつかの動作モードがあり、それぞれ異なるプリプロセス仕様で動作します。モードには次の５つあります。

- STD
  Standard C (C90, C99, C++98) のプリプロセス仕様です。これがデフォルトのモードです。
- COMPAT
  std モードの変種で、再帰的マクロを規格よりもさらに展開します。
- POSTSTD
  kmatsui が勝手に作ったプリプロセス仕様で、Standard C の首尾一貫しない規定を整理して単純化したものです。
- KR
  C90 以前の K&R 1st. のプリプロセス仕様です。
- OLDPREP
  いわゆる "Reiser" model cpp の動作仕様です。

これらのモードを指定するオプションは次の通りです。 モードを何も指定しないと -@std を指定したことになります。

- `-@std`
  STD モード
- `-@compat`
  COMPAT モード
- `-@post, -@poststd`
  POSTSTD モード
- `-@kr`
  KR モード
- `-@old, -@oldprep`
  OLDPREP モード

STD, COMPAT, POSTSTD を合わせて Standard モード、KR と OLDPREP を合わせて pre-Standard モードと呼ぶことにします。また、COMPAT は STD とほとんど同じなので、特に断らない限り STD には COMPAT も含め、必要な場合だけ COMPAT に言及します。*3

Standard と pre-Standard のモードとでは、マクロ展開方法の違いが多くあります。C90 と pre-C90 との違いだと思って間違いありません。最も大きな違いは、関数様マクロ（引数付きマクロ）の展開で、引数にマクロが含まれている場合、Standard モード では引数を先に完全に展開してから元マクロの置換リスト中のパラメータと置き換えるのに対して、pre-Standard では展開せずにパラメータと置換し、再スキャン時に展開することです。
また、Standard モード では直接にも間接にもマクロの再帰的展開は原則としてしません。pre-Standard では再帰的なマクロ定義があると、展開時に無限再帰を引き起こしてエラーとなります。

行末にある \ の扱いもモードによって異なり、Standard モード では trigraph 処理の後、tokenization の前に <backslash><newline> の sequence を削除しますが、pre-Standard モード では文字列リテラルの中にある場合と #define 行にある場合に限ってこれを削除します。

いわゆる tokenization（トークン分割、トークンの切り出し）もモードによって微妙に違います。
Standard モード では「token base での動作」という建前に忠実に tokenization を行います。具体的には、Standard モード では、マクロを展開するとその前後に space を挿入して、前後の token との意図しない連結が発生するのを防ぎます。pre-Standard モード は伝統的・便宜的・暗黙的な tokenization と、「character base でのテキスト置換」によるマクロ展開方法の痕跡を残しています。これらの点については、 cpp-test.html#2 をご覧ください。
Standard モード では preprocessing number という数値トークンを規定通りに扱います。pre-Standard では、数値トークンはＣの整数定数トークンおよび浮動小数点数トークンと同じです。整数定数での接尾子 'U', 'u', 'LL', 'll'、浮動少数点定数での接尾子 'F', 'f', 'L', 'l' はトークンの一部として認識しません。
ワイド文字の文字列リテラルと文字定数は Standard モード でしか単一のトークンとして認識されません。

Digraph, #error, #pragma, _Pragma() operator は Standard モード でないと使えません。-S <n> オプション（strict-ansi モード）と -+ オプション（C++ プリプロセッサとして動作する）も Standard モード でしか使えません。事前定義マクロ `__STDC__`, `__STDC_VERSION__` は Standard モード の時に定義され、pre-Standard の時は定義されません。
#if defined, #elif は pre-Standard では使えません。#include, #line の引数にマクロを使うことは pre-Standard ではできません。事前定義マクロ `__FILE__`, `__LINE__`, `__DATE__`, `__TIME__` は pre-Standard の時は定義されません。
他方で、#assert, #asm (#endasm), #put_defines, #debug は pre-Standard モードでしか使えません。
#if 式は Standard モード では long long / unsigned long long で評価しますが、pre-Standard モード では (signed) long だけで評価します。#if 式で sizeof (type) が使えるのは pre-Standard だけです。

Trigraph と UCN (universal-character-name) は、STD モードでしか使えません。

診断メッセージの出方もモードによって少し違っています。

以上に述べたこと以外で K&R 1st. に Standard C と異なる明確な規定のないことがらについては、pre-Standard モード では C90 の規定に従います。

OLDPREP モードの KR モードとの違い、および POSTSTD, COMPAT 各モードの STD モードとの違いは次のようなものです。

- OLDPREP
  次のような仕様の古い "Reiser model cpp" の動作をする（old preprocessor の意）。
  1. コメントを１個の space でなく０個の space に変換する。この変換は原則として最後の出力時に行わる。ただし、マクロ定義では定義の直後に行われる。
  2. マクロ定義の置換リスト中に文字列リテラルまたは文字定数があり、その中にどれかのパラメータ名と一致する部分がある場合は、そのマクロの呼び出しの際にこの部分は、パラメータに対応する引数で置き換えられる。一致する部分というのは、両端の " または ' をはずした中身をトークン列として見た場合に一致するトークンのことである。
  3. #else, #endif の行に何が書いてあってもエラーにせず無視する（対応する #if MACRO, #ifdef MACRO の MACRO を書いたりする）。
  4. リテラルを閉じる " や ' がない場合は、行末で閉じられているとみなす（unterminated string literal, unterminated character constant のエラーが発生しない）。
  5. # 123 という行を #line 123 と同じものとして扱う。
- COMPAT
  再帰的マクロを規格よりもさらに展開する。 すなわち、再帰的マクロの展開に際して、同名マクロの再置換を禁止する範囲を規格よりも狭くとる。
  再帰的マクロの展開の規定については cpp-test.html#3.4.26 を参照のこと。再帰的マクロの具体例は test-t/recurs.t を参照のこと。*4
- POSTSTD
  これは規格に次のような変更を加えたものである。
  1. Trigraphs は認識しない。Digraph は translation phase 1 で、すなわちプリプロセスの最初に変換してしまう。Token としては扱わない。
  2. Tokenization を完全な token-base の原則にしたがって単純化している。ソース中の preprocessing token の間に token separator としての white space がない場合は、そこに自動的に a space を挿入する（ただし、マクロ定義中のマクロ名と次の '(' の間には挿入しない）。したがって、# 演算子による文字列化でもすべての preprocessing token の間には a space が入った上で文字列化されることになる。また、マクロの再定義に際しては、token separator の有無は問題にならない。
  3. 関数様マクロの再定義に際しては、パラメータ名の違いは問題にしない。
  4. #if 式中に文字定数は使えない（エラーにする）。
  5. 関数様マクロの展開に関する「関数様」でない不規則な規定をカットしている。 すなわち、再スキャンはそのマクロの置換リストだけを対象とし、その後ろの sequence は取り込まない。
  6. #include <stdio.h> という形式の header name は通常は通すが、ウォーニングを出す（class 2 のウォーニングオプションで）。マクロで <stdio.h> の形の header name を使うと、特殊な場合にはエラーとなることがある。#include "stdio.h" の形式を推奨する。
  7. マクロ定義ではマクロ名と置換リストとの間に space が必要という規定が C99 で追加されたが、この規定には従わない（tokenization の際に自動的に a space が挿入されるので）。
  8. UCN (universal-character-name) は認識しない。また、identifier 中の multi-byte character も認識しない。
  9. C++ では１１種の identifier 様 operator は operator としては扱わない。

なお、このほか lang-asm モードというものもあります。 これはアセンブラソースに C のコメント、ディレクティブ、マクロが挿入された変則的なソースを処理するモードです。 POST_STD はこのモードになることはできませんが、それ以外のモードは一定のオプションを指定することでこのモードになります。 その仕様については 2.5 を見てください。

こういうことで mcpp の実行プログラムにはいくつもの仕様があるので、マニュアルを注意して読んでください。この章ではまず mcpp の共通のオプションを説明し、次に動作モードによって異なるオプションを述べ、次に特定の処理系専用版を除いて共通のオプションを述べ、そのあとで処理系専用版の処理系ごとに異なるオプションを記載します。

注:

*1 このほか subroutine-build というものもある。何らかの他のメインプログラムから mcpp がサブルーチンとして呼び出されるものである。しかし、その仕様は mcpp をコンパイルした時の設定によって compiler-specific-build または compiler-independent-build のどちらかと同じになるので、このマニュアルでは特にとりあげない。 Subroutine-build については mcpp-porting.html#3.12 を参照のこと。

*2 SourceForge のサイトで提供するバイナリ・パッケージはすべて compiler-independent-build である。

*3 V.2.5 までは Standard モードと pre-Standard モードとで実行プログラムが別になっていたが、V.2.6 から１つに統合された。

*4 これは GCC, Visual C++ 等の主要な処理系との互換性のためのオプションである。'COMPAT' は "compatible" の意味である。

## 2.2. 起動時のオプションの指定法

以下の記載では、<arg> という記法は arg がユーザの入力すべき任意の引数であることを示し、[arg] は arg が省略可能な引数であることを示します。どちらにしても <, >, [, ] の文字そのものは入力してはいけません。

mcpp を起動する書式は次の形です。ただし、mcpp という名前は mcpp のインストール時の指定によっては別の名前になります。

    mcpp [-<opts> [-<opts>]] [in_file] [out_file] [-<opts> [-<opts>]]

out_file（出力パス）が省略された時は（-o オプションが指定されない限り）標準出力に出力します。in_file（入力パス）も省略された時は標準入力から入力します。診断メッセージは（-Q オプションが指定されない限り）標準エラー出力に出力します。
これらのどれかのファイルがオープンできない時は、エラーメッセージを出して終了します。

引数を必要とするオプションに引数がない場合はエラーとなります（-M オプションだけは別）。
引数を必要とするオプションでは -I<arg>, -I <arg> のどちらも有効です（オプション文字と引数の間に space はあってもなくても良い）。
引数のないオプションは -Qi, -Q -i のどちらも有効です（１つの '-' の後につなげても別々に '-' を付けても良い。ただし、-M はつなげてはいけない）。
同一のオプションが複数回指定された場合、-D, -U, -I, -W オプションはそれぞれが有効です。-S, -V, -+ は２回目以降は無視されます。-2, -3 はそのたびに仕様が反転します。その他のオプションは最後に指定されたものが有効です。

大文字と小文字は区別されます。
いわゆるスイッチキャラクタは Windows でも - であり、/ ではありません。
不正なオプションを指定すると usage 文が表示されるので、mcpp -? 等とすることで、使えるオプションを確かめることができます。Usage 文のほかにもいくつかのエラーメッセージがありますが、その内容はいずれも自明のものであるので、説明は省略します。

## 2.3. 共通のオプション

mcpp の動作モードや処理系によらない共通のオプションは次の通りです。

- `-C`
  ソース中のコメントも出力する。デバッグ時に有用なオプションである。ただし、コメントはソースの論理行の先頭に移して出力する。コメントの処理はマクロ展開やディレクティブの処理の前に行われるものであり、コメントはマクロ呼び出しの途中にあるかもしれないからである。
- `-D <macro>[=[<value>]]`
- `-D <macro(a,b)>[=[<value>]]`
  マクロ macro を定義する。事前定義されているマクロでも `__STDC__, __STDC_VERSION__, __FILE__, __LINE__, __DATE__, __TIME__, __cplusplus` 以外であれば、このオプションで定義が変更できる（C99 の `__STDC_HOSTED__` は GCC V.3 のように -D オプションで定義する処理系もあるので、例外的に許可する）。=<value> が指定されていれば value に定義し、省略されていれば 1 に定義する（bcc32 では省略時には０個のトークンに定義されるので、それとは違うことに注意）。= の前には空白を入れてはいけない。= の後に空白があればそのマクロは０個のトークンに定義される。
  このオプションでは、引数つきマクロも定義できる。
  このオプションは何回でも指定できる。
- `-e <encoding>`
  Multi-byte character の encoding を <encoding> に変更する。 <encoding> については 2.8 を参照のこと。
- `-I <directory>`
  Include directory のサーチパスの第一位に directory を指定する（サーチパスについては 4.2 を参照）。directory 名に space が含まれる場合は、directory 名全体を " と " で囲むこと。
- `-I 1, -I 2, -I 3`
  #include "header" の形式の（<header> の形式ではない）ディレクティブで最初にサーチされるディレクトリの基準を指定する。-I1 ではカレントディレクトリ、-I2 ではソースファイル（インクルード元）のあるディレクトリ、-I3 ではその双方をサーチする（詳細は 4.2 を参照）。
- `-j`
  診断メッセージの出力ではソース行等の付加情報は出力せず、１行の診断メッセージだけ出力する（デフォルトでは１行の診断メッセージに続いて、そのソース行が表示される。ソースファイルが include されたものである場合は、include 元の行も順次表示される。マクロに関する診断メッセージでは、展開前のマクロも順次表示される）。
  GCC の testsuite で私の検証セットを使う時は、このオプションを指定して、GCC と同じ診断メッセージ形式にする必要がある。

-M* オプションは makefile 用の依存関係行を出力するものです。複数のソースファイルがある場合、すべてのソースについてこれらの -M* オプションを付けて実行して、その出力をマージすると、makefile で必要な依存関係記述行がそろいます。これらのオプションは GCC のものに合わせていますが、少し違いがあります。*1

- `-M`
  ソースファイルの依存関係を記述する行を出力する。出力先はコマンドラインで指定された出力ファイル、それが指定されていなければ標準出力である。行が長ければ折りたたむ。通常のプリプロセス結果は出力しない。
- `-MM`
  -M とほぼ同じであるが、次のヘッダは書き出さない。
  1. #include <stdio.h> の形式で include されるもの。
  2. #include "/include/stdio.h" 等と絶対パスで指定されているもの。
  3. #include "stdio.h" の形式であるが、カレントディレクトリ（処理系や -I <n> オプションによってはソースのあるディレクトリ）で発見されず、システム等の include ディレクトリで発見されたもの（-I <directory> オプションや環境変数等で指定されたディレクトリも含む）。
  ただし、GCC-specific-build では GCC と同様に、書き出さないのはシステムヘッダだけである。
- `-MD [FILE]`
  -M とほぼ同じであるが、通常のプリプロセス結果もコマンドラインで指定されたファイルまたは標準出力に出力されることが違っている。また、依存関係行の出力先は、 FILE が指定された時はそのファイルとなり、指定されていない時はソースファイル名の .c を .d に変えたファイルとなる。
- `-MMD [FILE]`
  -MD とほぼ同じであるが、-MM と同様にシステムヘッダとみなされるものは書き出さないことが違っている。依存関係行の出力先は -MD [FILE] と同じ。
- `-MF FILE`
  出力先を FILE にする。-MD FILE, -MMD FILE に優先する。
- `-MP`
  "Phony target" も出力する。Phony target というのは、インクルードされるファイル１つ１つについて、それ自体を依存先を持たないターゲットとして、次のように記述するものである。

      test.o: test.c test.h
      test.h:

- `-MT TARGET`
  ターゲットの名前を通常の foo.o ではなく TARGET にする。-MT '$(objpfx)foo.o' というオプションでは次のような行が出力される。

      $(objpfx)foo.o: foo.c

- `-MQ TARGET`
  -MT と同様であるが、make にとって特別な意味を持つ文字は次のように quote する。

      $$(objpfx)foo.o: foo.c

- `-N`
  '_' で始まるものも含めてすべての事前定義マクロを無効にする。ただし、規格で要求されている事前定義マクロおよび __MCPP は除く。規格で要求されている事前定義マクロとは、`__FILE__, __LINE__, __DATE__, __TIME__, __STDC__, __STDC_VERSION__`, C99 の `__STDC_HOSTED__` および C++ の `__cplusplus` である。（`__MCPP` を undefine したい場合は -U オプションを使うこと）。
- `-o <file>`
  プリプロセス後のソースを file に出力する。省略時は第二引数が出力パスとなるので、無くてもよいオプションであるが、コンパイラドライバによってはこのオプションを使うものがある。
- `-P`
  コンパイラ本体に行番号情報を伝える出力を省略する。Ｃのプリプロセッサ以外の用途に流用する場合に使うオプションである。
- `-Q`
  診断メッセージを（カレントディレクトリの）mcpp.err という名前のファイルに出力する（mcpp.err は後ろにアペンドされてゆくので、時々削除すること）。
- `-U <macro>`
  事前定義されているマクロ macro を取り消す（`__FILE__, __LINE__, __DATE__, __TIME__, __STDC__, __STDC_VERSION__`, C99 モードでの `__STDC_HOSTED__` および -+ オプションで起動した時の `__cplusplus` は取り消せない）。
- `-v`
  mcpp のバージョンおよびインクルードディレクトリのサーチ順を標準エラー出力に出力する。
  ただし、2.4 で述べる -K オプションが指定された時、または 3.5.8 で述べる #pragma MCPP macro_call が指定された時は、このオプションはその意味を変える。
- `-W <level>`
  Warning を出力するレベルを level に指定する。 level は 0 または 1, 2, 4, 8, 16 のうちの任意の値の OR をとったものである。 1, 2, 4, 8, 16 はそれぞれ warning の class を意味する。 例えば -W 5 では class 1, 4 の warning が出力される。 0 の場合は warning は出力しない。 このオプションが複数回指定されると、すべての指定の OR がとられる。 例えば -W 1 -W 2 -W 4 は -W 7 と同じである。 -W 7 の代わりに -W "1|2|4" とすることもできる（| がパイプと解釈されないように " と " で囲う必要がある）。 -W "3|4" 等としてもかまわない。 ただし、-W 0 が指定された場合は、他の -W オプションはすべてキャンセルされる。 このオプションを指定しない時は -W 1 を指定したのと同じである（warning の内容は 5.5 - 5.9 を参照）。
- `-z`
  #include で取り込まれたファイルのプリプロセス結果は出力しない。 しかし、マクロは定義される。 その代わりに #include 行そのものを出力する。 Include されたファイルの中の #include 行は出力しない。 プリプロセスのデバッグ時に使うオプションである。

注:

*1 GCC と違うのは次の点である。

1. -MG オプションはない。オプション指定の仕方が複雑すぎるからである（したがって、どういう仕様かの説明は略）。 しかし、-M オプションでも、インクルードファイルが見つからない場合はエラーにはなるが、依存関係行は出力されるので、それで代用できる。
2. GCC-specific-build は GCC に合わせてあるが、それ以外では、-MM, -MMD オプションで除外されるヘッダの範囲が広い。

## 2.4. mcpp のモードによるオプション

mcpp にはいくつかの動作モードがあります。その仕様は 2.1 を参照してください。
このマニュアルでは各種のモードの仕様が並べて記載されているので、少々見にくくなっていますが、がまんしてください。また、以下の説明で出てくる DIGRAPHS_INIT, TRUE, FALSE, etc. の大文字名（__ で始まらないもの）で italic で表示されているものはすべて system.H で定義されるマクロです。これらのマクロはあくまでも mcpp 自身をコンパイルする時に使われるだけで、できあがった mcpp 実行プログラムにはこれらのマクロは残ってはいません。勘違いしないようにしてください。

Standard モードでは次のオプションが使えます。

- `-+`
  C++ のプリプロセッサとして動作する。マクロ `__cplusplus` を事前定義し（その値は system.H で定義されている。デフォルトは 1）、// から論理行の行末までをコメントと解釈し、`::, .*, ->*` をそれぞれ単一のトークンとして認識する。#if 式中では true, false というトークンはそれぞれ 1, 0 と評価する。`__STDC__`, `__STDC_VERSION__` が定義されていれば、それを削除する（ただし、GCC 専用版では GCC との互換性のために `__STDC__` は削除しない）。_ で始まらない事前定義マクロも削除する。ただし、extended characters の UCN への変換はしない。*1, *2
- `-2`
  Digraphs 処理の初期設定を反転する。DIGRAPHS_INIT == FALSE の場合はこれで digraph を認識するようになり、逆の場合は認識しなくなる。
- `-h <n>`
  マクロ `__STDC_HOSTED__` の値を <n> に定義する。
- `-S <n>`
  C では `__STDC__` の値を <n> に変更する。C++ では無視される。<n> は [0,9] の範囲の数値でなければならない。<n> が 1 以上であれば、_ で始まらない事前定義マクロ（`unix, linux` 等）を無効にする。
  S は `__STDC__` の意味である。このオプションを指定しないと、`__STDC__` はデフォルトの値となる（通常は 1）。
  GCC 版では -pedantic, -pedantic-errors, -lang-c89 でも -S1 を指定したと同じことになるので、その次の -S は無視される。 また、これらのオプションでは `unix, linux, i386` 等、規格違反の事前定義マクロは無効にならない。 GCC との互換性のためである。 これらの事前定義マクロは -ansi または -std=iso* オプションで初めて無効になる。
- `-V <value>`
  C では `__STDC_VERSION__`、C++ では `__cplusplus` という事前定義マクロの値を指定された値 <value> に変更する。この場合の <value> は long の値となる（C95 ではこの値は 199409L、C99 では 199901L、C++ Standard では 199711L である）。`__STDC__` が 0 に定義されていると `__STDC_VERSION__` は必ず 0L となり、-V は無効である。
  C では、このオプションを指定しないと、`__STDC_VERSION__` は system.H の STDC_VERSION の値となる（GCC V.2.7 - V.2.9 では 199409L、それ以外では 0L）。-V199901L として `__STDC_VERSION__` >= 199901L になった場合は次のような C99 の仕様となる（ 3.7 参照）。
  1. // から行末までをコメントとして扱う。 *3
  2. Preprocessing-number の中に e+, E+, e-, E- と同様に p+, P+, p-, P- という sequence も認める。これは浮動小数点数のビットパターンを 0x1.FFFFFEp+128 というふうに、１６進で表記するためのものである。
  3. _Pragma( "foo bar") と書くと #pragma foo bar と書いたのと同じ効果を持つ _Pragma operator が有効になる。
  4. EXPAND_PRAGMA というマクロを TRUE に定義してコンパイルされた mcpp では、#pragma 行の引数は、STDC または mcpp で始まるのでない限りマクロ展開の対象となる（デフォルトでは Visual C, Borland C 版以外では EXPAND_PRAGMA == FALSE であり、マクロ展開しない）。
  5. 識別子・文字定数・文字列リテラル・pp-number の中にある UCN (universal-character-name) の sequence を通す（これは STD モードに限る）。
  なお、可変引数マクロは C99 の仕様であるが、C90 および C++ でも使えるようにしてある。*4
  C++ でも -V199901L として `__cplusplus` >= 199901L にすると、C99 互換モードとなり、上記 2,3,4 の機能拡張を行う（1 は無条件で有効。5 はほぼ同様）。これは mcpp 独自の拡張であり、C++ Standard には違反する。
  なお、これらのマクロ `__STDC__`, `__STDC_VERSION__`, `__cplusplus` の指定に -D オプションは使えない。一般のユーザ定義マクロと区別するためである。

STD モードでは次のオプションが使えます。

- `-3`
  Trigraph 処理の初期設定を反転する。 TRIGRAPHS_INIT == FALSE の場合はこれで trigraph を認識するようになり、逆の場合は認識しなくなる。
- `-K`
  マクロに関する情報をコメントに書き込んで出力する「マクロ注釈モード」(macro notification mode) を指定する。 このモードでは、プリプロセス後の出力から元のソース上のマクロの位置を知ることができる。 このモードは C/C++ の refactoring tool のために実装されたものである。 C/C++ にはプリプロセスというフェーズのあることが refactoring tool にとって困難な問題を引き起こすが、このモードを使えばプリプロセス後の出力からソースを再構成することができる。 さらに、出力をそのまま再プリプロセスしてコンパイルすることができる。*5
  このモードは次の pragma で指定することもできる。

      #pragma MCPP debug macro_call

  -K オプションはこの pragma をソースの冒頭に書いたのとほぼ同じ効果を持つ。 ただし、pragma では事前定義マクロは出力されない。 Macro notification の仕様については 3.5.8. #pragma MCPP debug macro_call を参照のこと。
  このモードでは次の -k オプションが自動的に指定される。 このモードで同時に -v オプションが指定された時は -v オプションはその意味を変え、macro notification の内容がより詳細になる。 また、-a (-x assembler-with-cpp) または -C オプションが指定された時は、-K オプションは自動的に無効になる。*6
- `-k`
  ソース中の horizontal white spaces (space, tab) を圧縮せずにそのまま出力する。 コメントは同じ長さの spaces に変換する。 ソースとプリプロセス後の出力との column 位置の対応を維持するためのオプションである。 マクロがあると位置がずれるが、-K オプションを使うとそれも元の位置を知ることができる。*7

注:

*1 C++ で `__STDC__` が定義されているのはトラブルの元であり、良い仕様ではない。GCC のドキュメントによると、ヘッダファイルの多くが `__STDC__` が定義されていることを期待しているので、C++ でもこれを定義しておく必要がある、とのことである。しかし、これはヘッダファイルの書き方が悪いと言わざるをえない。C90, C99, C++ に共通の部分には、`#if __STDC__ || __cplusplus` と書くべきなのである。

*2 C++ Standard では C99 と違って、UCN は大々的な扱いを受けており、中途半端な実装はできない。C 1997/11 draft でもそうであった。しかし、Unicode をそこまで導入することには、実装の負担が大きすぎること等の問題があって疑問だからである。

*3 // は C90（Standard モードの場合）でもコメントとして扱うが、ウォーニングを出す。

*4 これは GCC との互換性のためである。

*5 GCC-specific-build の mcpp をインストールすると、mcpp によるプリプロセス出力が cc1 (cc1plus) に -fpreprocessed というオプションを付けて渡されるようになる。 このオプションは入力がプリプロセスずみであることを cc1 に伝えるものであるが、それでもコメントは処理される。 したがって、-K オプションの結果を cc1 に -fpreprocessed オプションを付けて渡して差し支えない。 また、gcc (g++) コマンドに -save-temps というオプションを付けると、プリプロセス結果の *.i (*.ii) ファイルが残されるので、それを refactoring tool で処理することができる。

*6 -x assembler-with-cpp オプションでは C/C++ のソースではない GCC 用の *.S ファイルがソースファイルとして使われるが、この *.S ファイルのプリプロセスでは、-K オプションを使ってコメントを挿入するとカラム位置がずれるため、アセンブルできなくなるからである。 また、-C オプションによるコメントも -K オプションで挿入されたコメントと紛らわしいことがあるので、同時には使えない。

*7 このオプションを使っても、特殊な場合には正確な対応が維持されないことがある。 <backslash><newline> による行連結と行をまたぐコメントによる行連結が連続する場合や、コメントが 256 行以上続く場合などである。 なお、このオプションを指定してもしなくても、'\v', '\f' は space に変換される。

## 2.5. 特定の処理系以外の処理系に共通のオプション

UNIX 系のシステムでは次の２つのオプションが使えます。 処理系独立版・GCC 専用版に共通です。 ただし、GCC 専用版では GCC が対応していないとエラーになります。

- `-m32`
  事前定義マクロを 32 ビット用にする。 CPU が x86_64 または ppc64 の場合、デフォルトでは 64 ビット用の事前定義マクロが使われるが、このオプションを指定するとそれぞれ i386, ppc 用のものが使われる。
- `-m64`
  事前定義マクロを 64 ビット用にする。 CPU が i386 または ppc の場合、デフォルトでは 32 ビット用の事前定義マクロが使われるが、このオプションを指定するとそれぞれ x86_64, ppc64 用のものが使われる。

GCC はオプションが非常に多いので、GCC 専用版ではそれとの衝突を避けるためにいくつかのオプションは他の処理系とは違うものにしています。なお、GCC でコンパイルした mcpp でも compiler-independent 版のオプションは他の処理系でコンパイルしたものと同じです。以下のオプションは GCC 専用版以外に共通のものです。

- `-a`
  ある種のアセンブラソースに見られる次のような記法をエラーにせずに通す。

  1.     #APP

     のように # の次の identifier がＣのディレクティブに合致しない場合、この行をそのまま出力する。

  2.     # + any comment.

     といった # の次に identifier, pp-number 以外のものが現れる行は、ウォーニングとともに捨てるがエラーにはしない。

  3.     "Jugemjugem
         gokouno
         surikire"

     といった大昔の流儀の行をまたぐ文字列リテラルを
     `"Jugemjugem\ngokouno\nsurikire"`
     という行に連結する。

  4. ## 演算子による token の連結で pp-token としては無効な token が生成されても、エラーにしない。
  5. マクロを展開した前後に space を挿入しない。 その結果、前後の pp-token と暗黙のうちに連結されてもエラーにしない。

  これらは GNU のソースなどに時々見られるものですが、GCC 版ではこのオプションは -x assembler-with-cpp および -lang-asm です。
  ただし、このオプションは POSTSTD モードでは使えません。
  このモードをこのマニュアルでは lang-asm モードと呼びます。
  このモードは mcpp を C/C++ 以外のテキストのマクロ・プロセッサとして使う場合に適しています。xrdb から呼び出す cpp として使う場合などです。

- `-I-`
  デフォルトの include directory がキャンセルされ、環境変数で指定された directory および他の -I オプションで指定された directory だけが有効となる。ただし、GCC 版ではこのオプションは -I- ではなく -nostdinc である。GCC では -I- オプションはまったく違った意味を持つ（ 2.6 参照）。

## 2.6. 処理系ごとのオプション

特定の処理系でプリプロセッサとして mcpp を使うには、処理系のプリプロセッサのある場所に適当な名前で置いておきます。その時に、処理系付属のプリプロセッサを消してしまうことのないよう、あらかじめ別名のファイルにコピーしておいてください。
Linux, FreeBSD, CygWIN での設定については 3.9.5 を参照してください。 GCC 3.*, 4.* での設定については 3.9.7, 3.9.7.1 を参照してください。MinGW での設定についても 3.9.7.1 を参照してください。

処理系付属のコンパイラドライバからは通常の方法では mcpp に渡す方法のないオプションもあります。
Gcc では -Wp というオルマイティオプションを使うと、どんなオプションでもプリプロセッサに渡すことができます。例えば、

    gcc -Wp,-W31,-Q23

とすると、プリプロセッサに -W31 -Q23 というオプションが渡されます。プリプロセッサに渡したいオプションを -Wp, に続けて , で区切って並べます。*1, *2

他の処理系でも、もしコンパイラドライバのソースがあれば、この種のオルマイティオプションを追加したほうが良いでしょう。例えば、-P<opt> と指定すると P をとった -<opt> がプリプロセッサに渡されるようにしておくと、どんなオプションでも使えるようになるので便利です。

もう１つの方法は、先に mcpp でプリプロセスして、その出力ファイルをソースファイルとしてコンパイラに渡すように makefile を書くことです。この方法については 2.9, 2.10 を参照してください。

mcpp の特定の処理系ごとのオプションは以下の通りです。Compiler-independent 版にはもちろんこれらのオプションはありません。

LCC-Win32 版では次のオプションが使えます。

- `-g <n>`
  マクロ `__LCCDEBUGLEVEL` を <n> に定義する。
- `-O`
  マクロ `__LCCOPTIMLEVEL` を 1 に定義する。

Visual C 版では次のオプションが使えます。

- `-arch:SSE, -arch:SSE2`
  マクロ `_M_IX86_FP` をそれぞれ 1, 2 に定義する。
- `-Fl <file>`
  GCC の -include <file> と同じ。
- `-G<n>`
  <n> が 3, 4, 5, 6, B であれば、マクロ `_M_IX86` をそれぞれ 300, 400, 500, 600, 600 に定義する。
- `-GR`
  マクロ `_CPPRTTI` を 1 に定義する。
- `-GX`
  マクロ `_CPPUNWIND` を 1 に定義する。
- `-GZ`
  マクロ `__MSVC_RUNTIME_CHECKS` を 1 に定義する。
- `-J`
  マクロ `_CHAR_UNSIGNED` を 1 に定義する。
- `-RTC*`
  -RTC1, -RTCc, -RTCs, -RTCu 等のオプションが指定されると、マクロ `__MSVC_RUNTIME_CHECKS` を 1 に定義する。
- `-Tc, -TC`
  C のソースであることを指示する。 指定しなくても同じ。
- `-Tp, -TP`
  -+ オプションと同じ。
- `-u`
  -N オプションと同じ。
- `-Wall`
  -W17 (-W1 -W16) と同じ。
- `-WL`
  -j オプションと同じ。
- `-w`
  -W0 オプションと同じ。
- `-X`
  -I- オプションと同じ。
- `-Za`
  マクロ `_MSC_EXTENSIONS` を未定義にし、identifier 中に '$' を使えないようにする。
- `-Zc:wchar_t`
  マクロ `_NATIVE_WCHAR_T_DEFINED`, `_WCHAR_T_DEFINED` をそれぞれ 1 に定義する。
- `-Zl`
  マクロ `_VC_NODEFAULTLIB` を 1 に定義する。

Mac OS X 上の mcpp では GCC-specific-build でも compiler-independent-build でも、次のオプションが使えます。

- `-F <framework>`
  <framework> ディレクトリを標準の framework ディレクトリに優先してサーチする。 標準の framework ディレクトリはデフォルトでは /System/Library/Frameworks, /Library/Frameworks である。

Mac OS X 上の GCC-specific-build では次のオプションが使えます。

- `-arch <arch>`
  ターゲットマシンのアーキテクチャをデフォルトのものから <arch> に変更する。 それによって、事前定義マクロのいくつかが変わる。 <arch> として有効なのは x86 の実行プログラムでは i386, x86_64、ppc の実行プログラムでは ppc, ppc64 のどれかである （gcc コマンドには４つのどれでも渡せる。gcc コマンドは -arch i386, -arch x86_64 のどれかを指定すると x86 用のプリプロセッサを呼び出し、-arch ppc, -arch ppc64 のどれかを指定すると ppc 用のプリプロセッサを呼び出す）。

GCC (GNU C) 版では、以下の（この 2.6 セクションの終わりまでの）オプションが使えます。なお、GCC 用では `__STDC__` は 1 にしているので、-S1 オプションは指定してもしなくても同じです。

まず、mcpp のモードによらないオプションは次のとおりです。

- `-$`
  -fno-dollars-in-identifiers と同じ。
- `-b`
  行番号情報をＣのソースのスタイルで出力する。
  プリプロセッサからコンパイラ本体への行番号情報の受け渡しは

      #line 123 "filename"

  というＣのソースと同じ形式でできる処理系が普通であるが、この形式は使えない処理系もある。mcpp はそのような処理系用の build では、その処理系のコンパイラ本体の受け取れる形式で行番号情報を出力するのが、デフォルトの仕様である。
  しかし、これらの処理系専用版でも、このオプションを使うと、Ｃのソースのスタイルで行番号情報を出力する。
  このオプションは、出力をもう一度プリプロセスする時のためのもので、#pragma MCPP preprocess と組み合わせてヘッダファイルの「プリプリプロセス」をする時に使う。
- `-dD, -dM`
  プリプロセスの最後にその時点で有効なマクロ定義を #define 行の形で出力する。-dM オプションではプリプロセス結果は出力しない。また、事前定義されたマクロも規格の標準事前定義マクロでなければ出力する。-dD オプションではプリプロセス結果も出力する。また、事前定義マクロは出力しない。*3, *4
- `-fexceptions`
  マクロ `__EXCEPTIONS` を 1 に定義する。
  `-fno-exceptions` ではこのマクロを定義しない。
- `-finput-charset=<encoding>`
  -e <encoding> オプションと同じ。GCC はこのオプションを指定すると <encoding> を UTF-8 に変換するが、mcpp は変換はしないことに注意。
- `-fno-dollars-in-identifiers`
  Identifier 中に '$' を認めない（デフォルトでは認める）。
- `-fPIC, -fpic, -fPIE, -fpie`
  いずれもマクロ `__PIC__, __pic__` をともに 1 に定義する。
- `-fstack-protector`
  マクロ `__SSP__` を 1 に定義する。
- `-fstack-protector-all`
  マクロ `__SSP_ALL__` を 2 に定義する。
- `-fworking-directory`
  出力の２行目にカレントディレクトリを表す特別な #line 行を出力する。
- `-I-`
  このオプションの前と後とで -I <directory> オプションの仕様を変える。このオプションの前に -I で指定されたディレクトリは #include "header.h" の形のヘッダファイルのサーチにだけ使われる。このオプションの後で -I で指定されたディレクトリはすべての #include directive のサーチに使われる。また、#include "header.h" の形でも include 元のディレクトリはサーチしない。
- `-include <file>`
  メインのソースファイルを処理する前に、<file> を #include する（ソースの冒頭に #include <file> と書いたのと同じ結果になる）。
- `-iquote <dir>`
  <dir> を #include "header.h" の形式のヘッダファイルのサーチパスに加える。
- `-isysroot=<dir>, -isysroot <dir>, --sysroot=<dir>, --sysroot <dir>`
  <dir> を system header directory の root とみなす。 すなわち、<dir> を system header directory の path-list の頭に付ける。 例えば、include directory が /usr/include で <dir> が /Developer/SDKs/MacOSX10.4u.sdk であれば /Developer/SDKs/MacOSX10.4u.sdk/usr/include とする。
- `-isystem <dir>`
  <dir> をインクルードパスの system-specific な directory の前に（site-specific な directory の後に）加える。
- `-lang-c, -x c`
  C のプリプロセスを行う。指定しなくても同じ。
- `-mmmx`
  マクロ `__MMX__` を 1 に定義する。
  `-mno-mmx` では `__MMX__` を定義しない。
- `-nostdinc`
  他の処理系での -I- オプションと同じ。
- `-undef`
  -N オプションと同じ。
- `-O?`
  ? が 0 以外の数字であればマクロ `__OPTIMIZE__` を 1 に定義する。
- `-Wcomment, -Wcomments, -Wsign-compare`
  -W1 オプションと同じ。指定しなくても同じ。
- `-Wundef`
  -W4 オプションと同じ。
- `-Wtrigraphs`
  -W16 オプションと同じ。
- `-Wall`
  -W17 (-W1 -W16) オプションと同じ（class 2, 4 の warning は Linux 等の標準ヘッダでは頻発しやすくうるさいので、-Wall からはずしてある。class 8 の warning も通常は余計なお節介である。しかし、これらには portability を確かめる等の有用な使い道がある。使う時は gcc -Wp,-W31 とすること）。
- `-w`
  -W0 オプションと同じ。

GCC 版の Standard モードでは次のオプションが使えます。

- `-ansi`
  `__STRICT_ANSI__` マクロを 1 に定義する。 `linux, i386` 等の規格違反の事前定義マクロを無効にする。 GCC 仕様の可変引数マクロの呼び出しでは、可変引数が存在しなくてもその直前のコンマを削除しない。*5
- `-digraphs`
  Digraph を認識する（-2 での反転も有効）。
- `-lang-c89, -std=gnu89`
  -S1 オプションと同じ。指定しなくても同じ。C90 だけでなく C95 の仕様も含まれる。
- `-std=c89, -std=c90`
  -S1 オプションとほぼ同じであるが、-ansi を含意する。
- `-lang-c99, -lang-c9x, -std=gnu99, -std=gnu9x`
  -V199901L オプションと同じ。
- `-std=c99, -std=c9x`
  -V199901L オプションとほぼ同じであるが、-ansi を含意する。
- `-lang-c++, -x c++`
  C++ のプリプロセスを行う。-+ と同じ。
- `-std=c++98`
  -+ とほぼ同じであるが、-ansi を含意する。
- `-pedantic, -pedantic-errors`
  -W7 (-W1 -W2 -W4) オプションと同じ。
- `-std=iso<n>:<ym>`
  規格のバージョンを指定する。<n> は C では 9899 であり、C++ では 14882 である。<n> が 9899 の場合は、<ym> は 1990, 199409, 1999, 199901 等である。<n> が 14882 の場合は、<ym> は 199711 である。<ym> にその他の値が指定された場合は、`__STDC_VERSION__` または `__cplusplus` がその値に定義される（この場合は 200503 というように６ケタで指定すること）。
  これらのオプションでは -ansi も指定したことになる。 他方で、-std=gnu* は -ansi を含意しない。 -pedantic も -ansi を含意しない。

GCC 版の STD モードでは次のオプションが使えます。

- `-lang-asm, -x assembler-with-cpp`
  他の処理系での -a と同じ。 lang-asm モードにする。 GCC-specific-build ではマクロ `__ASSEMBLER__` が 1 に定義され、identifier 中に '$' は使えなくなる。 入力ファイルの名前が *.S であるときはこのオプションを指定しなくても、このモードになる。
- `-trigraphs`
  Trigraph を認識する（-3 での反転も有効）。

GCC 版の pre-Standard モードでは次のオプションが使えます。

- `-traditional, -traditional-cpp`
  -@old と同じ。

CygWIN / GCC 版では次のオプションが使えます。

- `-mno-cygwin`
  Include directory の /usr/include を /usr/include/mingw に変更し、事前定義マクロを cygwin1.dll 用から msvcrt.dll 用に変更する。

次のオプションはいずれもエラーにはしませんが、何も対応しません（ウォーニングを出す場合もある）。

- `-A <predicate(answer)>`
  GCC ではこのオプションはソースに #assert <predicate(answer)> と書いたのと同じ結果になるが、#pragma でない拡張ディレクティブは Standard C では認められないものである。幸いいままでのところ gcc はデフォルトでは -D オプションで同等のマクロを cpp に渡してくれるので、実際にはほとんどの場合、支障はない（#assert を使っている稀なソースでない限り）。
- `-g <n>`
- `-idirafter <dir>`
- `-iprefix <dir>, -iwithprefix <dir>, -iwithprefixbefore <dir>`
- `-noprecomp`
- `-remap`

GCC V.3.3 以降ではプリプロセッサがコンパイラに吸収されて独立したプリプロセッサが存在しなくなったため、gcc を -no-integrated-cpp オプションを付けて呼び出しても、プリプロセッサのオプションではないオプションがプリプロセッサに渡されてくることがあります。GCC V.3.3 以降用の mcpp では、次のようなもののうち mcpp の認識しないものはこの種のにせオプションとして無視します。

- `-c`
- `-E`
- `-f*`
- `-m*`
- `-quiet`
- `-W*`

注:

*1 -Wa はアセンブラ用の -Wl はリンカ用のオルマイティオプションである。 UNIX / System V / cc のマニュアルを見ると、やはりこれらのオプションがある。GCC / cc の -W<x> オプションはこれとの互換性のためのものなのであろう。

*2 GCC V.3 では cpp が cc1 (cc1plus) に吸収されてしまった。そのため、-Wp で指定したオプションは通常は cc1 (cc1plus) に渡されてしまう。プリプロセスを cc1 ではなく cpp (cpp0) にさせるためには、gcc の呼び出しに -no-integrated-cpp というオプションを指定する必要がある。

*3 ただし、GCC V.3.3 以降では大量のマクロが事前定義されるようになったが、これらは事前定義マクロとしては扱わない。すなわち、-dD オプションでもこれらのマクロを出力する。

*4 #pragma MCPP put_defines (#put_defines) の出力は -dM オプションとほぼ同様である。ただし、次の点が違っている。

1. put_defines では標準事前定義マクロとして規定されているものも、コメントの形で出力する。
2. put_defines ではそのマクロ定義のあるファイル名と行番号もコメントの形で出力され、また読みやすいように形を整えて出力するが、-d* オプションでは GCC と同じ形式で出力する。この形式を想定している makefile も見掛けるからである。

*5 3.9.6.3 を参照のこと。

## 2.7. 環境変数

Compiler-independent 版の mcpp では include ディレクトリは UNIX 系 OS での /usr/include, /usr/local/include 以外はデフォルトでは設定されていないので、他のディレクトリも必要であれば、環境変数や実行時オプションで指定しなければなりません。Compiler-independent 版の環境変数は C では INCLUDE、C++ では CPLUS_INCLUDE です。ファイルのサーチはデフォルトではソースファイルのあるディレクトリを基準とします（ 4.2 を参照のこと）。しかし、Linux では include ディレクトリに混乱があるので、特別な対策が必要です。それについては 3.9.9 を参照してください。

GCC 専用版でデフォルトで設定されている system include ディレクトリについては、noconfig/*.dif ファイルを見てください。また、include ディレクトリのサーチ順と環境変数の名前については、 4.2 を見てください。

環境変数 LC_ALL, LC_CTYPE, LANG については、 2.8 を見てください。

## 2.8. Multi-byte character の encoding

mcpp は multi-byte character の多様な encoding に対応しています。

> EUC-JP | 日本の extended UNIX code (UJIS)
> shift-JIS | 日本の MS-Kanji
> GB-2312 | 中国の EUC-like な encoding （簡体字）
> Big-Five | 台湾の encoding （繁体字）
> KSC-5601 | 韓国の EUC-like な encoding (KSX 1001)
> ISO-2022-JP1 | 国際規格の日本語
> UTF-8 | unicode の encoding の１種

そして、実行時に次のようないくつかの方法で実際に使う encoding を指定します。優先順位はこの順のとおりです。

1. ソース中で #pragma __setlocale( "<encoding>") で指定された encoding （Visual C 用では #pragma setlocale( "<encoding>") ）。これを使うと、１本のソースファイルの中でも複数の encoding を使うことができる。
2. 実行時オプション -e <encoding> または -finput-charset=<encoding> で指定された encoding。
3. 環境変数 LC_ALL, LC_CTYPE, LANG で指定された encoding（優先順位はこの順）。
4. mcpp をコンパイルする時に設定されたデフォルトの encoding。

#pragma __setlocale、-e オプション、環境変数で指定できる <encoding> は原則として共通で、次のとおりです。右辺の <encoding> は左辺の encoding を指定します。<encoding> は大文字・小文字の区別をしません。また、'-', '_' は無視します。さらに '.' があると、そこまでをすべて無視します。したがって、たとえば EUC_JP, EUC-JP, EUCJP, euc-jp, eucjp, ja_JP.eucJP はすべて同じものとして扱われます。また、* は任意の文字を意味します（iso8859-1, iso8859-2 等は iso8859* にマッチする）。

> EUC-JP | eucjp, euc, ujis
> shift-JIS | sjis, shiftjis, mskanji
> GB-2312 | gb2312, cngb, euccn
> BIG-FIVE | bigfive, big5, cnbig5, euctw
> KSC-5601 | ksc5601, ksx1001, wansung, eucKR
> IS0-2022-JP1 | iso2022jp, iso2022jp1, jis
> UTF-8 | utf8, utf
> なし | c, en*, latin*, iso8859*

C, en* (english), latin*, iso8859* のどれかを指定すると、multi-byte character は認識されなくなります。ASCII ではない ISO-8859-* の Latin 系の single-byte character を使う時は、これを指定します。#pragma __setlocale( "") と空の名前を指定すると、デフォルトの encoding に戻ります。

このほか、Visual C++ 用の #pragma setlocale に限って、次のものも使えます。これらは Visual C++ との互換性のために用意しているものです。Visual C++ ではコンパイラがこちらでないと認識しないので、こちらを使ったほうが良いでしょう（'-' は mcpp では省略できるが、Visual C++ のコンパイラに対しては省略できない）。Visual C++ では C, english も使えます。

> shift-JIS | japanese, jpn
> GB-2312 | chinese-simplified, chs
> BIG-FIVE | chinese-traditional, cht
> KSC-5601 | korean, kor

Visual C++ では、Windows がどの国語用であるかによってデフォルトの multi-byte character encoding が変わることになっています。また、Windows の「地域と言語のオプション」の指定によっても変わりますが、この機能は中途半端でやっかいです。しかし、#pragma setlocale による指定はそれらに優先します。

GCC は shift-JIS, ISO2022JP, BIG-FIVE のように multi-byte character が 0x5c の値を持つバイトを含む encoding はうまく処理できない場合があるので、GCC 版では mcpp がそれを補います。*1

注:

*1 GCC を configure する時に --enable-c-mbchar というオプションを付けると、その GCC では環境変数 LANG を C-EUCJP, C-SJIS, C-JIS のどれかにセットすることによって encoding を指定できることになっている。 しかし、この configuration は 1998 からあるもののようであるが、実際には使われておらず、使っても正しく動作しない。 GCC 版の mcpp ではこれら LANG=C-SJIS 等の環境変数をサポートしていたが、V.2.7 で廃止した。
GCC では LANG のほかに LC_ALL, LC_CTYPE でも encoding を指定できることになっているが、実際には診断メッセージが変わるだけである。

## 2.9. ワンパスコンパイラで mcpp を使うには

Visual C, Borland C, LCC-Win32 のようにプリプロセッサがコンパイラから独立していないいわゆる「ワンパスコンパイラ」が多くなっています。処理速度を上げるためだと思われます。しかし、プリプロセスに要する時間は現在のハードウェアでは小さなものになっています。また、そもそもプリプロセスというものは実行時環境や処理系からはほぼ独立した共通のフェーズであることに大きな意味があるので、「ワンパスコンパイラ」が多くなるのは決して良いことだとは思えません。プリプロセスの処理系依存の仕様も増える結果になります。

ともあれワンパスコンパイラでは、プリプロセッサを mcpp に置き換えることができません。したがって、mcpp を使うには、まず mcpp でソースをプリプロセスし、その出力をコンパイラに渡しますが、コンパイラによって再度ムダにプリプロセスがされることになります。ムダですが、やむをえません。それでも、mcpp を使うことはソースチェックのために有効であり、処理系付属のプリプロセッサにはない機能を使うこともできます。
ワンパスコンパイラで mcpp を使うには、この手順を makefile に書く必要があります。そのサンプルとしては、mcpp 自身のコンパイルに使う visualc.mak, borlandc.mak, lcc_w32.mak 等の makefile のリコンパイル用の設定を見てください。

なお、GCC 3, 4 ではコンパイラがプリプロセス機能を内蔵するようになったものの、外部プリプロセッサを使うオプションも用意されているので、それを活用することで mcpp を問題なく使うことができます（3.9.7 参照）。

## 2.10. 統合開発環境で mcpp を使うには

GUI のいわゆる「統合開発環境」(IDE) というものは処理系独自の仕様であり、内部的なインタフェースも通常は公開されていないので、その中で mcpp を使うには困難があります。その上、コンパイラがワンパスコンパイラであると、そこに mcpp を使うフェーズを挿入するのはさらに困難です。

ここでは Windows 上の Visual C++ 2003, 2005, 2008 の IDE で mcpp を使う方法を説明します。Borland C 版や LCC-Win32 版は、コマンドラインで使ってください。

また、Mac OS X の Xcode.app / Apple-GCC で mcpp を使う方法も説明します。

### 2.10.1. Visual C++ の IDE で mcpp を使う方法

Visual C++ の IDE は内部的なインタフェースが公開されておらず、しかもコンパイラがワンパスコンパイラなので、通常の「プロジェクト」では mcpp を使うことができません。しかし、mcpp を使う makefile を書いておけば、それを取り込んで「メイクファイルプロジェクト」を作成することができます。そして、ソースの編集や検索や、さらにソースレベルデバッグ機能を含む IDE の大半の機能を使うことができます。

「メイクファイルプロジェクト」を作るには次のようにします。この方法は 「Visual C++ .net 2003 ドキュメント」および「Visual C++ 2005 Express Edition」「Visual C++ 2008 Express Edition」の「ヘルプ」の「メイクファイルプロジェクトの作成」に書いてあるものです。

1. IDE のデバッグ機能を使う権限を持つユーザとしてログインする。*1
2. mcpp を使う makefile を書いておく（noconfig/visualc.mak を参照）。*2
3. Visual Studio を起動する。*3
4. 「新しいプロジェクト」をクリックし、 現れた「新しいプロジェクト」のウィンドウで「メイクファイル プロジェクト」を選び、「プロジェクト名」と「場所」を指定して「OK」をクリックする。
5. すると、「メイクファイル アプリケーション ウィザード」のウィンドウが開くので、「アプリケーションの設定」をクリックし、そこで「ビルドコマンドライン」「出力」「クリーンコマンド」（または「消去コマンド」）「リビルドコマンド」の欄を入力する。これらの用語はわかりにくいが、mcpp 自身の compiler-independent 版のコンパイルを例にとると次のようなことである（生成する mcpp の実行プログラムの名前を mcpp.exe とする）。

       「ビルドコマンドライン」:   nmake
       「出力」                :   mcpp.exe
       「クリーンコマンド」    :   nmake clean
       「リビルドコマンド」    :   nmake PREPROCESSED=1

   mcpp の Visual C 専用版をコンパイルする場合は COMPILER=MSC というオプションを付け加えて次のようにする。

       「ビルドコマンドライン」:   nmake COMPILER=MSC
       「出力」                :   mcpp.exe
       「クリーンコマンド」    :   nmake clean
       「リビルドコマンド」    :   nmake PREPROCESSED=1 COMPILER=MSC

   「メイクファイルプロジェクト」では make install に相当するコマンドがないので、「ビルドコマンドライン」「リビルドコマンド」で指定されるコマンドでは install も実行されるように makefile を書いておく必要がある。*4
   mcpp をコンパイルするのでなければ、「ビルドコマンドライン」と「リビルドコマンド」とは通常は同一で良い。
   これらを入力したら、「完了」をクリックする。
6. すると、 「ソリューションエクスプローラ」にプロジェクトが現れるので、その「ソースファイル」というフォルダをクリックして、そして、メニューの「プロジェクト」から「既存項目の追加」を選び、ソースファイルをすべて選択して「OK」する。すると、「ソリューションエクスプローラ」にソースファイル名が現れる。

これで、「編集」「ビルド」「リビルド」「デバッグ」等の機能がすべて使えるようになります。

注:

*1 VC 2003, 2005 でデバッグ機能を使うためには、WindowsXP Pro, Windows2000 では "Debugger users" というグループにユーザを所属させる必要がある。WindowsXP HE ではそういうグループはないので、管理者としてログインしなければならない。
VC 2008 ではユーザグループの制限はなくなった。

*2 ソースレベル・デバッグ機能を使うためには cl.exe の呼び出しに -Zi オプションを付加して、デバッグ情報が生成されるように makefile を書いておく必要がある。

*3 「スタート」->「プログラム」から起動すると、インクルードディレクトリ等の環境変数が設定されない。これを設定するには先に「Visual Studio コマンドプロンプト」を開いて、ソースファイルのあるディレクトリに移動し、そこから VC 2003 では

    devenv *.sln /useenv

として、VC 2005, 2008 express edition では

    vcexpress *.sln /useenv

として起動しなければならない。

*4 インストールするディレクトリにはユーザが書き込みの権限を持っていなければならない。 処理系の bin, lib 等のディレクトリに書き込む時は、そのパーミッションを管理者権限で変更しておく必要がある。 "Power users" または "Authenticated users" というグループにユーザを登録し、このグループに該当のディレクトリへの「書き込み」「変更」の権限を持たせるのが良い。 もう１つの方法は、「共有ディレクトリ」のような、ユーザが書き込み権限を持つディレクトリに処理系をインストールすることである。

### 2.10.2. Mac OS X / Xcode.app で mcpp を使う方法

Mac OS X の IDE である Xcode.app は mcpp をインストールした状態で問題なく使うことができます。*1

Xcode.app はなぜか /usr/bin ではなく /Developer/usr/bin にある gcc (g++) を使います（/Developer は Xcode のデフォルトのインストールディレクトリ）。 したがって、mcpp を使うには、その gcc (g++) 用の GCC-specific-build をインストールする必要があります。 そのためには次のようにします（${mcpp_dir} は mcpp のソースのあるディレクトリ）。

    export PATH=/Developer/usr/bin:$PATH
    configure ${mcpp_dir}/configure --enable-replace-cpp
    make
    sudo make install

PATH の設定以外は /usr/bin のコンパイラにインストールする場合と同じなので、クロスコンパイラへのインストールや universal binary をインストールする方法などについては、INSTALL-jp を見てください。

こうして mcpp をインストールしておけば、あとは mcpp のための特別な設定はせずに Xcode.app が使えます。 Xcode.app は Apple-GCC 特有な *.hmap という名前の "header map file" なるものを生成しますが、これも mcpp で処理されます。 ただし、mcpp は precompiled header の処理はしないので、`#include *.pch` は通常のヘッダファイルとして読み込まれます。 また、mcpp は Objective-C, Objective-C++ の処理はしないので、*.m, *.mm のソースファイルのプリプロセスは mcpp を通らずに cc1obj, cc1objplus に直接、渡されます。

mcpp 独自のオプションを使う場合は、Xcode.app の画面トップのメニューバーの「プロジェクト」から「プロジェクト設定を編集」を選んでクリックし、そこで現れるプロジェクトエディタのウィンドウで「ビルド」ペインを選んで、「その他の C フラグ」の項目を編集します。 オプションは次の例のように -Wp, に続けてコンマで区切りながら指定します。

    -Wp,-23,-W3

注:

*1 ここで取り上げるのは Mac OS X Leopard / Xcode 3.0 である。

# 3. 拡張機能と互換性

mcpp にはいくつかの固有の拡張機能があります。また、各処理系付属のプリプロセッサにはそれぞれの拡張機能がありますが、それらの一部は mcpp では使えません。ここではこうした拡張機能と互換性の問題を説明します。

なお、Standard モードでは #pragma 行は原則としてそのまま出力します。mcpp 自身が処理するものについても同様です。同じ #pragma がコンパイラ本体にとっても意味を持つ可能性があるためです。
しかし、#pragma MCPP で始まる行は mcpp 専用のものなので出力しません。GCC 版では、#pragma GCC に poison, dependency, system_header のどれかが続く行も出力しません。また、#pragma once, #pragma push_macro, #pragma pop_macro もコンパイラ本体にとっては無用なので出力しません。 他方で、#pragma GCC visibility * はコンパイラとリンカのためのものなので、出力します。*1

EXPAND_PRAGMA == TRUE でコンパイルされた mcpp では #pragma 行の引数はマクロ展開の対象となります（実際には EXPAND_PRAGMA == TRUE は Visual C, Borland C 版だけである）。ただし、#pragma に STDC, MCPP, GCC のどれかが続く行は展開しません。

#pragma sub-directive は implementation-defined ですが、そのため同じ名前の sub-directive が処理系によって異なる意味を持つ恐れがあります。名前の衝突を避ける工夫が必要です。また、EXPAND_PRAGMA == TRUE の場合は、#pragma sub-directive の名前自身がマクロ展開されては困るので、ユーザの名前空間と重ならないようにする仕組みも必要です。mcpp 固有の sub-directive が #pragma MCPP で始まりマクロ展開されないのはこのためです。C99 で規定された #pragma STDC や GCC 3 の #pragma GCC の方法を採り入れたものです。
ただし、#pragma once は多くの処理系に実装されて名前が衝突する恐れはなくなっているので、この名前のまま実装しています。また、#pragma __setlocale はコンパイラ本体に対しても必要なので MCPP という名前は付けず、"__" を先頭に付けてユーザの名前空間と重ならないようにしています。

注

*1 mcpp の GCC-specific-build では、#pragma GCC で始まる pragma のうち system_header だけサポートしている。poison, dependency はサポートしない。

## 3.1. #pragma MCPP put_defines, #pragma MCPP preprocess 等

#pragma MCPP put_defines, #pragma MCPP preprocess, #pragma MCPP preprocessed は Standard モードのもので、#put_defines, #preprocess, #preprocessed は pre-Standard モードのものです。以下では #pragma を例にとって説明します。

#pragma MCPP put_defines ディレクティブに出会うと mcpp は、その時点で定義されているすべてのマクロを #define 行の形で出力します。もちろん、#undef されたものは出てきません。`__STDC__` 等の #define, #undef の対象にできないものは、一応 #define 行の形をとって、しかしコメントマークで囲んで出力されます（`__FILE__`, `__LINE__` はマクロ呼び出し時に動的に定義される特殊なマクロなので、ここで出力される置換リストは無意味なものである）。
pre-Standard モードおよび POSTSTD モードでは、function-like マクロ定義のパラメータ名は記憶しません。そこでこのディレクティブでは、パラメータ名は第１パラメータから順に機械的に a, b, c, ... という名前で表示します。27 個目以降のパラメータには a1, b1, c1, ..., a2, b2, c2, ... という名前を使います。

mcpp を入力ファイルも出力ファイルも指定せずに起動して、キーボードからいきなり

    #pragma MCPP put_defines

と打ち込むと、事前定義マクロをすべて知ることができます。それぞれのマクロ定義のあるソースファイル名と行番号を表示するコメントも出力されます。-S1, -N 等のオプションを付けて起動すると、それぞれ事前定義マクロが違ってくることがわかります。

#pragma MCPP preprocess というディレクティブに出会うと mcpp は、

    #pragma MCPP preprocessed

という行を出力します。これは、このソースファイルはプリプロセス済みであることを示すものです。

#pragma MCPP preprocessed というディレクティブに出会うと mcpp は、そのソースファイルは mcpp によってプリプロセス済みであると判断して、#define 行が出てくるまでは入力をそのまま出力にコピーします。そして、#define 行が出てくると、あとはすべて #define 行であると判断して、マクロを定義します。コメント中にあるソースファイル名と行番号の情報も記憶します。*1, *2
#pragma MCPP preprocessed の有効範囲はそのディレクティブのあるソースファイルのその行以降だけです。そのソースファイルが #include されたものである場合は、include 元に戻ると通常のプリプロセスに戻ります。

注:

*1 実際の処理はもう少し複雑である。#pragma MCPP preprocessed があると入力行の大半をそのまま出力にコピーするが、標準事前定義マクロは #define 行がコメントマークに囲まれているので、その行は捨てる。また、特定の処理系用の版では #line 行は処理系のコンパイラ本体が受け取れる形式に変換して出力する。

*2 したがって、pre-preprocess してもマクロ定義の場所の情報は失われない。

### 3.1.1. ヘッダファイルの pre-preprocess

上記のディレクティブを利用すると、ヘッダファイルの「プリプリプロセス」をすることができます。「プリプリプロセス」をしておくと、本番のプリプロセス時間がかなり短縮されます。その方法は、上記の仕様ですでにわかったかと思いますが、念のために mcpp 自身のソースを例にとって説明します。

mcpp のソースには８本の *.c ファイルがあり、そのうちの７本はどれも "system.H" と "internal.H" を include しています。そして、他のヘッダは include していません。もっと正確に言うと、ソースではこうなっています。

    #if PREPROCESSED
    #include    "mcpp.H"
    #else
    #include    "system.H"
    #include    "internal.H"
    #endif

そして、system.H は noconfig.H または configed.H といくつかの標準ヘッダを include しています。mcpp.H は私の提供するソースにはありません。これが、これから生成する "pre-pre-processed" header なのです。

mcpp.H を生成するには（もちろん noconfig.H 等の設定がすんでから）、

    mcpp > mcpp.H

として mcpp を起動します（GCC 等では、-b オプションも付ける）。
そして、キーボードから

    #pragma MCPP preprocess
    #include "system.H"
    #include "internal.H"
    #pragma MCPP put_defines

と打ち込み、end-of-file を入力して mcpp を終了します。

これで mcpp.H ができあがりました。これは system.H, internal.H をプリプロセスしたものの末尾に #define 行の集合を付け加えたものです。これを include すれば、system.H, internal.H を include したのとまったく同じ効果を得ることができます。そして、これは標準ヘッダを含む元のヘッダファイルの総計の数分の１のサイズになっています。#if とコメントが消えているからです。これを７本の *.c ファイルで include するのは、system.H, internal.H を７回 include するのに比べて、はるかに短い時間ですみます。#pragma MCPP preprocess を使うことでさらに時間が短縮されます。

本番のコンパイルでは -DPREPROCESSED=1 というオプションを付けます。
この手順は何かのファイルに書いておいて、makefile でそれを参照するのが良いでしょう。mcpp のソースに付けた makefile と preproc.c には、それが書いてあるので、そちらを見てください。
Visual C, Borland C, LCC-Win32 のような１パス・コンパイラでは独立したプリプロセッサの使い道は制限されますが、その場合でもこの機能は有用です。

このヘッダファイルの pre-preprocess の機能は、GCC / cpp の -dD オプションの機能を真似たものです。ただし、次の点が違っています。

1. GCC は行番号情報を #line 123 "filename" ではなく # 123 "filename" の形で出力する。このため、それを GCC で再処理することはできるが、Standard C のプリプロセッサではできない。
2. GCC の古い cpp では #define 行は出現したところで出力されるが、#undef 行は出力されない。したがって、これを再処理すると元ソースの意図と異なる結果になることがあった。
3. GCC にはない #pragma MCPP preprocess を使うことで、さらに速度が速くなる。

Pre-preprocess の機能としては、mcpp のほうが間違いがなく実用的です。

## 3.2. #pragma once

#pragma once は Standard モードで使えます。
GCC, Visual C, LCC-Win32 および Wave という単体プリプロセッサでも #pragma once は使えます。
ヘッダファイルを１回しかインクルードしたくない時に使います。ヘッダファイルの中に

    #pragma once

と書いておくと、そのファイルをインクルードする #include 行が何回出てきても、最初の１回しかインクルードしません。

通常は、処理系付属の標準ヘッダでは

    #ifndef __STDIO_H
    #define __STDIO_H
    /* stdio.h の中身   */
    #endif

等の皮でくるんで多重定義を防いでいますが、それと似た機能です。しかし、マクロを使う方法ではヘッダファイルを読まないですますことはできません（スキップする部分でも、#if, #endif 等が出てくるのを監視するために、全部読まなければならない。行頭の # がディレクティブ行（# に preprocessing directive が続く行）の指示であるかどうかを確かめるためにはコメントも処理しなければならないし、そのためには文字列リテラルも判断しなければならない等で、結局、全部読んで tokenization の大半までやらなければならないのである）。
#pragma once は、ファイルへのアクセスさえもしないですますものです。その結果、多重 include がある場合の処理速度がやや速くなります。

Header name が同じであるかどうかは、サーチしたパスのディレクトリ部分も含めて文字の比較で判断します。ただし、Windows では大文字・小文字は区別しません。したがって、"/DIR1/header.h" と "/DIR2/header.h" は別のものとして扱い、"header.h" と "HEADER.H" とは Windows では同じもの、UNIX 系では別のものとして扱います。ディレクトリは絶対パスに変換して記憶します。`"foo/../"` といった冗長な部分は削除して正規化します。UNIX 系では symbolic link はリンク先に変換します。したがって、同じファイルであるかどうかは確実に判定されます。*1, *2, *3

この #pragma once は GCC V.1.* / cpp の #pragma once のアイデアを借用したものです。GCC V.2.*, V.3.* でもこの機能は残っていますが、obsolete なものとされています。#pragma once がなくても、ヘッダファイルの全体が #ifndef _MACRO, #define _MACRO, #endif で囲まれていれば、cpp がこれを記憶し、１回しか include しないという仕様に変更されています。
しかし、GCC V.2, V.3 の仕様は、GCC の使用を前提としない市販の処理系などでは使えないことがあります。標準ヘッダの書き方が違っているからです。また、実装も GCC V.2, V.3 の仕様のほうが面倒です。そこで、mcpp では #pragma once だけを実装しています。

他のプリプロセッサでも同じヘッダファイルを使う場合はこれだけに頼るわけにはゆきません。マクロを使う方法と併用して、ヘッダファイルを次のような皮でくるんでおくのが良いでしょう。

    #ifndef __STDIO_H
    #define __STDIO_H
    #pragma once
    /* stdio.h の中身   */
    #endif

ただし、<assert.h> には #pragma once は書いてはいけません（その理由は cpp-test.html#5.1.2 参照）。C++ の <cassert>, <cassert.h> 等も同様です。

もう一つの問題は、GCC / GLIBC の最近のシステムでは <stddef.h> のように、他の system header から繰り返し #include されるヘッダファイルがあることです。多くの system header が `__need_NULL`, `__need_size_t`, `__need_ptrdiff_t`, etc. のマクロを定義しては <stddef.h> を #include します。そのたびに、<stddef.h> では `NULL`, size_t, ptrdiff_t, etc. が定義されてゆきます。<errno.h>, <signal.h> 等も同様です。<stdio.h> でさえも、他の system header が `__need_FILE, __need___FILE` 等のマクロを定義しては #include <stdio.h> し、そのたびに FILE 等が定義されてゆく場合があります。これらのファイルには #pragma once は書き込むわけにはいきません。*4

注:

*1 正規化された結果は `#pragma MCPP debug path` で見ることができる。3.5.1 参照。 `#pragma MCPP put_defines` や診断メッセージのファイル名の表示でも、これが使われる。
しかし、#line 行の path-list は一般には正規化されているわけではなく、include directory だけ正規化した状態で表示される。 ただし、-K オプションでは正規化して表示される。 このモードを利用する他のツールの処理を用意にするためである。

*2 CygWIN では /bin と /usr/bin、/lib と /usr/lib はそれぞれ同じディレクトリで、/ が Windows 上の例えば C:/dir/cygwin だとすると /cygdrive/c/dir/cygwin もそれと同じディレクトリであるが、mcpp ではこれらはすべて同じディレクトリとして扱う。path-list はすべて /cygdrive/c/dir/cygwin/dir-list/file の形に変換する。

*3 MinGW では / と /usr とは実際には同じディレクトリで、/ が Windows 上の C:/dir/msys/1.0 だとすると /c/dir/msys/1.0 もそれと同じディレクトリで、/mingw が C:/dir/mingw だとすると /c/dir/mingw もそれと同じディレクトリである。mcpp ではこれらはそれぞれ同じディレクトリとして扱う。path-list はすべて c:/dir/msys/1.0/dir-list/file, c:/dir/mingw/dir-list/file の形に変換する。

*4 少なくとも Linux / GCC 2.9x, 3.*, 4.* / glibc 2.1, 2.2, 2.3 ではそうなっている。FreeBSD 4, 5, 6 では glibc は使われていないので、こういう複雑なシステムヘッダにはなっていない。

### 3.2.1. ヘッダファイルに #pragma once を書き込むツール

これを書き込むのはヘッダファイルの数が少なければ大したことではありませんが、数が多いと手ではうっとおしい作業になります。そこで、これを自動的に書き込む簡単なツールを用意しました。

tool/ins_once.c は古い GCC のシステム用です。Borland C 5.5 でも標準ヘッダの書き方は同じルールに従っているので、これを使うことができます。Glibc 2 のようなシステムでは上記のように例外が多いので、使わないほうが無難です。
ただし、これが使えるシステムでも、ヘッダファイルの中には GCC の慣習に従っていないものも散見されます。そうしたヘッダでは GCC の、１回しか読み込まないという機能も動作しません。
そこで、ins_once.c をコンパイルして、UNIX なら /usr/include, /usr/local/include 等のディレクトリで、まず

    chmod -R u+w *

とした上で、

    ins_once -t *.h */*.h */*/*.h

とします。そうすると、#ifndef または #if !defined で始まらないヘッダファイルが報告されます。それらのヘッダを手で修正してください。それから、

    ins_once *.h */*.h */*/*.h

とすると、各ヘッダファイルの最初に出現する #directive が #ifndef または #if !defined であった場合は、その直後に #pragma once 行が書き込まれます。（これができるのは root と特定のユーザだけのはず。さらに必要なら chmod -R u-w * として、access permission を元に戻しておく）。

ins_once には次のようなオプションがあります。システムに合わせて適当なオプションを選択してください。

- -t: ファイルが（コメントを除くと）#ifndef か #if !defined で始まっているかどうかをテストする。ファイルは書き換えない。
- -p: ファイルの冒頭に #pragma once 行を書き込む（デフォルトでは #ifndef / #if !defined 行の次に書き込む）。
- -g: GCC のために、<stddef.h>, <stdio.h>, <signal.h>, <errno.h> も書き換えない（デフォルトでは書き換えないのは <assert.h>, <cassert>, <cassert.h> だけ）。

ins_once は複数回実行しても同じファイルにダブって書き込むことはないように、簡単なチェックはしています。しかし、厳密なものではありません。

この ins_once は間に合わせなので、tokenization はほとんどやっていません。FreeBSD 2.0, 2.2.7, Borland C 5.5 の各ヘッダファイルでは期待通りの動作をしましたが、特殊なヘッダファイルがあると誤動作するかもしれません。ins_once は必ずバックアップをとってから実行してください。
ワイルドカードは shell に展開させてください（バッファがオーバーフローする場合は、何回かに分けて実行する）。

## 3.3. #pragma MCPP warning, #include_next, #warning

これらのディレクティブは GCC との互換性のために用意されているものです。GCC には #include_next, #warning という規格違反のディレクティブがあります。規格違反ですが、これを使っているソースも稀にあります。Glibc 2 のシステムでは、システムヘッダファイルにこれを使っているものさえあります。そこで mcpp では、これらのソースをコンパイルできるようにするため、GCC 用に限って #include_next, #warning を実装しています。ただし、Standard モードではウォーニングの対象となります。Standard モードでは #pragma MCPP warning も実装しています。これは GCC 用に限りません。

    #include_next   <header.h>

は include directory をサーチする際に、この include 元のファイルのあるディレクトリをスキップして、その次のサーチ順のディレクトリからサーチを始めます。

CygWIN, MinGW では、ヘッダ名の大文字・小文字の区別は無視します。

    #pragma MCPP warning    any message

    #warning    any message

では、any message をそのまま warning として標準エラー出力に出力します。しかし、これは #error と違ってエラーにはなりません。

## 3.4. #pragma MCPP push_macro, #pragma __setlocale 等

これらは Visual C に mcpp を移植した時に実装し、ついでに他の処理系でも使えるようにしたものです。

#pragma MCPP push_macro( "MACRO"), #pragma MCPP pop_macro( "MACRO") は、その時点での MACRO のマクロ定義をスタックに "push" したり "pop" したりするものです。
Visual C では #pragma push_macro( "MACRO"), #pragma pop_macro( "MACRO") も使えます。
push_macro ではそのマクロの定義が退避され、pop_macro で元に戻されますが、push してもそのマクロ定義はまだ有効です。これを無効にするためには #undef するか、または別の定義で再定義する必要があります。push_macro では同じ名前のマクロを何重にも push することができます。

#pragma __setlocale( "<encoding>") は multi-byte character encoding を <encoding> に変更します。__setlocale の引数は文字列リテラルでなければなりません。<encoding> については 2.8 を見てください。これを使うと、１つの translation unit の中でも複数の encoding を使うことができます。
Visual C++ では #pragma setlocale であり、#pragma __setlocale は使えません。Encoding の指定は mcpp だけでなく、コンパイラにも伝える必要がありますが、コンパイラが認識できるのは #pragma setlocale だけだからです。

#pragma __setlocale を認識できるコンパイラ本体はいまのところ、ありません。

## 3.5. #pragma MCPP debug, #pragma MCPP end_debug, #debug, #end_debug

#pragma MCPP debug, #pragma MCPP end_debug は Standard モードのものです。pre-Standard モードでは #debug, #end_debug となります。

#pragma MCPP debug <args> ディレクティブはソース中の任意の行に書くことができます。<args> でデバッグ情報の種類を指定します。１つの #pragma MCPP debug ディレクティブで複数の <arg> を指定することができます。必ず１つ以上の <arg> 指定が必要です。このディレクティブがあると、そこからデバッグ情報の出力が始まります。そして、#pragma MCPP end_debug <args> で、<args> のデバッグ情報出力が解除されます。#pragma MCPP end_debug では、<args> を省略することができます。その場合は、設定されていたすべてのデバッグ情報出力が解除されます。mcpp でサポートしない引数が <args> にあった時は、ウォーニングを出しますが、その前にあった正しい引数は有効です。
デバッグ情報はすべて、プリプロセスの本来の出力と同じパスに出力されます。これは本来の出力と同期させるためです。したがって、このディレクティブがあると通常はコンパイルできません。 ただし、#pragma MCPP debug macro_call では情報はコメントに埋め込んで出力されるので、それを再プリプロセスしてコンパイルすることができます。

プリプロセスの結果に疑問がある場合、その部分を

    #pragma MCPP debug token expand
    /* デバッグしたい部分   */
    #pragma MCPP end_debug

というふうにはさんで使います。

元来は mcpp 自身のデバッグ用のものですが、プリプロセスの過程をトレースしたい時に使えます。元来の目的が目的なので、ソースを見ないと理解できないところもあり、少々うるさくもありますが、がまんしてください。

<arg> の種類は次の通りです。

> path | include ファイルのサーチパスを表示する
> token | token を１つずつ切り分けて、その種類を表示する
> expand | マクロ呼び出しの展開過程をトレースする
> macro_call | マクロ定義およびマクロ呼び出しに際して、そのソース上の行とカラム位置をコメントに埋め込んで出力する
> if | #if (#elif, #ifdef, #ifndef) の真偽を表示する
> expression | #if 式の評価をトレースする
> getc | プリプロセスを 1 byte ずつトレースする
> memory | mcpp の使っているヒープメモリの状況を表示する

### 3.5.1. #pragma MCPP debug path, #debug path

このディレクティブに出会うと mcpp は、まず設定されている include ディレクトリのサーチパスを優先順位の高いものから順に表示します（ただし、最初にサーチされるカレントディレクトリおよびソースのディレクトリは省略）。

さらに、#include 行があると、そのヘッダファイルを include するために mcpp が実際にサーチしたディレクトリが（カレントディレクトリ等も含めて）すべて表示されます。
#pragma once のあるヘッダファイルを再度 #include した場合は、その旨を表示します。
また、mcpp は path-list 中の `"foo/../"` といった冗長な部分を削除して正規化しますが、その結果、元の path-list と違った path-list となった場合は、その旨を表示します。
UNIX 系のシステムでは mcpp は symbolic link は link 先に変換しますが、この場合もその旨を表示します。

### 3.5.2. #pragma MCPP debug token, #debug token

まず、読み込んだソース行を表示した上で、mcpp が token を１つ読むたびに、その token と種類を表示します。Token とは正確に言えば preprocessing-token (pp-token) のことです。ソースを読む時ばかりではなく、mcpp がマクロ展開などで内部的に読み返す pp-token も、そのつど（繰り返して）表示されます。

ただし、1 byte の pp-token のうち次のものは表示されません。これはプログラムのつごうによるものです。

1. プリプロセスディレクティブ行の開始の '#'
2. function-like マクロ定義のパラメータリスト開始の '('
3. function-like マクロ定義のパラメータを区切る ','
4. function-like マクロ呼び出しの引数リスト開始の '('

Pp-token の種類は次の通りです。

> (NAM) | identifier
> (NUM) | preprocessing-number
> (OPE) | operator or punctuator
> (STR) | string literal
> (WSTR) | wide string literal
> (CHR) | character constant
> (WCHR) | wide character constant
> (SPE) | $, @ 等の特殊な pp-token
> (SEP) | token separator (white space)

これらのうち (SEP) は改行コード以外は通常は表示されません。 改行コード等のコントロールコードは <^J>, <^M> 等と表示されます。

### 3.5.3. #pragma MCPP debug expand, #debug expand

マクロ呼び出しの展開過程をトレースします。

Standard モードの #pragma MCPP debug では次の通りです。
マクロ呼び出しがあると、まずそのマクロの定義が表示されます。さらに、引数が読み込まれ、置換リスト中のパラメータと置き換えられ、再走査されるようすが、逐一表示されます。マクロ定義がネストされていれば、それが順次再走査されて展開されていきます。引数中にマクロがあれば、この過程が再帰的に（パラメータとの置換の前に）トレースされます。

表示は mcpp 自身のいくつかの関数への出入りのたびに、その関数名とともに行われます。これらの関数は次のような役割をするルーチンです。Standard モードのソースを参照すると、さらによく理解できます。

> expand_macro | マクロ展開の入り口ルーチン
> replace | マクロを１レベル展開する
> collect_args | 引数を集める
> prescan | 置換リストを走査して #, ## 演算子の処理をする
> substitute | パラメータを引数で置換する
> rescan | 置換リストを再走査する

これらのうち、expand_macro 以外は互いに間接再帰の関係にあります。

replace, collect_args では、mcpp が内部的にスタックに積んでいる展開途中のデータも表示されます。これらのデータでは、mcpp の内部的なコードが次のような記号で表示されます。

> <n> | n 番目のパラメータ
> <TSEP> | pp-token を区切るために mcpp が挿入した token separator
> <MAGIC> | 同名マクロの再置換を禁止するコード
> <RT_END> | 置換リストの終わりを示すコード
> <SRC> | Identifier がソースファイルから取り込まれたことを示すコード

このうち <SRC> は STD モードでだけ使われ、POSTSTD モードでも COMPAT モードでも使われません。

#pragma MCPP debug token も指定したほうが、わかりやすいでしょう。

#pragma MCPP debug macro_call または -K オプションも指定した場合は、macro notification がコメントに書き込まれて出力されますが、replace() 以下のルーチンではまだコメントは書き込まれず、何種類かの magic character（内部的なコード）が書き込まれたり削除されたりします。 これは次のように表示されます。

> <MACm> | １つのマクロ呼び出しの中に含まれる m 番目のマクロの呼び出し
> <MAC_END> | 直前の MACm で始まるマクロ呼び出しの終わり
> <MACm:ARGn> | m 番目のマクロの呼び出し中の n 番目の引数
> <ARG_END> | 直前の MACm:ARGn で始まる引数の終わり

さらに -v オプションも指定した場合は、MAC_END, ARG_END についても開始マーカと同じ番号が symmetrical に表示されます。

pre-Standard モードの #debug expand では Standard モードとは内部ルーチンが大幅に違っています。説明は略します。

### 3.5.4. #pragma MCPP debug if, #debug if

#if, #elif, #ifdef, #ifndef の行を表示し、その評価が真であるか偽であるかを報告します。スキップされる #if section 内では、報告されません。

### 3.5.5. #pragma MCPP debug expression, #debug expression

#if, #elif 行の式の評価を詳細にトレースします。

これは DECUS cpp 自身のデバッグ用にオリジナル版以来あるもので、私はほとんど手を加えていません。内部的な関数名ばかりか、変数名とその値までズラズラと出てきます。mcpp のソースを追いながらでないと、変数は理解できません。
しかし、複雑な式の値が評価用のスタックに積み降ろしされていくようすは、ソースを見なくても何とか理解できるでしょう。

### 3.5.6. #pragma MCPP debug getc, #debug getc

mcpp 内の get_ch() という１バイト読み込み関数が呼び出されるたびに、詳細なデータを出力します。ただし、Standard モードでは pp-token をスキャンする時は、その１バイト目しかこのルーチンは呼び出されません。

#debug getc では token をスキャンする最中もこのルーチンが呼び出されるので、とんでもない量のデータが吐き出されます。

いずれにしても、膨大なデータが出力されます。使う必要はまずありません。

### 3.5.7. #pragma MCPP debug memory, #debug memory

このディレクティブがあると、その時点で mcpp が内部的に使っている malloc(), realloc(), free() によるヒープメモリの状況を１回だけ報告します。これは私の作った kmmalloc や他の何種類かの malloc() を使っている場合だけの機能です（ mcpp-porting.html#4.extra 参照）。他の malloc() の場合はエラーにはしませんが、何も報告しません。
このディレクティブが解除されないまま mcpp が終了すると、その時に再度ヒープメモリの状況が報告されます。mcpp が out of memory で終了した場合も同様です。

### 3.5.8. #pragma MCPP debug macro_call

Macro notification mode を開始します。 このモードでは、マクロが定義されるたびに、またマクロが展開されるたびに、そのソース上の行とカラム位置がコメントに埋め込まれて出力されます。 マクロが引数を持つ場合は、各引数についてもその位置が報告されます。 ただし、マクロ展開によってトークンが連結された場合は、連結前のトークンに関するマクロ情報は失われます。
さらに、#undef, #if (#elif, #ifdef, #ifndef), #endif についても、簡単な情報が出力されます。
このモードは -K オプションで指定することもできます。

このモードは C/C++ の refactoring tool のために実装されたものです。 C/C++ にはプリプロセスというフェーズのあることが refactoring tool にとって困難な問題を引き起こしますが、このモードを使えばプリプロセス後の出力からソースを再構成することができるので、tool を作成しやすくなります。*1

#pragma MCPP debug expand と似ていますが、expand のほうはマクロの展開過程をトレースするためのもので、詳細な情報が出力されるものの、それをコンパイルすることはできません。 それに対して、macro_call はソース上の正確な位置を伝えるためのもので、情報をコメントに埋め込むので、出力をそのまま再プリプロセスしてコンパイルすることができるのが特徴です。

注:

*1 この仕様は主として Taras Glek の提案によるものである。 彼自身は次のところで mozilla のソースの refactoring に取り組んでいる。

http://blog.mozilla.com/tglek/

#### 3.5.8.1. #define に関するコメント

例えば、ソースの冒頭に次のようなマクロ定義があると、

    #define NULL 0L
    #define BAR(x, y) x ## y
    #define BAZ(a, b) a + b

次のようなコメントが出力されます。

    /*mNULL 1:9-1:16*/
    /*mBAR 2:9-2:25*/
    /*mBAZ 3:9-3:24*/

このフォーマットは

`/*m[NAME] [start line]:[start column]-[end line]:[end column]*/`

を意味します。 行とカラムはともに 1 から始まります。 -K オプションを指定した場合は事前定義マクロも出力されますが、事前定義マクロには位置情報はありません。

#### 3.5.8.2. #undef に関するコメント

    #undef  BAZ

という行では、次のようなコメントが出力されます。

    /*undef 10*//*BAZ*/

/*undef [lnum]*//*[NAME]*/ というフォーマットで、[lnum] はその行の行番号を、[NAME] はそのマクロ名を示します。

#### 3.5.8.3. マクロ展開に関するコメント

マクロが呼び出されるたびに、それを挟んでその開始と終了を示すマーカが出力されます。 このマーカのフォーマットは HTML に似たネスト可能なもので、`/*<...*/` がマクロ展開の開始を示し、`/*>*/` がその終了を示します。 マクロ開始のフォーマットはマクロ定義のフォーマットの `/*m` を `/*<` に変えた次の形をとります。

`/*<[NAME] [start line]:[start column]-[end line]:[end column]*/`

マクロが引数をとる場合は、ソース上の引数の位置を示すマーカと、引数の展開の開始と終了を示すマーカも出力されます。 引数の位置を示すフォーマットは `/*!...*/` という形をしています。 引数中にマクロがある場合は、再帰的にそのマクロの情報が出力されます。 ただし、そのマクロがソース上にあったものでなければ、位置情報は出力されません。 どのマクロの何番目の引数であるかを示すために、引数の id が次の形で示されます。

`[func-like-macro-name]:[nesting level]-[argument number]`

これによって `BAZ(BAZ(a,b), c)` といったネストされた同名のマクロとその引数も互いに区別することができます。 引数の番号は 0 から始まります。 そして、これに次の形の位置情報が続きます。

`[start line]:[start column]-[end line]:[end column]`

また、引数の展開の開始を示すマーカは次の形です。

`/*<[func-like-macro-name]:[nesting level]-[argument number]*/`

引数の展開の終了を示すマーカはマクロ展開の終わりのマーカと同じ `/*>*/ です。`

すなわち、次のソースは

    foo(NULL);
    foo(BAR(some_, var));
    foo = BAZ(NULL, 2);
    bar = BAZ(BAZ(a,b), c);

次のような結果を出力します。

    foo(/*<NULL 4:5-4:9*/0L/*>*/);
    foo(/*<BAR 5:5-5:20*//*!BAR:0-0 5:9-5:14*//*!BAR:0-1 5:16-5:19*/some_var/*>*/);
    foo = /*<BAZ 6:7-6:19*//*!BAZ:0-0 6:11-6:15*//*!BAZ:0-1 6:17-6:18*//*<BAZ:0-0*//*<NULL 6:11-6:15*/0L/*>*//*>*/ + /*<BAZ:0-1*/2/*>*//*>*/;
    bar = /*<BAZ 7:7-7:23*//*!BAZ:0-0 7:11-7:19*//*!BAZ:0-1 7:21-7:22*//*<BAZ:0-0*//*<BAZ 7:11-7:19*//*!BAZ:1-0*//*!BAZ:1-1*//*<BAZ:1-0*/a/*>*/ + /*<BAZ:1-1*/b/*>*//*>*//*>*/ + /*<BAZ:0-1*/c/*>*//*>*/;

さらに -v オプションが指定されている時は、マクロ展開の終わりのマーカでも引数の展開の終了を示すマーカでも、開始マーカにあるのと同じマクロ名と引数の id が出力されます。 すなわち、次のようになります。

    foo(/*<NULL 4:5-4:9*/0L/*NULL>*/);
    foo(/*<BAR 5:5-5:20*//*!BAR:0-0 5:9-5:14*//*!BAR:0-1 5:16-5:19*/some_var/*BAR>*/);
    foo = /*<BAZ 6:7-6:19*//*!BAZ:0-0 6:11-6:15*//*!BAZ:0-1 6:17-6:18*//*<BAZ:0-0*//*<NULL 6:11-6:15*/0L/*NULL>*//*BAZ:0-0>*/ + /*<BAZ:0-1*/2/*BAZ:0-1>*//*BAZ>*/;
    bar = /*<BAZ 7:7-7:23*//*!BAZ:0-0 7:11-7:19*//*!BAZ:0-1 7:21-7:22*//*<BAZ:0-0*//*<BAZ 7:11-7:19*//*!BAZ:1-0*//*!BAZ:1-1*//*<BAZ:1-0*/a/*BAZ:1-0>*/ + /*<BAZ:1-1*/b/*BAZ:1-1>*//*BAZ>*//*BAZ:0-0>*/ + /*<BAZ:0-1*/c/*BAZ:0-1>*//*BAZ>*/;

この例でもわかるように、マクロ展開終了のマーカも引数展開終了のマーカも、それぞれその前にある最後の同じネストレベルの開始マーカに対応しています。 したがって、-v オプションを指定しなくても、自動的に対応関係を判断することができます。

#### 3.5.8.4. #if (#elif, #ifdef, #ifndef) に関するコメント

#if (#elif, #ifdef, #ifndef) 行に関しては、その行にあるマクロ等の情報が出力されます。 例えば、このソースを bar.h とし、

    #define NULL 0L
    #define BAR(x, y) x ## y
    #define BAZ(a, b) a + b

こちらを foo.c とすると、

    #include "bar.h"
    #ifdef  BAR
    #ifndef BAZ
    #if 1 + BAR( 2, 3)
    #endif
    #else
    #if 1
    #endif
    #if BAZ( 1, BAR( 2, 3))
    #undef  BAZ
    #endif
    #endif
    #endif

foo.c は次のような結果を出力します。

    #line 1 "/dir/foo.c"
    #line 1 "/dir/bar.h"
    /*mNULL 1:9-1:16*/
    /*mBAR 2:9-2:25*/
    /*mBAZ 3:9-3:24*/
    #line 2 "/dir/foo.c"
    /*ifdef 2*//*BAR*//*i T*/
    /*ifndef 3*//*BAZ*//*i F*/

    /*else 6:T*/
    /*if 7*//*i T*/
    /*endif 8*/
    /*if 9*//*BAZ*//*BAR*//*i T*/
    /*undef 10*//*BAZ*/
    #line 11 "/dir/foo.c"
    /*endif 11*/
    /*endif 12*/
    /*endif 13*/

すなわち、まず /*if [lnum]*/ というフォーマットで、ディレクティブ名に続いて現在の行番号が表示されます。 そして、その行にマクロがあれば、それが１つずつ /*[NAME]*/ というフォーマットで表示されます。 最後に、/*i T*/ または /*i F*/ で、その行の評価が true であるか false であるか、すなわちその #if で始まるブロックがコンパイルされるブロックかスキップされるブロックかが示されます。 地の文と違って、マクロの展開結果は表示されません。 `#if 1` のようにマクロのない行については、マクロを表示する /*[NAME]*/ がないだけです。

`#elif, #ifdef, #ifndef` についても同様に /*elif [lnum]*/, /*ifdef [lnum]*/, /*ifndef [lnum]*/ で始まり、マクロが定義されていれば /*[NAME]*/ が続き、/*i T*/ または /*i F*/ で終わります。

スキップされるブロック中のディレクティブについては何も表示されません。

#### 3.5.8.5. #else, #endif に関するコメント

#else 行では上の例のように、/*else [lnum]:[C]*/ というフォーマットで情報が表示されます。 [lnum] は行番号、[C] は T または F で、その #else - #endif ブロックがコンパイルされるブロックかそれともスキップされるブロックかを示します。

#endif 行では上の例のように、/*endif [lnum]*/ というフォーマットで、現在の行番号が表示されます。 これはもちろん、まだ #endif に対応づけられていない最後の #if (#ifdef, #ifndef) に対応します。

#### 3.5.8.6. #line の出力

なお、macro notification mode では #line 行のファイル名の出力がデフォルトの場合と違い、#include 行の指定を full-path-list に正規化したものが出力されます（3.2 参照）。 Refactoring tool の作成を容易にするためです。

## 3.6. #assert, #asm, #endasm

#assert は pre-Standard モードでだけ使えます。GCC 版では実装されません。Standard C の #error に対応する機能です。Standard C で

    #if ULONG_MAX / 2 < LONG_MAX
    #error  Bad unsigned long handling.
    #endif

とするところを

    #assert LONG_MAX <= ULONG_MAX / 2

と書けます。引数を #if 式として評価し、真（non-zero）であれば何もせず、偽（0）であれば

    Preprocessing assertion failed

という言葉に続いてその行（行接続とコメント処理をした後の行）を表示します。 これはエラーとしてカウントしますが、処理は中止しません。

この #assert は System V や GCC の #assert とは、まったく別のものです。

#asm, #endasm の２つのディレクティブ行ではさまれたブロックはアセンブラソースとして扱われます。pre-Standard モードでだけ使えます。ただし、これは Microware C / 6809 用に書かれたものなので、他の処理系にも移植するには、system.c の do_old(), do_asm(), put_asm() に書き足す必要があります。
#asm ブロックについては、trigraphs の変換と <backslash><newline> の削除はしますが、コメントの処理や token チェックや文字チェックはせず、行頭の space も削除せず、たまたまマクロと同じ名前があってもマクロ展開せず、ソースの行をそのまま出力します。その他のディレクティブ行は #asm ブロック内では意味を持ちません。

この #asm, #endasm は Standard C では認められないものです。まず、#pragma sub-directive 以外の拡張ディレクティブが規格外ですが、そればかりか、#pragma asm, #pragma endasm と名前を変えても解決しません。と言うのは、Standard C ではソースはすべてＣの token sequence で成り立っている必要がありますが（厳密に言えば preprocessing token sequence）、アセンブラプログラムはＣの token sequence ではないからです。Standard C でアセンブリコードを使うには、それを文字列リテラルという token に埋め込む方法しかありません。そして、それを処理する組み込み関数をコンパイラ本体に実装して、

    asm(
        " leax _iob+13,y\n"
        " pshs x\n"
    );

といった形で呼び出すのです。

やや長いコードだとこんなことはやっていられないので、 その場合はその部分を別の関数にして、ライブラリ関数を書く時のように、別のファイルでアセンブラプログラムそのものを書いて、アセンブルパスで処理し、それをリンクして使うことになります。これは窮屈な制限のように思えるかもしれませんが、portable なＣプログラムを書くにはアセンブラの部分は完全に分離する必要がありますから、むしろ #asm を使わずに別ファイルで書くようにしたほうが良いでしょう。

## 3.7. C99 の新機能（_Pragma() 演算子、可変引数マクロ等）

これは Standard モードでだけ使えます。
-V199901L オプションで `__STDC_VERSION__` を 199901L 以上にすると、C99 の次の機能が有効になります。
C++ でも -V199901L オプションで `__cplusplus` を 199901L 以上にした場合は同様です。1, 7 以外の仕様は C++ Standard にはありませんが、Standard モードでは C99 との互換性を高めるために、このオプションを用意しています。
ただし、可変引数マクロは Standard モードでは C90 および C++ でも使えるようにしてあります。*1

1. // から行末までをコメントとして扱う。
2. 可変引数マクロが使える。
3. Preprocessing-number の中に e+, E+, e-, E- と同様に p+, P+, p-, P- という sequence も認める。これは浮動小数点数のビットパターンを 0x1.FFFFFEp+128 というふうに、１６進で表記するためのものである。
4. _Pragma() operator が有効になる。
5. EXPAND_PRAGMA というマクロを TRUE に定義してコンパイルされた mcpp では、#pragma 行の引数は、STDC, MCPP, GCC のどれかで始まるのでない限りマクロ展開の対象となる（デフォルトでは EXPAND_PRAGMA == FALSE であり、マクロ展開しない。展開するのは Visual C, Borland C 版だけである）。
6. #if 式は long long のある処理系では long long / unsigned long long で評価する。
7. 識別子・文字定数・文字列リテラル・pp-number の中にある \unnnn, \Unnnnnnnn の形の UCN という escape sequence を通す。これは Unicode の文字の値を意味する。#if 式では UCN の値は１６進表記として評価する。（ただし、POSTSTD モードでは UCN は使えない）。

可変引数マクロというのは、次のようなものです。

    #define debug(...)  fprintf(stderr, __VA_ARGS__)

というマクロ定義があると、

    debug( "X = %d\n", x);

というマクロ呼び出しは次のように展開されます。

    fprintf(stderr, "X = %d\n", x);

すなわち、パラメータ・リスト中の `...` が１個以上のパラメータを意味し、置換リスト中の `__VA_ARGS__` がそれに対応します。そして、マクロ呼び出し時には `...` に対応する引数が複数あっても、それらを , を含めて連結したものが一つの引数のように扱われます。

_Pragma 演算子は _Pragma( "foo bar") と書くと #pragma foo bar と書いたのと同じ効果を持つ演算子です。引数は文字列リテラルまたはワイド文字列リテラル１個でなければなりません。ワイド文字列であれば接頭子 L を削除し、文字列リテラルを囲む " を削除し、文字列リテラルの中の \", \\ をそれぞれ ", \ に置き換えたものが #pragma の引数として扱われます。
#pragma はソースの論理行１行の初めから終わりまでに書かなければならず、引数が（少なくとも C90 では）マクロ展開されないのに対して、_Pragma() 演算子はソースのどこに書いても独立した論理行に #pragma を書いたのと同じ効果を持ち、マクロの置換リスト中に書くこともでき、マクロ展開の結果として生成された _Pragma() operator も有効です。この柔軟性を利用することで、広い portability を持った pragma directive を書くことができ、処理系による #pragma の違いを１つのヘッダファイルで吸収することもできます。（サンプルとしては "Validation Suite" の pragmas.h, pragmas.t を参照のこと）。*2

なお、C99 では #if 式の型はその処理系の最大の整数型となっています。long long / unsigned long long は必須とされているので、#if 式の型は long long / unsigned long long またはそれ以上ということになります。C90, C++98 では #if 式は long / unsigned long で評価することになっています。しかし、mcpp は C90, C++98 でも long long / unsigned long long で評価し、long / unsigned long の範囲を超える値に対してはウォーニングを出します。*1

注:

*1 これは GCC, Visual C 2005, 2008 等との互換性のためである。他の処理系でも、 C99 の仕様を一挙に実装するのは難しいので、`__STDC_VERSION__` を 199409L 等としたままこうした一部の仕様から実装してゆくことが予想される。

*2 C99 では #pragma の引数が STDC で始まる場合はマクロ展開されないが、そうでない場合は implementation-defined である。

## 3.8. 処理系ごとの特殊な仕様

mcpp の compiler-specific-build にはそれぞれの処理系に固有の仕様がいくつか実装されていますが、そのうち実行時オプションでも #pragma でもない特殊なものをこの節で説明します。

### 3.8.1. GCC, Visual C の可変引数マクロ

GCC は V.2 のころから 3.9.1.6 にあるような独自の仕様の可変引数マクロを持っています。 これをこのマニュアルでは GCC2 仕様の可変引数マクロと呼びます。 また GCC V.3 では 3.9.6.3 のような新しい仕様が実装されました。 これをここでは GCC3 仕様の可変引数マクロと呼びます。 GCC 2.95 では C99 仕様の可変引数マクロも実装されましたが、glibc や Linux のシステムヘッダでは C99 仕様のものはおろか GCC3 仕様のものさえ使われず、いまだに GCC2 仕様が使われています。

GCC-specific-build の mcpp は V.2.6.3 から STD モードに限って GCC3 仕様の可変引数マクロを実装し、V.2.7 からは GCC2 仕様のものも実装しました。 これは Linux 等で使う際の不自由を避けるために実装したものです。 ただし、ウォーニングが出ます。 GCC 仕様は portability がないだけでなく、文法的に汚いので、新しく書くソースでは使うべきではありません。 ことに GCC2 仕様がそうです。

Visual C は 2003 までは可変引数マクロは実装していませんでしたが、2005 で実装されました。 これは C99 の仕様に GCC3 仕様と似た修正を加えたもので、可変引数が欠如していた場合は、その直前のトークンがコンマであればそれを削除します。 ただし、GCC と違って '##' という記号は使いません。 この仕様は Visual C のドキュメントによると、次の例のような結果になります。 この３番目の例ではコンマは削除しないとされています。 しかし、Visual C 2005, 2008 は実際にはこのコンマも削除してしまいます。

    #define EMPTY
    #define VC_VA( format, ...)     printf( format, __VA_ARGS__)
    VC_VA( "var_args: %s %d\n", "Hello", 2005);     /* printf( "var_args: %s %d\n", "Hello", 2005); */
    VC_VA( "absence of var_args:\n");               /* printf( "absence of var_args:\n");   */
    VC_VA( "empty var_args:\n", EMPTY);             /* printf( "empty var_args:\n", );  */  /* trailing comma   */

mcpp は V.2.7 から Visual C-specific-build の STD モードにこの仕様を実装しました。 ただし、ウォーニングが出ます。 上記の３つ目の例ではコンマは削除せず、仕様通りに実装しています。

### 3.8.2. GCC の 'defined' の処理

マクロ中に 'defined' というトークンが出てくるケースでは、GCC はそのマクロが #if 行にある場合は、他の場合と異なった恣意的な処理をします。 この問題については 3.9.4.6 で検討しているので、参照してください。

mcpp の GCC-specific-build の STD モードでは V.2.7 から GCC と同じ処理をするようにしました。 Linux の一部のシステムヘッダなどでこの種の間違ったマクロが使われているのに対処するためです。 ただし、ウォーニングが出ます。 自分の書くプログラムでは正しい #if 式を書くようにしてください。

### 3.8.3. Borland C の asm 文その他の特殊な構文

Borland C には asm というキーワードがあって、

    asm {
        mov x,4;
        ...;
    }

といった形でアセンブリコードを記述するようになっていますが、これはＣ言語の文法からはずれた極めて変則的なものです。この中にたまたまマクロと同じ名前があると、それはマクロ展開されてしまいます。Borland C そのものでも mcpp でも、その点は同じです。アセンブラプログラムは別の .asm ファイルで書くのが本当でしょう。 mcpp はこれについては特別な扱いは何もしません。

Visual C++ にも __asm という同様のキーワードがあります。
GCC には asm( " mov x,4\n") というまっとうな形の組み込み関数が用意されています。

### 3.8.4. #import その他

#### 3.8.4.1. Mac OS X / GCC の #import

GCC には Objective-C の #import というディレクティブがあり、C/C++ でもこれを #pragma once を暗黙に宣言した #include として使えます。 Mac OS X の C/C++ ソースには時にこれを使うものがあるようです。

mcpp V.2.7 以降は Mac OS X に限って、GCC 版でもコンパイラ独立版でも、これを実装しています。

#### 3.8.4.2. Visual C の #import, #using

Visual C には #import と #using という特殊なディレクティブがあります。 プリプロセスディレクティブの形をしていますが、内容はコンパイラとリンカに対する指示です。 #import は GCC のものとは関係ありません。

mcpp の Visual-C-specific-build はこれらの行をそのまま出力します。

## 3.9. GCC の問題と GCC との互換性

GCC 専用版の mcpp では、GCC / cpp (cc1) との互換性を実用上あまり不便がない程度に確保していますが、非互換な面も多々あります。
まず実行時オプションについては、2 章に見るようにいろいろ違いがあります。-A オプション等は実装していません。また、ディレクティブでは #assert, #ident 等は実装していません。*1
しかし、幸いなことに、これらのことが原因でコンパイルできないというソースはごく少ないようです。

むしろ実際に問題となるのは、古いプリプロセッサの特殊な仕様をあてにしたソースです。これらの多くは GCC で -pedantic を指定するとウォーニングが出ます。mcpp は Standard モードのものではエラーチェックを規格通りに実装しているので、ほぼ GCC の -pedantic がデフォルトとなっています。GCC はデフォルトではそうした規格違反ソースを黙って通すため、それをあてにしたソースが一部に見られます。そうしたソースを規格に合致するように書くのはきわめて簡単なことで、わざわざ規格違反の書き方をする必然性は何もありません。単に移植性を損なうだけで、悪くするとバグの温床となるので、見つけしだい直しておきましょう。*2

注:

*1 これらはいずれも必要なら #pragma で実装すべきものである。 #include_next, #warning も同様であるが、GCC のシステムでは実際に時々使われているので、mcpp でも GCC 版に限って実装した。ただし、ウォーニングの対象になる。

*2 3.9 から 3.9.3 は 1998 年に書かれたものであるが、その後は状況が変わってきている。 古い仕様に依存するソースは少なくなり、それに代わって GCC 独自の拡張機能や実装の細部に依存するものが増えてきている。 3.9.4 以降、ことに 3.9.8 以降で取り上げるのは主としてそうした問題である。 (2008/03)

### 3.9.1. FreeBSD 2 / kernel ソースのプリプロセス

以下に、FreeBSD 2.2.2-R (1997/05) の kernel ソースを例に問題点を挙げておきます。ディレクトリ名はいずれも /sys (/usr/src/sys) 中のものです。これらのうち 7, 8 は必ずしも規格違反ではなく、mcpp でも期待通りの処理をします。しかし、あぶなっかしい書き方なのでウォーニングが出ます。6 は拡張機能で、C99 でも同じ機能が用意されていますが、GCC / cpp とは記法が異なります。

#### 3.9.1.1. 行をまたぐ文字列リテラル

`i386/apm/apm.c, i386/isa/npx.c, i386/isa/seagate.c, i386/scsi/aic7xxx.h, dev/aic7xxx/aic7xxx_asm.c, dev/aic7xxx/symbol.c, gnu/ext2fs/i386-bitops.h, pc98/pc98/npx.c` にはこういう書き方でアセンブラソースが埋め込まれています。

    asm("
        asm code0
    #ifdef PC98
        asm code1
    #else
        asm code2
    #endif
        ...
    ");

文字列リテラルを閉じる " が行末までになかった場合は行末で閉じられていると解釈するのが GCC / cpp のデフォルトの仕様ですが、それを使っているのです（さらにコンパイラ本体では asm() の中身全体が行をまたぐ文字列リテラルと解釈されるらしい）。

アセンブラソースは .s ファイルとして切り離しておくのが良いスタイルだと思われますが、どうしても .c ファイルに埋め込みたければ、こんなあぶなっかしいことをしなくても、次のようにすればすみます。これであれば Standard C 準拠のプリプロセッサでも問題ありません。

    asm(
        "    asm code0\n"
    #ifdef PC98
        "    asm code1\n"
    #else
        "    asm code2\n"
    #endif
        "    ...\n"
    );

#### 3.9.1.2. #else junk, #endif junk

`ddb/db_run.c, netatalk/at.h, netatalk/aarp.c, net/if-ethersubr.c, i386/isa/isa.h, i386/isa/wdreg.h, i386/isa/tw.c, i386/isa/b004.c, i386/isa/matcd/matcd.c, i386/isa/sound/sound_calls.h, i386/isa/pcvt/pcvt_drv.c, pci/meteor.c, pc98/pc98/pc98.h` にはこういう行があります。

    #endif MACRO

これはこうしておきましょう。

    #endif /* MACRO */

#### 3.9.1.3. #ifdef 0

`i386/apm/apm.c` にはなんと

    #ifdef 0

という奇怪な行があります。

もちろん、

    #if 0

の間違いです。実際には使われていない、 デバッグもされていないソースなのでしょう。

#### 3.9.1.4. マクロの二重定義

`gnu/i386/isa/dgb.c` では次の行が何かのヘッダファイルと矛盾する二重定義になります。

    #define DEBUG

Standard C では二重定義は violation of constraint で、実際には処理系によって、エラーにした上で初めの定義を有効とするものと、GCC 2 / cpp のようにデフォルトでは黙ってあとの定義を有効とするものとあります。確実にあとの定義を有効にするには、直前に

    #undef DEBUG

を入れるべきです。

#### 3.9.1.5. #warning

`i386/isa/if_ze.c, i386/isa/if_zp.c` には #warning があります。Kernel ソース中で唯一の規格違反ディレクティブです。Standard C に合わせるためには、この行をコメントアウトするしかありません。

mcpp の GCC 版では #warning が使えるので、このまま通ります。

#### 3.9.1.6. 可変引数マクロ

`gnu/ext2fs/ext2_fs.h, i386/isa/mcd.c` には次のような可変個引数のマクロが定義されています。

    #define MCD_TRACE(fmt, a...)                \
    {                                           \
        if (mcd_data[unit].debug) {             \
            printf("mcd%d: status=0x%02x: ",    \
                unit, mcd_data[unit].status);   \
            printf(fmt, ## a);                  \
        }                                       \
    }

    #   define ext2_debug(fmt, a...)    { \
            printf ("EXT2-fs DEBUG (%s, %d): %s:", \
                __FILE__, __LINE__, __FUNCTION__); \
            printf (fmt, ## a); \
            }

これは GCC / cpp 独自の拡張仕様で、他の処理系では通用しません。この ## a のところは単に a とする書き方もあります。## があると、マクロ呼び出しで `a...`に対応する引数がなかった場合は、その直前のコンマを削除します。
C99 では可変個引数マクロが追加されていますが、記法が異なり、これらの例は次のように書くことになります。``

    #define MCD_TRACE( ...)                     \
    {                                           \
        if (mcd_data[unit].debug) {             \
            printf("mcd%d: status=0x%02x: ",    \
                unit, mcd_data[unit].status);   \
            printf( __VA_ARGS__);               \
        }                                       \
    }

    #   define ext2_debug( ...)         { \
            printf ("EXT2-fs DEBUG (%s, %d): %s:", \
                __FILE__, __LINE__, __FUNCTION__); \
            printf ( __VA_ARGS__); \
            }

C99 では `...` に対応する呼び出し時の引数は１個以上必要なのに対して、GCC / cpp では `a...` に対応する引数は０個でもかまわないというのが、やっかいな相違点です。mcpp ではこれに対処するため、`...` に対応する引数が１個もない場合は、warning は出すもののエラーにはしないようにしています。したがって、次のような書き方もできます。このほうが書き換えは一対一対応でできるので、簡単です。しかし、この書き方では、カラ引数の直前のコンマが残るので、たとえば printf( fmt, ) 等という展開結果になってしまうことがあります。その場合は、マクロ定義を上記の C99 仕様の書き方にするか、またはマクロ呼び出しでカラ引数を使わないようにするしかありません。カラ引数の代わりには NULL や 0 のような無害なトークンを使って、`MCD_TRACE(fmt, NULL)` 等と書くことになります。*1

    #define MCD_TRACE(fmt, ...)
    {
        if (mcd_data[unit].debug) {
            printf("mcd%d: status=0x%02x: ",
                unit, mcd_data[unit].status);
            printf(fmt, __VA_ARGS__);
        }
    }

    #   define ext2_debug(fmt, ...)         {
            printf ("EXT2-fs DEBUG (%s, %d): %s:",
                __FILE__, __LINE__, __FUNCTION__);
            printf (fmt, __VA_ARGS__);
            }

注:

*1 GCC 2.95.3 以降では C99 の構文の可変引数マクロも実装されているので、こちらを使うほうが良い。GCC の可変引数マクロは引数がゼロ個でも良いという柔軟性があるが、その記法は良くない。`args...` というパラメータでは `args` と `...` とはくっついていなければならないが、こういう pp-token は存在しない。置換リストでトークン連結演算子と同じ記法を別の用途に使っているのも、感心しない。C99 の記法で、ゼロ個の可変引数も許容するという仕様が妥当であろう。
なお、GCC 3 では可変引数マクロについて、GCC 2 以来の仕様と C99 の仕様との折衷的な書き方が追加された。それについては 3.9.6.3 を参照のこと。

#### 3.9.1.7. マクロ呼び出しのカラ引数

`nfs/nfs.h, nfs/nfsmount.h, nfs/nfsmode.h, netinet/if_ether.c, netinet/in.c, sys/proc.h, sys/socketvars.h, i386/scsi/aic7xxx.h, i386/include/pmap.h, dev/aic7xxx/scan.l, dev/aic7xxx/aic7xxx_asm.c, kern/vfs_cache.c, pci/wd82371.c, vm/vm_object.h, vm/device/pager.c` にはこういうマクロ呼び出しがあります。`/usr/include/nfs/nfs.h` でも同様です。

    LIST_HEAD(, arg2)
    TAILQ_HEAD(, arg2)
    CIRCLEQ_HEAD(, arg2)
    SLIST_HEAD(, arg2)
    STAILQ_HAED(, arg2)

第一引数がカラなのです。カラ引数は C99 では公認されましたが、C90 では undefined です。ネストされたマクロ呼び出しでたまたま引数がカラになった場合のこと等を考えると、カラ引数が規定されているほうが良いと言えますが、ソース中にカラ引数を書くことにはこれらの場合は必然性がなく、感心しません。引数が１つのマクロではカラ引数と引数の欠落との区別がつかないという syntax のあいまいさがあることも、忘れてはなりません。

こう書くほうが良いでしょう。これであれば Standard C 準拠のどのプリプロセッサでも問題ありません。

    #define EMPTY

    LIST_HEAD(EMPTY, arg2)
    TAILQ_HEAD(EMPTY, arg2)
    CIRCLEQ_HEAD(EMPTY, arg2)
    SLIST_HEAD(EMPTY, arg2)
    STAILQ_HAED(EMPTY, arg2)

ところで、これらのヘッダファイルの中には、これらのマクロの定義もなければ他のどのヘッダも #include されていないというものがあります（nfs ディレクトリのもの）。これらのマクロの定義は sys/queue.h にあり、*.c プログラムがそちらを先に #include することを期待しているのです。あぶなっかしい書き方のヘッダ群です。

なお、`kern/kern_mib.c` には次のようなマクロ呼び出しがあります。

    SYSCTL_NODE(, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)

しかし、このカラ引数は `EMPTY` とするわけにはいきません。このマクロの定義は `sys/sysctl.h` にあり、次のようになっているからです。

    #define SYSCTL_NODE(parent, nbr, name, access, handler, descr)
        extern struct linker_set sysctl_##parent##_##name;
        SYSCTL_OID(parent, nbr, name, CTLTYPE_NODE|access,
            (void*)&sysctl_##parent##_##name, 0, handler, "N", descr);
        TEXT_SET(sysctl_##parent##_##name, sysctl__##parent##_##name);

すなわち、これらの引数はマクロ展開されないのです（この中にある `SYSCTL_OID` というマクロでも、やはり第一引数等はマクロ展開されない）。これはカラ引数のままにしておくしかありません。*1

注:

*1 C99 ではカラ引数は合法とされている。また、この `SYSCTL_NODE`(), `SYSCTL_OID`() のようなマクロのことを考えると、`EMPTY` というマクロを使う方法は万能ではないので、カラ引数のままにしておくのも一理あることである。`EMPTY` を使っても、マクロ呼び出しがネストされていると、やはりカラ引数が発生してしまうという問題もある。しかし、ソースの readability を考えると、`EMPTY` を使えるところでは使ったほうが良いように思われる。

#### 3.9.1.8. Function-like マクロの名前に置換される object-like マクロ

`i386/include/endian.h` にはこういうマクロ定義があります。`/usr/include/machine/endian.h` でも同様です（同種の定義が４つある）。

    #define __byte_swap_long(x)     (replacement text)
    #define NTOHL(x)                (x) = ntohl((u_long)x)
    #define ntohl                   __byte_swap_long

問題は ntohl の定義です。これは object-like マクロでありながら function-like マクロの名前に展開され、そのため再走査で続くテキストを巻き込み、あたかも function-like マクロのように展開されます。この展開方法は K&R 1st. 以来の暗黙の仕様で、Standard C ではなぜか合法となったものです。しかし、私が別のところで論じているように、この仕様こそがマクロ展開を無用に複雑にし、規格書にまで混乱をもたらしている元凶であり、「バグのような仕様」なのです。*1

この例は実態は function-like マクロであるものを省略して object-like マクロとして書いているものですが、function-like マクロらしく次のように書いたほうが良いでしょう。これであれば何の問題もありません。

    #define ntohl(x)                __byte_swap_long(x)

`i386/isa/sound/os.h` にも同種のマクロ定義があります。

    #define INB                     inb
    #define INW                     inb

これはこうしておきましょう。

    #define INB(x)                  inb(x)
    #define INW(x)                  inb(x)

注:

*1 ISO 9899:1990 の Corrigendum 1:1994 ではこうした例は undefined とされた。そして、C99 では Corrigendum のこの項は別のものと置き換えられた。しかし、規格書はこれに関しては混乱している。詳細は cpp-test.html#2.7.6 を参照。

#### 3.9.1.9. .S ファイルの「プリプロセス」

Kernel ソースには何本かの .S ファイル、すなわちアセンブラソースがあります。ところがこれらには #include や #ifdef があって、「プリプロセス」が必要なのです。FreeBSD 2.2.2-R ではこれらのソースは cc を -x assembler-with-cpp オプションを付けて呼び出すという方法で処理しています。そうすると cc は /usr/libexec/cpp を -lang-asm オプションをつけて呼び出し、さらに as を呼び出します。

こういう .S ファイルはもちろん規格外の変則ソースです。これが意図通りに「プリプロセス」されるためには、マクロとたまたま一致する「名前」がアセンブラソースに含まれていてはいけません。そして、「プリプロセス」では、「トークン」間の white space の有無はそのまま保存されなければならず、行頭の space も削除せずに保存されなければなりません。行の最初の「トークン」がアセンブラのコメントである # である場合はプリプロセッサの側で特殊な処理が必要です。使えるプリプロセッサがかなり制限され、バグの混入にも余計な神経を使わなければならないので、決して良いことだとは思えません。*1

複数のマシンに対応させる等のためにプリプロセスする必要があるのであれば、.S ファイルではなく .c ファイルとして、次の形で書くのが良いでしょう。4.4BSD-Lite では実際にこの書き方になっています。

    asm(
        "    asm code0\n"
    #ifdef Machine_A
        "    asm code1\n"
    #else
        "    asm code2\n"
    #endif
        "    ...\n"
    );

注:

*1 FreeBSD 2.0-R ではこれらのファイル名は *.S ではなく *.s となっていて、Makefile ではその処理には cc ではなく cpp を呼び出して、次に as を呼び出すようになっていた。そして、cpp を呼び出すと /usr/bin/cpp が動くが、これは shell-script で、/usr/libexec/cpp -traditional を呼び出す。このほうが、script を書き換えることで使うプリプロセッサを変えることができるので、便利であった。

### 3.9.2. FreeBSD 2 / libc ソースのプリプロセス

FreeBSD 2.2.2R の `/usr/src/lib/libc` の全ソースもコンパイルしてみましたが、特に問題はありませんでした。大半は 4.4BSD-Lite からそのまま来ているからでしょうか。これだけの規模できれいなソースがそろっているのは珍しいことで、特筆に値します。

ただ１個所、`gen/getgrent.c` に次のような行が見つかりました。もちろん、行末の ; は余計です。

    #endif;

### 3.9.3. GCC 2 / cpp の仕様の問題

さて、以上に見たように、これらのソースを規格に合致した、より移植性の高い、より安全なスタイルで書くことには、何の面倒もデメリットもありません。にもかかわらず、なぜこうしたソースがいまだに書かれているのでしょうか？

FreeBSD 2.0-R と 2.2.2-R の kernel ソースを比べても、この種のものはあまり減っていません。新しいほど規格合致性が高くなっているとは限らないところが問題なのです。この種のあやしげなスタイルのソースは 4.4BSD-Lite から存在しているものはわずかです。4.4BSD は Standard C と POSIX に準拠して書き直されたからでしょう。ところが、FreeBSD への実装で一部のソースにこうした古いスタイルが復活してしまったのです。上記の ntohl のように 4.4BSD-Lite では ntohl(x) の形になっているものが FreeBSD ではわざわざ ntohl の形に書き替えられているものさえあります。いったん一掃されたものが、なぜ復活するのでしょうか？

私はここには、GCC / cpp がこれらを黙って通してしまうことの悪影響が現れていると思います。-pedantic の動作がデフォルトであれば、こうしたスタイルのソースがわざわざ「新しく」書かれることはなかったでしょう。かつては -pedantic-errors をデフォルトにしたのではコンパイルできないソースが多くて、実用的ではなかったのかもしれません。gcc の man page には -pedantic オプションについて、「これを使う理由は何もない。このオプションの存在理由は pedants を満足させることだけである」とあります(*1)。しかし Standard C が決まって８年もたついまとなっては、-pedantic-errors とまではいかなくても -pedantic をデフォルトにすべき時に来ていると思われます。

FreeBSD 2.0-R ではコメントのネストが時々見られましたが、2.2.2-R では一掃されています。一掃されたのは、GCC / cpp が認めなくなったからです。これは -pedantic とは関係ありませんが、プリプロセッサのソースチェックの威力はそれほど大きいのです。

注:

*1 この 3.9.3 が書かれたのは 1998 のことである。その後、gcc の man や info では、さすがにこの表現は削除された。しかし、仕様が特に変わったわけではない。

### 3.9.4. Linux / glibc 2.1 ソースのプリプロセス

glibc (GNU LIBC) 2.1.3 (2000/02) のソースをコンパイルしてみました。これには FreeBSD の libc とは違ってかなり多くの問題があります。中には GCC / cpp の undocumented な仕様を利用しているものさえあり、その仕様を突き止めるだけでかなりの時間を費やしてしまったことが何回かありました。

#### 3.9.4.1. 行をまたぐ文字列リテラル

`sysdeps/i386/dl-machine.h, stdlib/longlong.h` には

    #define MACRO asm("
        instr 0
        instr 1
        instr 2
    ")

といった形の行をまたぐ文字列リテラルがいくつもあります。 中にはかなり長大なものもあります。

また、make によって生成される `compile/csu/version-info.h` にも行をまたぐ文字列リテラルが現れます。
これはもちろん規格違反のソースですが、GCC ではこれを改行コードの入った文字列リテラルとして扱います。
mcpp では -lang-asm (-x assembler-with-cpp, -a) オプションを指定すると、こうした行をまたぐ文字列リテラルを

    #define MACRO asm("\n    instr 0\n    instr 1\n    instr 2\n")

という形に変換して処理します（3.9.1.1 のように途中にディレクティブのはさまっているものは、これでは対応できず、ソースを書き直すしかない）。

#### 3.9.4.2. #include_next, #warning

`catgets/config.h, db2/config.h, include/fpu_control.h, include/limits.h, include/bits/ipc.h, include/sys/sysinfo.h, locale/programs/config.h, sysdeps/unix/sysv/linux/a.out.h` には #include_next が現れます。

また、`sysvipc/sys/ipc.h` には #warning があります。

これらは規格では認められていないディレクティブですが、glibc 2 のシステムではことに #include_next は不可欠のものとなってしまっているので、mcpp でも GCC 用では #include_next と #warning は実装しています。

#include_next の問題は規格違反だということだけではありません。Include directories とそのサーチ順は環境変数等のユーザ側の設定によって変わる場合があるので、それによって結果が違ってくる危険があります。

Glibc の include ディレクトリのファイルには、glibc を install すると /usr/include ディレクトリにコピーされるものもあります。すなわち、システムのヘッダファイルとして使われるものなのです。こうしたヘッダファイルに#include_next が使われていることは、システムヘッダがかなりつぎはぎ状態になってきていることを表しています。大整理が必要な時期にきているようです。

#### 3.9.4.3. 可変引数マクロ

`elf/dl-lookup.c, elf/dl-version.c, elf/ldsodefs.h, glibc-compat/nss_db/db-XXX.c, glibc-compat/nss_files/files-XXX.c, linuxthreads/internals.h, locale/loadlocale.c, locale/programs/linereader.h, locale/programs/locale.c, nss/nss_db/db-XXX.c, nss/nss_files/files-XXX.c, sysdeps/unix/sysdep.h, sysdeps/unix/sysv/linux/i386/sysdep.h, sysdeps/i386/fpu/bits/mathinline.h`

以上のファイルには GCC / cpp の仕様の可変個引数マクロの定義と呼び出しがあります。*1

注:

*1 これは GCC2 からある仕様であるが、そのほか GCC3 では C99 と GCC2 との折衷的な仕様が追加された。 それについては 3.9.6.3 を参照のこと。

#### 3.9.4.4. マクロ呼び出しのカラ引数

`catgets/catgetsinfo.h, elf/dl-open.c, grp/fgetgrent_r.c, libio/clearerr_u.c, libio/rewind.c, libio/clearerr.c, libio/iosetbuffer.c, locale/programs/ld-ctype.c, locale/setlocale.c, login/getutent_r.c, malloc/thread-m.h, math/bits/mathcalls.h, misc/efgcvt_r.c, nss/nss_files/files-rpc.c, nss/nss_files/files-network.c, nss/nss_files/files-hosts.c, nss/nss_files/files-proto.c, pwd/fgetpwent_r.c, shadow/sgetspent_r.c, sysdeps/unix/sysv/linux/bits/sigset.h, sysdeps/unix/dirstream.h`

以上のファイルにはマクロ呼び出しのカラ引数が現れます。ことに `math/bits/mathcalls.h` にはカラ引数が 79 個もあります。このヘッダファイルは `/usr/include/bits/mathcalls.h` に install されて、`/usr/include/math.h` から #include されるものです。`EMPTY` というマクロを使っても、マクロ呼び出しがネストされているので、やはり大量のカラ引数が発生します。もっときれいなマクロの書き方はできないものでしょうか。

#### 3.9.4.5. Function-like マクロの名前に置換される object-like マクロ

`argp/argp-fmtstream.h, ctype/ctype.h, elf/sprof.c, elf/dl-runtime.c, elf/do-rel.h, elf/do-lookup.h, elf/dl-addr.c, io/ftw.c, io/ftw64.c, io/sys/stat.h, locale/programs/ld-ctype.c, malloc/mcheck.c, math/test-*.c, nss/nss_files/files-*.c, posix/regex.c, posix/getopt.c, stdlib/gmp-impl.h, string/bits/string2.h, string/strcoll.c, sysdeps/i386/i486/bits/string.h, sysdeps/generic/_G_config.h, sysdeps/unix/sysv/linux/_G_config.h`

以上のファイルには function-like マクロの名前に置換される object-like マクロの定義があります。中には、math/test-*.c にあるもののように、function-like マクロが object-like マクロに置換されて、さらにそれが function-like マクロの名前に置換されるものもあります。そういう書き方をする必然性があるのでしょうか？

#### 3.9.4.6. 'defined' に展開されるマクロ

`sysdeps/generic/_G_config.h, sysdeps/unix/sysv/linux/_G_config.h, malloc/malloc.c` には、例えば次のように defined という pp-token に展開されるマクロ定義があります。

    #define HAVE_MREMAP defined(__linux__) && !defined(__arm__)

これは、

    #if HAVE_MREMAP

というディレクティブがあると

    #if defined(__linux__) && !defined(__arm__)

となることを期待しているものです。

しかし、まず #if 行中でマクロ展開の結果に defined という pp-token が出てくるのは、規格では undefined です。そのことは別としても、なおこのマクロは変です。

`HAVE_MREMAP` というマクロはいったん

    defined(__linux__) && !defined(__arm__)         (1)

と置換され、次に identifier である defined, __linux__, __arm__ がそれぞれマクロであるかどうかが調べられ、マクロであれば展開されます。したがって、defined はマクロとして定義されているはずはないので（もし定義されていれば、それ自体がすでに undefined）、仮に __linux__ が 1 に定義されていて、__arm__ が定義されていなければ、このマクロは最終的に次のように展開されます。

    defined(1) && !defined(__arm__)

defined(1) はもちろん #if 式の syntax error です。

ところが GCC では、#if 行でなければこうなるのですが、#if 行に限って (1) でマクロ展開をやめてしまい、これを #if 式として評価します。Undefined であるのでそれも間違いとは言えませんが、マクロ展開が #if 行とそうでない場合とで異なるのは、一貫しない仕様です。少なくともその仕様には portability がありません。*1

問題のマクロは次のように書けば、何も問題ないのです。

    #if defined(__linux__) && !defined(__arm__)
    #define HAVE_MREMAP 1
    #endif

こういう危なっかしいソースは早く一掃されてほしいものです。 *2

注:

*1 GCC 2 / cpp は #if 行では内部的に defined を特殊なマクロとして扱っている。 そのため、次のようなトークン列をマクロ展開のために再走査するのであるが、その結果がこれをマクロ展開せずに、#if 式として評価することになるのである。すなわち、マクロ展開と #if 式の評価とが分離せずに混交しているのである。

> `defined(__linux__) && !defined(__arm__)`

これは GCC / cpp のプログラム構造にもかかわる問題である。GCC 2 / cpp では rescan() というマクロ再走査ルーチンが事実上のメインルーチンとなっていて、これがソーステキストを初めから終わりまで読みながら処理してゆく。そして、プリプロセスディレクティブの処理ルーチンもこの中から呼び出されるのである。何でもマクロで実装するのはマクロプロセッサの伝統的なプログラム構造であるが、この構造が、マクロ展開と他の処理との混交を引き起こす背景になっていると考えられる。

*2 glibc 2.4 ではこのマクロは直されている。 ところが、他のマクロで同種のものが新たにいくつも出てきている。

#### 3.9.4.7. .S ファイルの「プリプロセス」

*.S という名前のファイルはプリプロセスを要するアセンブラのソースですが、その中には #include, #define, #if 等のプリプロセスディレクティブが出てきます。さらに、Make によって生成される `compile/csu/crti.S` というファイルには、

    #APP

とか

    #NO_APP

という行まで現れます。 これらの行は無効なプリプロセスディレクティブと構文上、区別がつきません。GNU ではこれらの行はそのままプリプロセス後に残って、アセンブラのコメントとして扱われるようです。

また、## 演算子による pp-token の連結が invalid な pp-token を生成してしまうソースもあります。GCC / cpp はこれを黙ってそのまま出力します。

mcpp では GCC / cpp との互換性のためにやむなく、-lang-asm (-x assembler-with-cpp, -a) オプションを付けると、こうした illegal なディレクティブや ## によって生成された invalid な pp-token をエラーにせず、ウォーニングを出すもののそのまま出力するようにしました。

こうしたソースは本来、アセンブラ用のマクロプロセッサで処理すべきものだと思われます。GNU にも gasp というアセンブラ用マクロプロセッサがあるようですが、なぜかほとんど使われていないようです。

#### 3.9.4.8. rpcgen と -dM オプションの仕様の問題

GCC は -dM というオプションで起動するとマクロ定義だけを出力しますが、make check で使われる `stdlib/isomac.c` はこれを利用しています。isomac.c の問題は、マクロ定義ファイルの形式として GCC の出力形式だけを想定していて、コメントも空行もエラーになってしまうことです。

glibc の make では rpcgen というプログラムが使われることがあります。このプログラムの問題は、プリプロセッサの行番号情報の出力形式としてやはり GCC の

    #123 "filename"

という形式だけを想定していて、

    #line 123

も

    #line 123 "filename"

もエラーになってしまうことです。

mcpp では GCC 版では GCC の形式をデフォルトにしました。しかし、rpcgen がこういう特殊な形式を前提にして、標準的な形式に対応していないというのは、お粗末な仕様です。

#### 3.9.4.9. -include, -isystem, -I- オプション

glibc 2.1 の makefile では、-include オプションがしばしば使われています。時には -isystem オプションや -I- オプションも使われます。-include はソースの冒頭で #include すればすむもので、-isystem, -I- はシステムヘッダを更新する場合しか必要性の感じられないものです。
mcpp では GCC 用の実装に限って、この３つのオプションも実装しましたが、あまり必要のないオプションは整理してもらいたいものです。*1

注:

*1 GCC / cpp にはこのほかに -iprefix, -iwithprefix, -iwithprefixbefore, -idirafter といった include directory とその順序を指定するオプションがいくつもある。また、long-file-name と MS-DOS 上の 8+3 形式のファイル名との対応表の使用を指定する -remap オプションもある。これらは CygWIN システムの specs ファイル等で使われることがあるが、include directory は環境変数で指定しておけばすむことであり、8+3 形式のファイル名への対応がいまさら CygWIN で必要だとも思えない。

#### 3.9.4.10. Undocumented な事前定義マクロ

これは glibc の問題ではなく、GCC の問題です。

    __VERSION__, __SIZE_TYPE__, __PTRDIFF_TYPE__, __WCHAR_TYPE__

以上の名前はドキュメントには見当たりませんが、GCC / cpp では事前定義マクロとなっています。`__VERSION__` の値は Vine Linux 2.1 (egcs-1.1.2) では "egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)" となっています。他の３つは Linux / i386 をはじめ多くのシステムではそれぞれ unsigned int, int, long int となっているようです。しかし、FreeBSD, CygWIN では少しずつ違っています（なぜ違う必要があるのかわからないが）。
こういうことがどうしてドキュメントにないのでしょうか。

#### 3.9.4.11. Undocumented な環境変数

もっとも奇怪なのは `SUNPRO_DEPENDENCIES` という undocumented な環境変数です。`sysdeps/unix/sysv/linux/Makefile` には

    SUNPRO_DEPENDENCIES='$(@:.h=.d)-t $@' \
    $(CC) -E -x c $(sysinclude) $< -D_LIBC -dM | \
    ... \
    etc.

という script があります。これは `SUNPRO_DEPENDENCIES` という環境変数でファイル名を指定し、cpp がソース中のマクロ定義とソースファイルの依存関係行をその指定されたファイルに出力するというものなのです。

この動作を理解するには、GCC / cpp のソース (egcs-1.1.2/gcc/cccp.c) を読むしかありませんでした。
このほか、`DEPENDENCIES_OUTPUT` という環境変数もあり、同様の意味を持っています。`SUNPRO_DEPENDENCIES` のほうは system headers の依存関係行も出力するのに対して、`DEPENDENCIES_OUTPUT` はそうではないという違いがあります。

mcpp では GCC 対応版に限って、この２つの環境変数に対応させましたが、こういう「裏仕様」のようなものは早く廃止してほしいものです。

#### 3.9.4.12. その他の問題

Linux (i386) / GCC ではこのほか、specs ファイルの指定によって、cpp の呼び出しに -Asystem(unix) -Acpu(i386) -Amachine(i386) というオプションが付加されます。しかし、これを利用するソースは少なくとも glibc 2.1.3 の Linux / x86 版にはないようです。
Glibc からインストールされるシステムヘッダがつぎはぎだらけの非常に複雑なものになってきていることは、大きな問題です。ちょっとした設定の違いによって処理結果が違ってくる恐れがあります。

他方で、FreeBSD 2.2.2 / kernel ソースで見られた #else junk, #endif junk やマクロの二重定義は glibc 2.1.3 では見られませんでした。Glibc 2.1 のソースが FreeBSD 2 / kernel のソースより整理されている面もいくらかあります。

しかし全体としては、glibc 2.1 には GCC の特殊な仕様に依存しているソースが稀ならずあり、他の処理系への移植は困難になっています（数千本のソースファイルの中ではごく一部であるが）。プログラムの可読性やメンテナンス性のためにも、こうした GCC local な仕様への依存は好ましくありません。GCC V.3 ではこれらの裏技的仕様を廃止し、それに依存するソースを一掃することを期待したいと思います。

### 3.9.5. GCC 2 で mcpp を使うには

mcpp を glibc 2.1 のコンパイルに使うには、まず一部のソースの修正が必要です。*1
一つは可変個引数のマクロの定義と呼び出しです。上記 3.9.4.3 にある 14 個のファイルについて、3.9.1.6 にあるような形で修正します。もちろん、元のファイルも残しておいたほうが良いでしょう。
もう一つは、3.9.4.6 にある３つのファイルの、置換リストに "defined" が出てくるマクロの修正です。また、`/usr/include/_G_config.h` は `sysdeps/unix/sysv/linux/_G_config.h` が install されてできる同一のファイルですが、こちらも修正しておいたほうが良いでしょう。

mcpp の起動には、Makefile や specs ファイルで付加されるオプションのほかに、行をまたぐ文字列リテラルやアセンブラ用のコメントを含む *.S ファイルのために -lang-asm (-x assembler-with-cpp) が必要です。このオプションは他のファイルのプリプロセスにも付けておいて、通常はかまいません。

GCC / cpp を使ったり mcpp を使ったり、デフォルトで付加するオプションを変更したりするためには、次のようにするのが良いでしょう。

Super-user になって、cpp の存在するディレクトリ（ここでは `/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66` とする）に行きます。ここに GCC / cpp が cpp という名前で存在し、mcpp が mcpp という名前で install されているとします。まず、次のような内容の mcpp.sh という名前のファイルを作ります。*2

    #!/bin/sh
    /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/mcpp -Q -lang-asm "$@"

-Q オプションはなくてもかまいませんが、大量の診断メッセージを記録するには付けたほうが良いでしょう。
さらに、次のようなコマンドを打ち込みます。

    chmod a+x mcpp.sh
    mv cpp cpp_gnuc
    ln -sf mcpp.sh cpp

こうしておくと、gcc が cpp を呼び出した時に、それにリンクされている mcpp.sh が実行され、mcpp に上記のオプションを（gcc の付加するオプションの前に）付加して呼び出します。

デフォルトのオプションを変更する時は、mcpp.sh を変更するなり、mcpp を直接呼び出すなりします。そして、GCC / cpp を使う時は

    ln -sf cpp_gnuc cpp

とします。

注:

*1 mcpp V.2.7 からは GCC-specific-build にこれらの仕様が実装されたので、ソースを修正しなくてもプリプロセスはできるようになった。

*2 mcpp を configure してインストールした場合は自動的に適切な設定がされる。 あとは -Q -lang-asm オプションを追加するだけですむ。

#### 3.9.5.1. mcpp のウォーニングを整理するには

mcpp を使う場合のもう一つの問題は、大量のウォーニングが出力されることです。-Q オプションでリダイレクトしても、glibc のような大規模なソースを処理すると mcpp.err が総計数百 MB 以上になるので、すべてに目を通すわけにはいきません。
しかし、この内容を見ると、同じウォーニングが繰り返し出ていることがわかります。同じ *.h ファイルが多くのソースから #include されるために、同じウォーニングが繰り返し繰り返し出るのです。これを整理して見るには、次のようにします。
まず、エラーをチェックします。

    grep 'fatal:' `find . -name mcpp.err`
    grep 'error:' `find . -name mcpp.err`

次に、ウォーニングを整理します。

    grep 'warning:' `find . -name mcpp.err` | sort -k3 -u > mcpp-warnings-sorted

ウォーニングの出所をすべて見るためには、次のようにします。

    grep 'warning:' `find . -name mcpp.err` | sort -k3 | uniq > mcpp-warnings-all

特定の種類のウォーニングを見るには、たとえば次のようにします。

    grep 'warning: Replacement' `find . -name mcpp.err` | sort -k3 | uniq | less

こうして見当をつけたうえで、該当する mcpp.err を less で見て内容を確認し、必要ならソースを見ます。
さらに必要なら、ソースの問題の個所を #pragma MCPP debug expand, #pragma MCPP end_debug 等で挟んで再度プリプロセスして、その出力を見ます。この時には、プリプロセスの出力と診断メッセージとが同じファイルに出るように、次のようにします（make する場合は、上記の shell-script を一時書き換える）。

    mcpp <-opts> in-file.c > in-file.i 2>&1

### 3.9.6. GCC 3.2 ソースのプリプロセス

Linux および FreeBSD で、GCC 2.95.* で GCC 3.2R (2002/08) のソースをコンパイルしてみました。そして、生成された gcc を使って mcpp をコンパイルし、次にプリプロセスにそれを使って GCC 3.2 で GCC 3.2 のソースをリコンパイルしてみました。

GCC の make はいくつかの段階を経て bootstrap されてゆくようになっています。すなわち、最初の段階で生成された gcc, cc1, etc. を使って自分自身をリコンパイルし、そうやって再生成されたものを使ってまた自分自身をリコンパイルし、といった経過をたどります。gcc は bootstrap の途中では xgcc という名前で存在しています。

また、GCC 2 では cc1, cc1plus からは独立していた cpp が、GCC 3 では cc1, cc1plus に吸収されてしまいました。しかし、独立したプリプロセッサである cpp0 も存在しており、gcc や g++ に -no-integrated-cpp というオプションを付けるとこれにプリプロセスをさせることができるようになっています。したがって、mcpp にプリプロセスをさせるためには、gcc (xgcc), g++ の呼び出しを shell-script に置き換えて、mcpp => cc1 または mcpp => cc1plus という順序で実行されるようにしなければなりません。*1

GCC のシステムでは、システムヘッダやそのサーチ順の設定が非常に複雑になってきています。また、GCC 3 では GCC が内部的に使う C++ の shared library の仕様が GCC 2 とは変わったようです。これらのためか、コンパイルするだけでもうまくゆかないことがあります。また、コンパイルとテストには多くの他のソフトウェアも必要で、それらのバージョンが古いと、うまくゆかないことがあります。私のところではハードウェアの問題でうまくコンパイルできないこともありました。

FreeBSD 4.4R では GCC 3.2 はコンパイルできませんでした。FreeBSD を 4.7R に upgrade し、packages を 4.7 用のものに入れ替えて、ようやくコンパイルすることができました。*2
私のところでは２台のパソコンに同じ VineLinux 2.5 が入っていますが、その片方 (K6/200MHz) では GCC 2.95.3 を使ってのコンパイルはできたものの、生成された GCC 3.2 / cc1 が segmentation fault を繰り返し起こしてしまい、自分自身のリコンパイルができませんでした。その後、K6 を AthlonXP に取り替えてリコンパイルしたところ、今度は segmentation fault は発生しませんでした。問題はハードウェアだったのかもしれません。
また、FreeBSD では K6 のパソコンで GCC 2.95.4 を使って GCC 3.2 をコンパイルした時は、make -k check ではほとんどすべて通ったのですが、さらに生成された GCC 3.2 で GCC 3.2 自身をリコンパイルしたところ、今度は、g++, libstdc++-v3 が make -k check で testsuite の２割近くが通らないという現象も起こりました。しかし、AthlonXP に取り替えてからはうまくゆくようになりました。これもハードウェアの問題だったのかもしれません。

また、VineLinux で GCC 3.2 自身と mcpp を使ってリコンパイルした場合は、生成された gcc は make -k check を通りましたが、g++, libstdc++-v3 は testsuite の２割近くが通りませんでした。*3, *4, *5
どちらにしても、生成された gcc, g++, cc1, cc1plus 等の問題ではなく、ヘッダファイルかライブラリか何かの設定の微妙な問題のようです。
mcpp は GCC とは完全に互換ではありませんが、かなり高い互換性をもっているので、取り替えて使ってほぼ問題はないと思われます。

GCC 3.2 のコンパイルに使ったシステムは次のものです。

> OS | make | library | CPU
> VineLinux 2.5 | GNU make | glibc 2.2.4 | Celeron/1060MHz
> VineLinux 2.5 | GNU make | glibc 2.2.4 | K6/200MHz
> VineLinux 2.5 | GNU make | glibc 2.2.4 | AthlonXP/2.0GHz
> FreeBSD 4.7R | UCB make | libc.so.4 | K6/200MHz
> FreeBSD 4.7R | UCB make | libc.so.4 | AthlonXP/2.0GHz

コンパイルしたのは C と C++ だけです。

注:

*1 これを bootstrap の各段階ごとにやらなければならないのである。 Makefile は手を入れるにはあまりに大きく複雑なので、画面に張り付いていて、stage が変わったところで ^C で中断して、script に置き換えるという不細工な方法をとった。

*2 しかも、多くの packages の間の依存関係があるので、バージョンがまちまちだと混乱が生じる。私のところではこのために、一時は kterm が起動しないという状態に陥ったこともある。

*3 make -k check する時は mcpp は使ってはいけない。診断メッセージが GCC とは異なるからである。

*4 make -k check する時は環境変数 LANG, LC_ALL を C として、英語環境にしないといけない。

*5 Testsuite が通らない直接の原因はすべて、`i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.5.0.0` というライブラリで pthread_getspecific, pthread_setspecific 等 pthread_* という関数がリンクできないというものである。正しく生成されたこのライブラリを入れてやれば、make -k check は通る。FreeBSD ではこの問題は起こらない。何かの設定の微妙な問題のようである。

#### 3.9.6.1. 行をまたぐ文字列リテラル

このあまりにも古い書き方は GCC 3.2 のソースにはありません。この仕様は GCC 3.2 でようやく obsolete とされました。ソース中にこれがあると、期待通りに処理はされますが、ウォーニングが出ます。

#### 3.9.6.2. #include_next, #warning

Make の途中で生成される `build/gcc/include` の limits.h, syslimits.h には #include_next があり、GCC 3.2 を install すると、`lib/gcc-lib/i686-pc-linux-gnu/3.2/include` の limits.h, syslimits.h にコピーされます。

#warning は見当たりませんでした。

#### 3.9.6.3. 可変引数マクロ

可変引数マクロはいくつかありますが、大半は testsuite のもので、テスト用に書かれたサンプルにすぎません。そして、GCC 2 以来の記法はまだサポートされてはいるものの、`__VA_ARGS__` を使った C99 のものが多くなっています。

また、GCC 3 では GCC 2 の仕様と C99 の仕様との折衷的な書き方が追加されています。次の形です。*1

    #define eprintf( fmt, ...)   fprintf( stderr, fmt, ##__VA_ARGS__)

これは GCC 2 仕様の次の書き方に対応します。

    #define eprintf( fmt, args...)   fprintf( stderr, fmt, ##args)

これは、`...` に対応する引数がなかった時は、その直前のコンマを削除するという仕様です。たとえば、次のように展開されます。

    eprintf( "success!\n")  ==>  fprintf( stderr, "success!\n")

この例を見ると便利な仕様のようですが、マクロ定義の置換リスト中のコンマはパラメータを区切るものとは限らないこと、トークン連結演算子である ## に別の働きをさせていること、規則に例外を作って複雑にするものであること、等の問題があります。*2, *3, *4

注:

*1 このマニュアルでは、GCC 2 以来の可変引数マクロを GCC2 仕様、GCC 3 で追加されたものを GCC3 仕様と呼ぶことにする。

*2 GCC 2.* では GCC2 仕様の可変引数マクロの定義の `args...` 等では `args` と `...` とはくっついていなければならなかったが、GCC 3 では間に space が入っていても良くなった。

*3 ただし、-ansi オプションが指定された場合（-std=c*, -std=iso* オプションでも同様）、可変引数が存在しなくてもコンマは削除されない。 しかし、'##' は黙って消え去る。

*4 mcpp では V.2.6.3 から GCC-specific-build の STD モードに限って、GCC3 仕様の可変引数マクロも実装された。 さらに V.2.7 からは GCC2 仕様のものにも対応した。

#### 3.9.6.4. マクロ呼び出しのカラ引数

マクロ呼び出しのカラ引数は、#include されるシステムヘッダのもの（`/usr/include/bits/mathcalls.h, /usr/include/bits/sigset.h`）を別とすると、GCC 3.2 のソース自体では、`gcc/libgcc2.h` にだけ見られます。*1

注:

*1 この２つのヘッダファイルは glibc を install することでシステムヘッダに入ってゆくものである。FreeBSD では glibc を使っていないので、これらのシステムヘッダは存在しない。

#### 3.9.6.5. Function-like マクロの名前に置換される object-like マクロ

`gcc/fixinc/gnu-regex.c, libiberty/regex.c` には、function-like マクロの名前に置換される object-like マクロの定義が見られます。また、#include される `/usr/lib/bison.simple` も同様です。これらはすべて alloca に関するものです。たとえば、`libiberty/regex.c` にはこういうマクロ定義があります。

    #define REGEX_ALLOCATE  alloca
    #define alloca( size)   __builtin_alloca( size)

これは、こう書けば問題ないのですが、なぜこんなところを省略するのでしょうか。

    #define REGEX_ALLOCATE( size)   alloca( size)

この regex.c では alloca は場合によっては次のように定義されるようになっており、スタイルが一貫していません。

    #define alloca  __builtin_alloc

また、regex.c には #include "regex.c" という行があり、自分自身をインクルードするようになっています。複雑怪奇なソースです。

#### 3.9.6.6. 'defined' に展開されるマクロ

これは GCC 3.2 のソースには見当たりません。

ドキュメントによると、この種のマクロの処理は GCC 2 / cpp と同じであるものの、「portability がない」というウォーニングを出すことになっています。ただ、テストしてみると、3.9.4.6 の例ではウォーニングが出ません。

#### 3.9.6.7. .S ファイルの「プリプロセス」

GCC 3 / cpp のドキュメントには、次のように書かれています。

> Wherever possible, you should use a preprocessor geared to the language you are writing in. Modern versions of the GNU assembler have macro facilities.

しかし、GCC 3.2 自身のソースには、*.S ファイルが `gcc/config` ディレクトリに数本あります。

#### 3.9.6.8. rpcgen と -dM オプションの仕様の問題

GCC 3.2 の make では rpcgen も -dM オプションも使われませんでした。しかし、rpcgen や -dM オプションの仕様は特に変わってはいないようです。

#### 3.9.6.9. -include, -isystem, -I- オプション

これらのオプションはしばしば使われており、-isystem オプションで system include directory が同時に数個指定される場合さえあります。こういう、システムヘッダそのものを更新するソフトウェアのコンパイルでは、やむをえないことなのでしょうか。環境変数で一括して指定したほうが、わかりやすいと思うのですが。

一方で、GCC 3 / cpp のドキュメントでは、-iwithprefix, -iwithprefixbefore オプションについては、「使わないほうが良い (discouraged)」と書かれています。GCC には include directory を指定するオプションがやたらにありますが、整理する方向に入ってきたのでしょうか。*1

注:

*1 しかし、GCC 3.2 の Makefile は -iprefix オプションを付けている。 ところが -iwithprefix, -iwithprefixbefore は使われないのである。-iprefix オプションは、その後にこの２つのオプションのどちらかがあって初めて意味を持つのであるが。

#### 3.9.6.10. Undocumented な事前定義マクロ

GCC 2 では `__VERSION__`, `__SIZE_TYPE__`, `__PTRDIFF_TYPE__`, `__WCHAR_TYPE__` 等の事前定義マクロについては、ドキュメントに記載がなく、-dM オプションでも知ることができませんでしたが、GCC 3 ではドキュメントに意味が記載され、具体的な値も -dM で知ることができるようになりました。

#### 3.9.6.11. Undocumented な環境変数

GCC 2 ではドキュメントに記載のなかった `SUNPRO_DEPENDENCIES` という環境変数については、GCC 3 ではドキュメントに記載されるようになりました（しかし、なぜこんなものが必要なのかはわからない）。

#### 3.9.6.12. その他の問題

GCC 3 では次のような #pragma が実装されています。

    #pragma GCC poison
    #pragma GCC dependency
    #pragma GCC system_header

GCC 3.2 のソースでも、このうち poison と system_header が使われています。しかし、mcpp ではこれらはサポートしていません。仕様の説明は省きますが、必要性があまり感じられないからです。*1

GCC 3 では #assert 等の assertion directives は「推奨しない (deprecated)」とされました（しかし、gcc はデフォルトで -A オプションを発行するが）。

また、GCC 2 では -traditional オプションは同一の cpp で実装されており、そのため非常に古い仕様と C90 の仕様が混在する奇怪な仕様となっていましたが、GCC 3.2 ではプリプロセッサが通常の cpp0 と tradcpp0 とに分けられました。-traditional オプションは gcc に対してだけ有効で、cpp0 にはありません。gcc -traditional はプリプロセスに tradcpp0 を呼び出します。tradcpp0 は C90 以前の真に traditional なプリプロセッサに近いものとなっています。そして、tradcpp0 のほうは今後、深刻なバグの修正以外はメンテナンスしないとされています。

GCC 2 / cpp の奇妙な仕様はだいぶ修正されてきたように見えます。

注:

*1 mcpp V.2.7 からは GCC-specific-build で #pragma GCC system_header をサポートした。

### 3.9.7. GCC 3, 4 で mcpp を使うには

以上に見てきたように、GCC 3.2 のソースは少なくともプリプロセス上は、glibc 2.1.3 などに比べるとかなりきれいなものになっています。Traditional な書き方はほぼ一掃され、意味のないオプションは使われなくなってきています。
また、GCC 3.2 / cpp0 そのものも、traditional な仕様を obsolete なものとして扱い、token-based な原則を明確にするなど、GCC 2 / cpp に比べると格段に優れたものとなっています。ドキュメントも undocumented な部分が大幅に減りました。まだ不徹底な面も多々ありますが、方向としては良い方向に向かっていると思われます。

ただ、GNU のシステムではシステムヘッダが複雑化する一方で、何がどうなっているのか容易に把握できないようになっています。これが GNU のシステムのトラブルの最大の要因となってくるのではないでしょうか。

もう一つ残念なのは、プリプロセスがコンパイラ本体に吸収されてしまったことです。そのため、mcpp を使うには gcc や g++ を -no-integrated-cpp というオプションを付けて呼び出す必要があります。複雑な makefile や多くの makefile を持つ大きなソースファイル群をコンパイルする場合や、何かのプログラムから gcc が自動的に呼び出される場合は、gcc, g++ の呼び出しを shell-script に置き換えて、このオプションが自動的に付加されるようにしなければなりません。

具体的には、gcc, g++ の置かれているディレクトリ（私の Linux の例では `/usr/local/gcc-3.2/bin`）に次のような script をそれぞれ gcc.sh, g++.sh という名前で置きます。

    #!/bin/sh
    /usr/local/gcc-3.2/bin/gcc_proper -no-integrated-cpp "$@"

    #!/bin/sh
    /usr/local/gcc-3.2/bin/g++_proper -no-integrated-cpp "$@"

そして、このディレクトリで次のようにします。

    chmod a+x gcc.sh g++.sh
    mv gcc gcc_proper
    mv g++ g++_proper
    ln -sf gcc.sh gcc
    ln -sf g++.sh g++

また、cpp の置かれているディレクトリ（私の Linux の例では `/usr/local/gcc-3.2/lib/gcc-lib/i686-pc-linux-gnu/3.2`）で、GCC 2 の場合と同様に、cpp0 の呼び出しで mcpp が実行されるようにしておきます（ 3.9.5 参照）。*1
こうしておくと、gcc や g++ からまず mcpp が呼び出され、その後に cc1, cc1plus が -fpreprocessed というプリプロセス済みであることを示すオプションを付けて呼び出されるようになります。

なお、システムの標準と異なるバージョンの GCC をインストールした場合は付加的な include directory の設定が必要なことがありますが、mcpp ではこれらも mcpp のコンパイル時に組み込むので、通常は環境変数で設定する必要はありません。

できれば cc1, cc1plus のプリプロセス部分である cpplib のソースを mcpp のものに置き換えたいところですが、cpplib の cc1, cc1plus との内部的な interface および cpplib を使うユーザプログラムとの外部的な interface を定義しているソースファイルが合わせて 46KB もあり、とても置き換えは不可能です。どうしてこういう複雑な interface にする必要があるのでしょうか。残念なことです。

注:

*1 configure して mcpp をインストールした場合は、これらが自動的に設定される。

#### 3.9.7.1. GCC 3.3, 3.4-4.1 で mcpp を使うには

良い方向に向かっていた GCC V.3 でしたが、V.3.3 からは妙な方向に転換してしまいました。V.3.3 は V.3.2 と比べると、次のような点で大きく変わっています。

1. 単体の cpp0 はなくなった。gcc -no-integrated-cpp はまだ使えるが、そこから呼び出されるのは cc1 (cc1plus) である。すなわち、プリプロセスでもコンパイルでも cc1 が呼び出される。そして、プリプロセスフェーズの cc1 にプリプロセス用でないオプションがしばしば渡される（何という汚い実装！）。
2. 60 個から 70 個の大量のマクロが事前定義されている。これによってシステムヘッダと GCC との関係がさらに複雑になった。
3. tradcpp もなくなり、GCC V.3.2 では obsolete とか deprecated とされた古い仕様の一部が復活した。

全体として、１つの巨大なコンパイラにすべてを吸収する方向となっており、Ｃ処理系の構成のしかたとしても、オープンソースの処理系の開発の方向としても、大いに疑問のあるところです。

mcpp の移植では、gcc からどんなオプションが渡されてくるかわからないというのが困るところです。間違ったオプションもすべてチェックせずに無視するのでは危険があります。とりあえず、しばしば間違って渡されてくるオプションは無視するようにしましたが、それ以外のオプションが渡されるとエラーになるはずです。
これだけの変更でも、mcpp の従来のオプションの中には使えなくなったものがあります。V.2.5 からは -E オプションは廃止し、-m オプションは -e に、-c は -@compat に変更しました。
また、GCC V.3.2 では cpp0 の呼び出しを mcpp に置き換えればすんだところが、今度は cc1 (cc1plus) の呼び出しを mcpp と cc1 (cc1plus) とに振り分けることが必要になります。このための shell-script は src/set_mcpp.sh の中に用意しました。大量の事前定義マクロは１つ１つ対応しているわけにはいかないので、GCC の -dM オプションの出力を一括して利用するようにしました。*1, *2, *3

GCC V.3.4 ではさらに、multi-byte character はすべて UTF-8 に変換してから処理するように変わりました。ドキュメントによると、具体的には次のようになっています。*4

1. プリプロセスの最初のフェーズで、multi-byte character を UTF-8 に変換する。
2. この変換には libiconv の関数を使う。したがって、iconv が対応している encoding はすべて使える。
3. ソースファイルの encoding を指定するには -finput-charset=<encoding> オプションを使う。（実際には、これを指定しなければ UTF-8 には変換されない）。
4. コンパイル後の encoding はデフォルトでは UTF-8 であるが、-fexec-charset=<encoding> オプションで他の encoding を指定することができる。*5

「国際化」と言えば Unicode に対応させることと考える風潮が、ことに実際に multi-byte character を使わない西欧の人々の間にありますが、この風潮が GCC にも及んでしまったようです。

しかも、仕様がまだ十分に実装されていません。実際に使ってみると、次のようになります。

1. EUC-JP, GB2312, KSC-5601, Big5 は -finput-charset オプションを指定すると UTF-8 に正しく変換され、指定しなければそのまま通る。*6
2. -fexec-charset オプションは V.3.4, 4.0 では効かなかったが、V.4.1-4.3 では効く。
3. ISO2022-JP は V.3.4, 4.0 では処理できなかったが、V.4.1-4.3 ではできる。
4. どのバージョンでも shift-JIS は -finput-charset を指定するとかえって混乱してしまう。

mcpp は -e <encoding> オプションで encoding を指定しますが、GCC-specific-build では、BIG5, shift-JIS, ISO2022-JP では <backslash> 等と一致する値のバイトの直前に <backslash> を挿入してコンパイラの欠陥を補います。しかし、UTF-8 には変換せず、元の encoding のまま出力します。-finput-charset オプションも -e オプションと同じものとして扱います。これは次のような理由です。*7

1. SJIS は -f*-charset を指定すると、どのバージョンの GCC でもダメである。しかし、どのバージョンの GCC でも、SJIS, JIS, BIG5 は変換せずに <backslash> を補ってやるとそのまま通り、期待通りの結果になる。 EUC-JP, GB2312, KSC-5601 も変換しなければそのまま通る。 すなわち、single byte character sequence であるかのように扱われる。
2. GCC 4.0 までのバージョンではどの encoding も UTF-8 から元の encoding に戻せない。
3. GCC の近い将来の仕様変更を期待したい。

注:

*1 しかし、-dM オプションの出力は他のオプションによって少し違ってくるのである。 しかも、これらの事前定義マクロの大半は undocumented なものである。 このため、全容はなかなか把握できない。

*2 MinGW では symbolic link が使えない。ln -s というコマンドはあるが、これは単にコピーするだけのものである。また、MinGW の GCC はたとえ cc1 という名前のファイルであってもそれが shell-script であると、起動を拒否する。そのため、mcpp の GCC-specific-build では、MinGW に限って cc1.exe という名前の実行プログラムを生成する（これは cc1plus.exe にもコピーされる）。そして、その中から mcpp.exe または GCC の cc1.exe ないし cc1plus.exe を起動するようにしている。

*3 CygWIN / GCC では -mno-cygwin というオプションがあり、これを指定すると system include directory が変わり、GCC の事前定義マクロも変わる。このため、mcpp の CygWIN GCC-specific-build では V.2.6.1 からは２セットの事前定義マクロを用意するようにした。

*4 私のところの FreeBSD 6.3 ではなぜか GCC のこの変換はまったく動作しない。libiconv はリンクされているのであるが。 FreeBSD 5.3, 6.2 でもそうであった。

*5 この変換はプリプロセスフェーズではなく、コンパイルフェーズで行われるようである。 -E オプションの出力は UTF-8 のままである。

*6 しかし、GCC V.4.1-4.3 では、-save-temps または -no-integrated-cpp オプションを -f*-charset オプションと同時に指定すると、エラーになるというバグがある。

*7 mcpp の出力を cc1 に渡す時には -finput-charset はもちろん -fexec-charset オプションも付けてはいけない。

### 3.9.8. Linux / glibc 2.4 ソースのプリプロセス

glibc 2.4 (2006/03) のソースをコンパイルして、プリプロセスをチェックしてみました。 処理系は GCC 4.1.1 で、プリプロセッサを mcpp 2.6.3 に置き換えたものです。 マシンは x86 系なので、他の CPU 用のコードはチェックしていません。

以前に私がチェックした glibc 2.1.3 (2000/02) から６年経ったバージョンなので、大きく変わったところももちろんありますが、意外に変わっていないところも多く見られます。 全体としては、以前のバージョンについて私が取り上げた問題点は整理されずに、むしろ増える傾向にあります。

#### 3.9.8.1. 行をまたぐ文字列リテラル

行をまたぐ文字列リテラルは見られなくなりました。

#### 3.9.8.2. #include_next, #warning

次のファイルには #include_next があります。 ５年半前のバージョンと比べると増えています。

`catgets/config.h, elf/tls-macros.h, include/bits/dlfcn.h, include/bits/ipc.h, include/fpu_control.h, include/limits.h, include/net/if.h, include/pthread.h, include/sys/sysctl.h, include/sys/sysinfo.h, include/tls.h, locale/programs/config.h, nptl/sysdeps/pthread/aio_misc.h, nptl/sysdeps/unix/sysv/linux/aio_misc.h, nptl/sysdeps/unix/sysv/linux/i386/clone.S, nptl/sysdeps/unix/sysv/linux/i386/vfork.S, nptl/sysdeps/unix/sysv/linux/sleep.c, sysdeps/unix/sysv/linux/ldsodefs.h, sysdeps/unix/sysv/linux/siglist.h`

次のファイルは make check で使われるテスト用のものですが、ここにも #include_next があります。

`sysdeps/i386/i686/tst-stack-align.h`

#warning は `sysvipc/sys/ipc.h` にありますが、正常にコンパイルされるときはスキップされるブロックにあるので、ひっかかることはありません。

#### 3.9.8.3. 可変引数マクロ

次のファイルには可変引数マクロの定義がありますが、これらはすべて GCC2 以来の古い仕様のマクロです。 C99 の仕様のものはおろか、GCC3 仕様のものさえも見当たりません。

`elf/dl-lookup.c, elf/dl-version.c, include/libc-symbols.h, include/stdio.h, locale/loadlocale.c, locale/programs/ld-time.c, locale/programs/linereader.h, locale/programs/locale.c, locale/programs/locfile.h, nptl/sysdeps/pthread/setxid.h, nss/nss_files/files-XXX.c, nss/nss_files/files-hosts.c, sysdeps/generic/ldsodefs.h, sysdeps/i386/fpu/bits/mathinline.h, sysdeps/unix/sysdep.h, sysdeps/unix/sysv/linux/i386/sysdep.h`

次はテスト用のルーチンですが、ここにも GCC2 仕様の可変引数マクロの定義があります。

`localedata/tst-ctype.c, posix/bug-glob2.c, posix/tst-gnuglob.c, stdio-common/bug13.c`

しかも実際にこれらのマクロを呼び出すときには、可変引数が１つもない（カラ引数さえもない）変則的な呼び出しがきわめて多くなっています。 この種の変則的なマクロ呼び出しは 142 本ものソースファイルに見られます。 そのうち、置換リストで可変引数の直前に ", ##" という sequence があってこの ',' が削除されるケースは、120 本のソースに見られます。

可変引数マクロは C99 の仕様を使ったほうが portable で良いのですが、GCC2 あるいは GCC3 仕様の可変引数マクロを C99 仕様のものに書き直すのは、必ずしも簡単ではありません。 GCC2 あるいは GCC3 仕様では実際のマクロ呼び出しで可変引数がない場合、その直前のコンマが削除されるので、マクロの呼び出し方によっては C99 仕様とは１対１に対応しないからです。 マクロ定義を C99 仕様に書き換えた場合、コンマを削除しないですむようにするためには、マクロ呼び出しのほうも書き直す必要があります。

glibc 2.1.3 ではまだこれらのマクロの数がさほど多くはなかったので、ユーザが目で確かめながらエディタで書き換えることができましたが、glibc 2.4 では数が増え、しかもそれらのマクロの呼び出し個所が大幅に増えたため、ユーザが書き換えることは不可能になりました。

このため、mcpp では V.2.6.3 から GCC-specific-build に限って、GCC3 仕様の可変引数マクロも実装しました。 さらに V.2.7 からは GCC2 仕様のものにも対応しました。 しかし、GCC2 仕様のものは token-based な原則から掛け離れているので、それを使うマクロを新しく書くべきではありません。 GCC2 仕様は GCC3 仕様と１対１に対応しているので、マクロ定義を GCC3 仕様に書き直すことは容易にでき、マクロ呼び出しを書き直さなくてもすみます。 すでに GCC2 仕様で書かれているマクロはこう書き換えたほうが、いくらかわかりやすくなります。*1

GCC2 仕様の可変引数マクロを GCC3 仕様に書き直すには、例えば次のようなものを

    #define libc_hidden_proto(name, attrs...)   hidden_proto (name, ##attrs)

次のようにします。

    #define libc_hidden_proto(name, ...)    hidden_proto (name, ## __VA_ARGS__)

すなわち、パラメータの `attrs...` を `...` にし、置換リスト中の `attrs` を `__VA_ARGS__` にします。

注:

*1 GCC2 仕様および GCC3 仕様の可変引数マクロについては、3.9.1.6, 3.9.6.3 を参照のこと。

#### 3.9.8.4. マクロ呼び出しのカラ引数

カラ引数のあるマクロ呼び出しはきわめて多く、488 本ものソースファイルに見られます。 以前のバージョンと比べても大幅に増えました。 C99 でカラ引数が公認されたことが影響しているのでしょうか。

ことに `math/bits/mathcalls.h` には 79 個ものカラ引数マクロ呼び出しがあります。 以前とほぼ同様です。

#### 3.9.8.5. Function-like マクロの名前に置換される object-like マクロ

次のファイルには function-like マクロの名前に置換される object-like マクロの定義が見られます。

`argp/argp-fmtstream.h, hesiod/nss_hesiod/hesiod-proto.c, intl/plural.c, libio/iopopen.c, nis/nss_nis/nis-hosts.c, nss/nss_files/files-hosts.c, nss/nss_files/files-network.c, nss/nss_files/files-proto.c, nss/nss_files/files-rpc.c, nss/nss_files/files-service.c, resolv/arpa/nameser_compat.h, stdlib/gmp-impl.h, string/strcoll_l.c, sysdeps/unix/sysv/linux/clock_getres.c, sysdeps/unix/sysv/linux/clock_gettime.c`

`elf/link.h` には次のように function-like マクロの名前に置換される function-like マクロがあります。

    #define ELFW(type) _ElfW (ELF, __ELF_NATIVE_CLASS, type)
                                            /* sysdeps/generic/ldsodefs.h:46    */
    #define _ElfW(e,w,t)    _ElfW_1 (e, w, _##t)            /* elf/link.h:32    */
    #define _ElfW_1(e,w,t)  e##w##t                         /* elf/link.h:33    */
    #define __ELF_NATIVE_CLASS __WORDSIZE               /* bits/elfclass.h:11   */
    #define __WORDSIZE 32           /* sysdeps/wordsize-32/bits/wordsize.h:19   */
    #define ELF32_ST_TYPE(val) ((val) & 0xf)                /* elf/elf.h:429    */

上記のようなマクロ定義があると、

        && ELFW(ST_TYPE) (sym->st_info) != STT_TLS      /* elf/do-lookup.h:81   */

このマクロ呼び出しでは ``ELFW`(ST_TYPE)` が次のように展開されていきます。

        ELFW(ST_TYPE)
        _ElfW(ELF, __ELF_NATIVE_CLASS, ST_TYPE)
        _ElfW_1(ELF, 32, _ST_TYPE)
        ELF32_ST_TYPE

そして、`ELF32_ST_TYPE` がさらに `(sym->st_info)` を巻き込んで `((sym->st_info) & 0xf)` と展開されます。 すなわち、``_ElfW_1`(ELF, 32, _ST_TYPE)` という function-like macro の呼び出しが `ELF32_ST_TYPE` という別の function-like macro の名前に展開されるのです。

これは３つのマクロ定義を次のように書いて、

    #define ELFW( type, val)        _ElfW( ELF, __ELF_NATIVE_CLASS, type, val)
    #define _ElfW( e, w, t, val)    _ElfW_1( e, w, _##t, val)
    #define _ElfW_1( e, w, t, val)  e##w##t( val)

次のように呼び出したほうが、わかりやすくなります。 引数の渡し方が少し冗長に見えるかもしれませんが、関数呼び出しをモデルとして考えれば、こちらのほうが自然でしょう。

        && ELFW(ST_TYPE, sym->st_info) != STT_TLS

#### 3.9.8.6. 'defined' に展開されるマクロ

次のファイルには、置換リスト中に 'defined' というトークンの出てくるマクロの定義があります。*1

`iconv/skeleton.c, sysdeps/generic/_G_config.h, sysdeps/gnu/_G_config.h, sysdeps/i386/dl-machine.h, sysdeps/i386/i686/memset.S, sysdeps/mach/hurd/_G_config.h, sysdeps/posix/sysconf.c`

次のファイルではそれらのマクロが #if 行で使われています。 また、上記のファイル自身の中でも使われている場合があります。

`elf/dl-conflict.c, elf/dl-runtime.c, elf/dynamic-link.h`

glibc 2.1.3 の `malloc/malloc.c` には `HAVE_MREMAP` という、やはり置換リストに 'defined' が出てくるマクロがありましたが、glibc 2.4 ではこれは portable な形に直されています。 しかし、他のソースにこの種のマクロが増えてしまいました。

#if 行で置換リスト中に 'defined' が出てくるマクロ呼び出しの結果は規格では undefined であり、これを恣意的に処理するのは GCC の勝手仕様です。 他の処理系でも処理できる portable なソースにするためには、これらのマクロ定義を書き換えることが必要です。 時にはマクロ呼び出しも書き換えなければなりません。*2

この書き換えは多くの場合は、3.9.4.6 にある方法でできます。 しかし、中にはこれでは対応できないものもあります。 `'defined MACRO'` の評価がタイミングによって異なる場合です。 例えば、`sysdeps/i386/dl-machine.h` には次のようなマクロ定義があり、このマクロが他のファイルの #if 式中で使われています。

    #define ELF_MACHINE_NO_RELA defined RTLD_BOOTSTRAP

これをこう書き換えたのでは、うまくいきません。

    #if defined RTLD_BOOTSTRAP
    #define ELF_MACHINE_NO_RELA 1
    #endif

`RTLD_BOOTSTRAP` というマクロは `elf/rtld.c` で定義されますが、これが `dl-machine.h` より先に include される場合と後で include される場合があり、それによって `'defined RTLD_BOOTSTRAP'` の結果が違ってくるのです。 これを portable に書き直すには、`ELF_MACHINE_NO_RELA` というマクロを使うことをやめて、次のような #if 行を

    #if ELF_MACHINE_NO_RELA

次のようにします。 `ELF_MACHINE_NO_RELA` は #if 行でしか使われていない無用なマクロなのです。

    #if defined RTLD_BOOTSTRAP

glibc では実際にこの書き方になっているところも多くあるのですが、それが `ELF_MACHINE_NO_RELA` を使う undefined な書き方と混在しています。

注:

*1 Linux では /usr/include/_G_config.h は glibc の sysdeps/gnu/_G_config.h がインストールされたものなので、ここにも次のような同じマクロ定義がある。

    #define _G_HAVE_ST_BLKSIZE defined (_STATBUF_ST_BLKSIZE)

これはこう書き換えておくべきである。

    #if defined (_STATBUF_ST_BLKSIZE)
    #define _G_HAVE_ST_BLKSIZE 1
    #endif

*2 mcpp V.2.7 からは GCC-specific-build の STD モードに限って、#if 行のマクロに出てくる 'defined' トークンを GCC と同様に処理するようにした。 しかし、こうした bug-to-bug な対処をあてにすべきではない。

#### 3.9.8.7. .S ファイルの「プリプロセス」

*.S ファイルは CPU ごとに用意されているのできわめて多く、1000 本あまりもあります（x86 等、１種類の CPU で使われるのはそのうちの一部）。

*.S ファイルはアセンブラソースに C の #if, #include 等の directive やコメントやマクロを混在させたものですが、アセンブラソースは C の token sequence の形をしていないので、これを C プリプロセッサで処理することには無理があります。 プリプロセッサは C で使わない %, $ 等の文字もそのまま通し、space の有無も原則としてそのまま維持しなければなりません。 さらに C ではエラーになるところをエラーにせずにそのまま通すように、文法チェックを大幅に緩和しなければなりません。 そして、その一方で #if やマクロは処理し、一応のエラーチェックもしなければならないのです。 やっかいなことです。 これらの仕様にはすべて論理的な根拠は何もなく、GCC の local な（多くは undocumented な）仕様を使っているにすぎません。

例えば `nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S` には次のような部分があります

        .byte   8               # Return address register
                                # column.
    #ifdef SHARED
        .uleb128 7              # Augmentation value length.
        .byte   0x9b            # Personality: DW_EH_PE_pcrel
                                # + DW_EH_PE_sdata4

`'#ifdef SHARED'` は C の directive ですが、各行の後半の # で始まる部分はコメントのつもりのようです。 しかし、`'# column.'` のように行の最初の non-white-space-character が # の場合は、無効な directive と構文上、区別がつきません。 `'# + DW_EH_PE_sdata4'` に至っては C では構文エラーになるものです。
他のファイルには次のような例もあります。 ここで '\'' という文字は C では character constant の両端に使われるものなので、これが単独で現れると tokenization のエラーになります。

        movl 12(%esp), %eax     # that `fixup' takes its parameters in regs.

さらに、上記の pthread_cond_wait.S には次のような部分がありますが、これはマクロの呼び出しです。

    versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait,
              GLIBC_2_3_2)

このマクロの定義は次のようになっています。

    # define versioned_symbol(lib, local, symbol, version) \
      versioned_symbol_1 (local, symbol, VERSION_##lib##_##version)
                                        /* include/shlib-compat.h:65    */
    # define versioned_symbol_1(local, symbol, name) \
      default_symbol_version (local, symbol, name)
                                        /* include/shlib-compat.h:67    */
    # define default_symbol_version(real, name, version) \
         _default_symbol_version(real, name, version)
                                        /* include/libc-symbols.h:398   */
    #   define _default_symbol_version(real, name, version) \
         .symver real, name##@##@##version
                                        /* include/libc-symbols.h:411   */
    #define VERSION_libpthread_GLIBC_2_3_2  GLIBC_2.3.2
                                /* make で生成される abi-versions.h:145 */

これによって、このマクロはこういう展開結果になることが期待されています。

    .symver __pthread_cond_wait, pthread_cond_wait@@GLIBC_2.3.2

問題は `_default_symbol_version` の定義です。 '@' という文字は C の token (pp-token) にはありません。 そして、これを ## 演算子で連結した `pthread_cond_wait@@GLIBC_2.3.2` という token もありません。 連結の途中にも illegal な token が生成されます。 このマクロは C の ## 演算子を使っていますが、文法は C とは掛け離れたものです。

アセンブラソースについてプリプロセスのようなことをするのであれば、やはりアセンブラ用のマクロプロセッサを使うべきでしょう。 C プリプロセッサを使うのであれば、*.S ではなく *.c として、アセンブラ用のコードは文字列リテラルに埋め込んで asm() なり __asm__() なりの関数に渡すべきでしょう。 `libc-symbols.h` には次のような別バージョンのマクロも用意されていて、*.c であればこちらが使われます。 こちらは規格準拠の C プリプロセッサで問題なく処理できます。

    #   define _default_symbol_version(real, name, version) \
         __asm__ (".symver " #real "," #name "@@" #version)

glibc には asm() や __asm()__ を使った *.c, *.h ファイルも多くありますが、まだ過半は変則的な *.S ファイルとなっています。

どうしてもアセンブラソースを C プリプロセッサで処理するのであれば、せめてコメント記号としては # ではなく /* */ か // を使うべきです。 実際、glibc でも /* */ か // を使っているソースが多いのですが、# を使っているソースもいくつかあります。

とは言え、glibc 2.4 では *.S ファイルがあまりにも多く、しかも 2.1.3 のころよりも C の文法を無視したソースが多くなっているので、mcpp では V.2.6.3 から lang-asm モードでの文法チェックを大幅に緩和し、これらの変則ソースが処理できるようにしました。

#### 3.9.8.8. versions.awk, rpcgen と -dM オプションの仕様の問題

3.9.4.8 に書いた `stdlib/isomac.c` の問題は変わっていません。

rpcgen の問題も変わっていません。

さらに glibc 2.4 には `scripts/versions.awk` というファイルがありますが、これはプリプロセッサの出力の行頭の space の数について、GCC でしか通用しない仮定を持ち込んでいます。 mcpp や他のプリプロセッサを使うためには、このファイルを次のように修正しなければなりません。

    $ diff -c versions.awk*
    *** versions.awk        2006-12-13 00:59:56.000000000 +0900
    --- versions.awk.orig   2005-03-23 10:46:29.000000000 +0900
    ***************
    *** 50,56 ****
      }

      # This matches the beginning of a new version for the current library.
    ! /^ *[A-Z]/ {
        if (renamed[actlib "::" $1])
          actver = renamed[actlib "::" $1];
        else if (!versions[actlib "::" $1] && $1 != "GLIBC_PRIVATE") {
    --- 50,56 ----
      }

      # This matches the beginning of a new version for the current library.
    ! /^  [A-Za-z_]/ {
        if (renamed[actlib "::" $1])
          actver = renamed[actlib "::" $1];
        else if (!versions[actlib "::" $1] && $1 != "GLIBC_PRIVATE") {
    ***************
    *** 65,71 ****
      # This matches lines with names to be added to the current version in the
      # current library.  This is the only place where we print something to
      # the intermediate file.
    ! /^ *[a-z_]/ {
        sortver=actver
        # Ensure GLIBC_ versions come always first
        sub(/^GLIBC_/," GLIBC_",sortver)
    --- 65,71 ----
      # This matches lines with names to be added to the current version in the
      # current library.  This is the only place where we print something to
      # the intermediate file.
    ! /^   / {
        sortver=actver
        # Ensure GLIBC_ versions come always first
        sub(/^GLIBC_/," GLIBC_",sortver)

#### 3.9.8.9. -include, -isystem, -I- オプション

`-isystem, -I-` オプションは使われていません。

しかし、`-include` オプションは極端に頻繁に使われています。 `include/libc-symbols.h` というヘッダファイルが `-include` オプションによって 7000 回も include されるのです。 このオプションは、本来はソース中に `#include` で書くべき行を makefile に押し出して使うためのものです。 ソースを不完全にするので、決して良いことではありません。

#### 3.9.8.10. Undocumented な事前定義マクロ

これは glibc の問題ではなく、GCC の問題です。 GCC 2 ではいくつかの重要な事前定義マクロが undocumented でしたが、その状態は GCC 3 で解消されました。 しかし、他方で GCC 3.3 からは事前定義マクロが極端に増えたのに伴って、undocumented なものが大幅に増えてしまいました。

#### 3.9.8.11. その他の問題

`debug/tst-chk1.c` には奇妙な部分があり、次のように修正しないと GCC 以外のプリプロセッサでは意図通りに処理されません。

    $ diff -cw tst-chk1.c*
    *** tst-chk1.c  2007-01-11 00:31:45.000000000 +0900
    --- tst-chk1.c.orig     2005-08-23 00:12:34.000000000 +0900
    ***************
    *** 113,119 ****
      static int
      do_test (void)
      {
    -   int   arg;
        struct sigaction sa;
        sa.sa_handler = handler;
        sa.sa_flags = 0;
    --- 113,118 ----
    ***************
    *** 135,146 ****
        struct A { char buf1[9]; char buf2[1]; } a;
        struct wA { wchar_t buf1[9]; wchar_t buf2[1]; } wa;

      #ifdef __USE_FORTIFY_LEVEL
    !   arg = (int) __USE_FORTIFY_LEVEL;
      #else
    !   arg = 0;
      #endif
    !   printf ("Test checking routines at fortify level %d\n", arg);

        /* These ops can be done without runtime checking of object size.  */
        memcpy (buf, "abcdefghij", 10);
    --- 134,146 ----
        struct A { char buf1[9]; char buf2[1]; } a;
        struct wA { wchar_t buf1[9]; wchar_t buf2[1]; } wa;

    +   printf ("Test checking routines at fortify level %d\n",
      #ifdef __USE_FORTIFY_LEVEL
    !         (int) __USE_FORTIFY_LEVEL
      #else
    !         0
      #endif
    !         );

        /* These ops can be done without runtime checking of object size.  */
        memcpy (buf, "abcdefghij", 10);

この元ソースは何の変哲もない書き方に見えますが、実はここでは printf() がマクロとして定義されているのです。 そのため、`#ifdef` 等のディレクティブ行らしきものが通常はマクロの引数として扱われる結果になります。 マクロ呼び出しの中でなければディレクティブ行となるものが引数中に現れた場合の結果は、規格では undefined です。 ディレクティブの処理とマクロ展開は同じ translation phase で行われるので、ディレクティブを先に処理するのは、GCC の勝手仕様です。 そもそも、`#ifdef __USE_FORTIFY_LEVEL` の処理にもマクロの処理が必要なので、この行を先に処理してから printf() マクロを展開するというのは、きわめて恣意的な処理です。 C プリプロセスというものは、頭から sequentially に処理してゆくべきものなのです。

glibc を configure するときにも、GCC の特殊な help message を使う部分があります。 Help に `"-z relro"` というオプションが出てくるかどうかをサーチするものです。 プリプロセッサに mcpp を使っていると、これは期待する結果にはなりません。 しかし、それでも glibc のコンパイルとテストは正常に行われます。

なお、GCC 3.2 までは gcc を起動すると無用な -A オプションがいろいろとデフォルトで付加されましたが、GCC 3.3 からはなくなりました。

#### 3.9.8.12. 深まる GCC 依存

glibc 2.4 を６年前の glibc 2.1.3 と比べると、私がかつて取り上げた portability の問題はほとんど改善されておらず、逆に portability を欠いたソースが増えてきていることがわかります。

改善されたのは、行をまたぐ文字列リテラルがなくなったこと、`-isystem, -I-` オプションが使われなくなったこと、GCC の側で `-A` オプションが使われなくなったことくらいです。

#include_next、GCC2 仕様の可変引数マクロ、中でも可変引数が 0 個のマクロ呼び出し、置換リストに 'defined' のあるマクロ、*.S ファイル、`-include` オプション、これらは大幅に増えました。マクロ呼び出しのカラ引数も増えました。 Portable なソースに書き直すことが簡単にはできない、Standard C と１対１に対応しない書き方が増えたのも、やっかいなところです。

これらの問題はすべて GCC の local な仕様への依存によるものです。 GCC の undocumented な動作に依存している部分も多くあります。 大規模なソフトウェアでそうした部分が発生すると、多くのソースファイルが絡み合うため、修正が困難になり、何年でも同じ書き方が継承されてゆくことになりがちです。 そして、新しいソースもそれに合わせて書かれることになります。 可変引数マクロは GCC2 仕様のものだけで、C99 の仕様のものはおろか GCC3 仕様のものさえ使われていないことは、この関係を端的に表しています。 また、せっかく一部のソースの unportable な部分が書き直されても、他のソースに古い書き方が新たに現れる場合も多く、なかなか整理されません。

逆に GCC のほうも仕様の変更は大きな影響をもたらすので、簡単にはできなくなってしまいます。 どこかで双方の思い切った整理が必要だと思われます。

### 3.9.9. Linux の stddef.h, limits.h, #include_next の問題

Linux では GCC がシステムコンパイラである上にライブラリが glibc であるため、システムヘッダには GCC しか想定していない部分がところどころにあります。 これが compiler-independent-build の mcpp のような他の処理系を使う場合の障害になります。 中でも `stddef.h` 等のいくつかの標準ヘッダファイルが GCC の version-specific な include directory にだけあって `/usr/include` に存在しないというのはお粗末な欠陥であり、mcpp にとっては対策が必要です。 このセクションではこの問題を検討します。

Linux では GCC のバージョンごとに例えば `/usr/lib/gcc-lib/SYSTEM/VERSION/include` といった include directory がシステムに追加されますが、そこにある stddef.h, limits.h 等は奇妙なものです。 CygWIN でも同じです。 Mac OS X にも少し問題があります。

#### 3.9.9.1. /usr/include に標準ヘッダがない

まず、C の標準ヘッダファイルのうち `float.h, iso646.h, stdarg.h, stdbool.h, stddef.h` の５つは Linux ではこの GCC のディレクトリにだけあり、`/usr/include` にも `/usr/local/include` にも存在しません。Linux のシステムヘッダは処理系が GCC であれば GCC 固有の include directory も使い、GCC でなければ `/usr/include` だけを使うように一応書かれているようですが、stddef.h 等がないのでは困ります。

かといって、GCC 以外の処理系で GCC 固有の include directory を使うようにすると、今度はこのディレクトリにある limits.h で GCC 固有の拡張仕様である #include_next にぶつかります。たとえ #include_next を実装しても、今度は limits.h の書き方がおかしいために問題が起こります。ことに GCC V.3.3 以降では limits.h で定義すべき定数を事実上、処理系で事前定義するという乱暴なことをやるようになったため、他の処理系では limits.h が使えないという結果になってしまいます。

また、GCC 自身も、この limits.h の #include_next では不可解な動作をします。

この問題は説明するとややこしいのですが、なぜか何年ものあいだ放置されている問題なので、ここにまとめておきます。

なお、これは compiler-independent-build の mcpp で問題になるものです。 GCC-specific-build では問題は発生しません。

#### 3.9.9.2. #include_next の奇妙な処理

GCC では include directory は通常は次のようになります。

    /usr/local/include
    /usr/lib/gcc-lib/SYSTEM/VERSION/include
    /usr/include

これらのディレクトリが上から下へサーチされます。 この２番目が GCC 固有の include directory です。この SYSTEM は例えば i386-vine-linux, i386-redhat-linux、VERSION は 3.3.2, 3.4.3 等となります。GCC の別のバージョンを `/usr/local` に追加インストールした場合はこの `/usr/lib/gcc-lib` は `/usr/local/lib/gcc` となります。C++ では `/usr/local/include` の前にさらにいくつかのディレクトリが加わります。GCC V.3.*, 4.* では次のものです。

    /usr/include/c++/VERSION
    /usr/include/c++/VERSION/SYSTEM
    /usr/include/c++/VERSION/backward

これらのディレクトリの名前は GCC 固有のものに見えますが、C++ の標準ディレクトリはほかに存在しないので、他の処理系も `/usr/include/c++/VERSION` を使うしかありません。GCC V.2.95 では C++ の include directory は次のものでした。

    /usr/include/g++-3

さらに -I オプションや環境変数で指定されたディレクトリが、このリストの前に追加されます。

以下では説明をわかりやすくするために GCC V.3.3 以降での C の limits.h に話を限ります。中でも `LONG_MAX` の定義を例にとります。limits.h は `/usr/include` と GCC 専用ディレクトリの２個所にあります。

    #include <limits.h>

この行があると GCC は `/usr/lib/gcc-lib/SYSTEM/VERSION/include/limits.h` を include します。すると、このファイルの冒頭は

    #ifndef _GCC_LIMITS_H_
    #define _GCC_LIMITS_H_
    #ifndef _LIBC_LIMITS_H_
    #include "syslimits.h"
    #endif

となっているので、`/usr/lib/gcc-lib/SYSTEM/VERSION/include/syslimits.h` が include されます。このファイルは次のような短いものです。

    #define _GCC_NEXT_LIMITS_H
    #include_next <limits.h>
    #undef _GCC_NEXT_LIMITS_H

さて、ここで limits.h が再び include されますが、#include_next なので `/usr/lib/gcc-lib/SYSTEM/VERSION/include` をスキップして `/usr/include` がサーチされるはずです。GCC の cpp.info には次のように書いてあります。

> This directive works like `#include' except in searching for the specified file: it starts searching the list of header file directories _after_ the directory in which the current file was found.

ところが GCC はなぜか `/usr/include/limits.h` ではなく `/usr/lib/gcc-lib/SYSTEM/VERSION/include/limits.h` をもう一度 include してしまいます。
今度は `_GCC_LIMITS_H_` が定義されている状態なので

    #ifndef _GCC_LIMITS_H_

以下のブロックはスキップされ、次のブロックが評価されます。

    #else
    #ifdef _GCC_NEXT_LIMITS_H
    #include_next <limits.h>
    #endif
    #endif

またしても `/usr/lib/gcc-lib/SYSTEM/VERSION/include/syslimits.h` にあったのとまったく同じ #include_next <limits.h> という行です。また `/usr/lib/gcc-lib/SYSTEM/VERSION/include/limits.h` が、すなわち自分自身が include されるのかと思うと、今度は GCC は `/usr/include/limits.h` を include するのです。GCC の #include_next の動作は一向にわかりません。

`/usr/include/limits.h` では <features.h> 等が include されます。また、次の行で始まるブロックがあります。

    #if !defined __GNUC__ || __GNUC__ < 2

このブロックでは <bits/wordsize.h> を include した上で、wordsize が 32 ビットであるか 64 ビットであるかに応じて規格で要求されている各種定数を定義するようになっています。たとえば 32 ビットであれば `LONG_MAX` は次のように定義されます。

    #define LONG_MAX     2147483647L

しかし、GCC ではこのブロックは当然スキップされます。そして、このファイルが終わり、include 元の `/usr/lib/gcc-lib/SYSTEM/VERSION/include/limits.h` に戻ります。このファイルもこれで２度目の include を終わり、`/usr/lib/gcc-lib/SYSTEM/VERSION/include/syslimits.h` に戻り、このファイルもこれで終わって `/usr/lib/gcc-lib/SYSTEM/VERSION/limits.h` の最初の include に戻ります。このファイルは上記の部分のあとに各種定数の定義があります。`LONG_MAX` については次のようになっています。

    #undef LONG_MAX
    #define LONG_MAX __LONG_MAX__

これでこのファイルも終わります。

    #include <limits.h>

の処理はこれですべて終わりです。 結局、`LONG_MAX` は `__LONG_MAX__` と定義されてオシマイなのです。`__LONG_MAX__` とはいったい何なのでしょうか。実は GCC V.3.3 以降では `__LONG_MAX__` やその他の多くのマクロが事前定義されているのです。32 ビットシステムでは `__LONG_MAX__` は 2147483647L と事前定義されています。`LONG_MAX` 以外の規格で要求されている各種マクロについても、事前定義されたマクロを元に定義されているので、事情は大同小異です。それなら、このややこしいヘッダファイルと #include_next の処理はいったい何のためなのでしょうか？

#include_next の動作は GCC V.2.95.3, V.3.2, V.3.4, V.4.0, V.4.1 でも V.3.3 と同じです。すなわち、

    #include_next <limits.h>

で `/usr/lib/gcc-lib/SYSTEM/VERSION/include/syslimits.h` から `/usr/lib/gcc-lib/SYSTEM/VERSION/include/limits.h` が include され、そこにある同じ

    #include_next <limits.h>

で `/usr/include/limits.h` が include されます。

    #include <limits.h>

では `/usr/lib/gcc-lib/SYSTEM/VERSION/include/limits.h` が２回 include されることになります。このファイルを２回 include しても結果は変りませんが、ムダであり、何より仕様と動作が違っており、動作が一貫していません。このファイルの次のブロックも本来はムダな部分です。

    #else
    #ifdef _GCC_NEXT_LIMITS_H
    #include_next <limits.h>
    #endif

#### 3.9.9.3. GCC でないと標準ヘッダを使えない

では次に GCC 以外の処理系で Linux のシステムヘッダを使うとどうなるでしょうか？ GCC 固有の include directory を使わないと、stddef.h 等が見つかりません。それなら GCC の include directory を使うとどうなるでしょうか？ stddef.h は見つかりますが、今度は limits.h がおかしくなります。

    #include <limits.h>

この行でプリプロセッサは `/usr/lib/gcc-lib/SYSTEM/VERSION/include/limits.h` を include します。そこから `/usr/lib/gcc-lib/SYSTEM/VERSION/include/syslimits.h` が include されます。そして、

    #include_next <limits.h>

でエラーになります。

では、プリプロセッサが #include_next を実装したらどうなるでしょうか？ #include_next が仕様通りに実装されていれば、ここで `/usr/include/limits.h` が include されます。そして、上記の

    #if !defined __GNUC__ || __GNUC__ < 2

以下のブロックが処理され、`LONG_MAX` が次のように定義され、その他のマクロも適切な定数に定義されます。

    #define LONG_MAX     2147483647L

そして、include 元の `/usr/lib/gcc-lib/SYSTEM/VERSION/syslimits.h` に戻り、このファイルが終わって `/usr/lib/gcc-lib/SYSTEM/VERSION/limits.h` に戻ります。すると、何と

    #undef LONG_MAX
    #define LONG_MAX __LONG_MAX__

これでいったん正しく定義されたマクロはすべてご破算にされて `__LONG_MAX__` 等という未定義の名前になってしまうのです！

GCC V.3.2 までなら、これでもまだ

    #define __LONG_MAX__ 2147483647L

という行があったので、 ご破算にされてももう一度、正しく定義し直されました。途中はムダな処理ですが、結果は正しいので使うことができました。しかし、V.3.3 以降のヘッダファイルではすべては徒労に終わります。

#### 3.9.9.4. 当面の対策

以上をまとめると、問題は次の点にあります。*1, *2, *3, *4

1. Linux のシステムヘッダを GCC 専用にしないためには `/usr/include` に `float.h, iso646.h, stdarg.h, stdbool.h, stddef.h` が必要であるが、これがない。
2. C++ の標準 include directory を GCC のバージョン依存にしないためには、`/usr/include/c++/VERSION` ではなく `/usr/include/c++` を使ってもらいたいものである。`/usr/include/c++/VERSION/*` は GCC 固有のものに限るべきである。 FreeBSD, Linux, Mac OS X ではすべて C++ の standard library は GCC の libstdc++ なので、対処しにくいことであるが。
3. GCC の #include_next の動作は仕様と異なっており、かつ一貫性がない。
4. GCC が <limits.h> で定義するマクロを事実上自前で事前定義するのでは、ややこしいヘッダファイルの処理をする意味がない。`/usr/lib/gcc-lib/SYSTEM/VERSION/include/limits.h` ですべてを #undef するのではなおさらムダである。少なくとも Linux や CygWIN では limits.h を２つに分ける必要はないはずである。このディレクトリのヘッダファイルは GCC をインストールする時に自動生成されるものなので、ある程度の冗長性はやむをえないであろうが、それにしてもシステム・ヘッダとしてインストールするには汚すぎる。

これらの問題の根元にあるのはシステムヘッダの構成の過剰な複雑さです。そして、#include_next という拡張ディレクティブが混乱に輪をかけています。このディレクティブの用途はごく限られており、GCC や glibc のコンパイルとインストールで使われているものの、インストールされたシステムヘッダでは limits.h にあるだけです。その limits.h の処理がこう混乱しているのでは、その存在理由が疑われます。

さて、Linux および CygWIN 上の mcpp の compiler-independent 版では以上の問題に対処するため、とりあえずは次のように設定する必要があります。Compiler-independent 版では混乱を避けるため、#include_next は実装しません。GCC 固有の include directory も組み込みません。

1. `/usr/include/stddef.h` を `/usr/lib/gcc-lib/SYSTEM/VERSION/include/stddef.h` へのリンクとして作成しておく。複数の GCC のバージョンがインストールされている場合もそのどれかにリンクしておけば、Linux, CygWIN 上で compiler-independent で使う分には問題ないと思われる。これは GCC にも、GCC 専用版の mcpp にも悪影響は与えない。stdarg.h も同様である。マクロが GCC の組み込み関数に展開されるが、単にプリプロセスするだけならそれでもすむ。
2. iso646.h, stdbool.h はごく簡単なものであり、処理系やシステムに依存するものではないので、GCC のどれかのバージョンのものを `/usr/include` にコピーするか移動すればすむ。limits.h は GCC 以外の処理系では `/usr/include` のものだけで十分である。
3. float.h は `DBL_MAX_EXP` が `__DBL_MAX_EXP__` に定義されるといった内容なので、他のプリプロセッサには役に立たない。必要なら GCC の内部設定などを参考に書く。*5
4. GCC 固有の C の include directory は環境変数では設定しない。
5. 環境変数 CPLUS_INCLUDE で `/usr/include/c++/VERSION:/usr/include/c++/VERSION/SYSTEM:/usr/include/c++/VERSION/backward` を C++ の include directory として設定する。

GCC 専用版の mcpp では GCC 固有の include directory を組み込み、#include_next も仕様通りに実装し、互換のための事前定義マクロも定義するので、特別な設定は必要ありません。

注:

*1 この 3.9.9 の記載は Linux / GCC 2.95.3, 3.2, 3.3.2, 3.4.3, 4.0.2, 4.1.1, 4.3.0 および CygWIN / GCC 2.95.3, 3.4.4 で確認したものである。 CygWIN / GCC 2.95.3 では #include_next の動作は仕様通りであったが、3.4.4 では Linux と同じになった。 また、CygWIN の C++ の include directory は 2.95.3 では `/usr/include/g++-3` であったが、3.4.4 では `/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++` 以下となっている。

*2 FreeBSD 6.2, 6.3 / GCC 3.4.6 では `/usr/include` に C のすべての標準ヘッダがあり、GCC 固有の include directory も C では存在しない。 #include_next もシステムヘッダには無い。 ただ、C++ の include directory は `/usr/include/c++/3.4, /usr/include/c++/3.4/backward` と GCC のバージョン依存となっている。
FreeBSD でも別バージョンの GCC をインストールすると GCC-version-specific な include directory が作られる。 そこにできるヘッダファイルの多くはムダなものである。 しかし、`/usr/include` のヘッダファイルが書き換えられることはない。

*3 Mac OS X Leopard / Apple-GCC 4.0.1 では GCC の version-specific な include directory は Linux と同様である。 #include_next も `limits.h` 等で使われている。 `limits.h` の構成も Linux と同様であるが、ただし `syslimits.h` の #include_next は削除されている。 また、`float.h, iso646.h, stdarg.h, stdbool.h, stddef.h` はすべて `/usr/include` にあるので、mcpp の側での対策はあまり必要ない。 しかし、`float.h, stdarg.h` は GCC 用と Metrowerks (for powerpc) 用なので、mcpp で使うなら `stdarg.h` は GCC の version-specific なディレクトリ中のヘッダを include するようにディレクティブを書き足し、`float.h` ではマクロを書き足しておかなければならない。 `float.h` は x86 と powerpc とでいくつかの値が異なることに注意。

*4 MinGW / GCC 3.4.* では include directories とその優先順位が他のシステムと異なるが、GCC の #include_next の動作は同じであり、また標準の include directory である `/mingw/include` にいくつかの標準ヘッダがなくて version-specific-directory にある点も CygWIN 等と同じである。

*5 GCC の設定を参照して i386 版の float.h を書けば、次のようになる。

    /* float.h  */

    #ifndef _FLOAT_H___
    #define _FLOAT_H___

    #define FLT_ROUNDS      1
    #define FLT_RADIX       2

    #define FLT_MANT_DIG    24
    #define DBL_MANT_DIG    53
    #define LDBL_MANT_DIG   64

    #define FLT_DIG         6
    #define DBL_DIG         15
    #define LDBL_DIG        18

    #define FLT_MIN_EXP     (-125)
    #define DBL_MIN_EXP     (-1021)
    #define LDBL_MIN_EXP    (-16381)

    #define FLT_MIN_10_EXP  (-37)
    #define DBL_MIN_10_EXP  (-307)
    #define LDBL_MIN_10_EXP (-4931)

    #define FLT_MAX_EXP     128
    #define DBL_MAX_EXP     1024
    #define LDBL_MAX_EXP    16384

    #define FLT_MAX_10_EXP  38
    #define DBL_MAX_10_EXP  308
    #define LDBL_MAX_10_EXP 4932

    #define FLT_MAX         3.40282347e+38F
    #define DBL_MAX         1.7976931348623157e+308
    #define LDBL_MAX        1.18973149535723176502e+4932L

    #define FLT_EPSILON     1.19209290e-7F
    #define DBL_EPSILON     2.2204460492503131e-16
    #define LDBL_EPSILON    1.08420217248550443401e-19L

    #define FLT_MIN         1.17549435e-38F
    #define DBL_MIN         2.2250738585072014e-308
    #define LDBL_MIN        3.36210314311209350626e-4932L

    #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
    #define FLT_EVAL_METHOD 2
    #define DECIMAL_DIG     21
    #endif /* C99 */

    #endif /* _FLOAT_H___ */

### 3.9.10. Mac OS X / Apple-GCC とシステムヘッダの問題

mcpp は V.2.7 から Mac OS X / GCC もサポートするようになりましたが、このシステムの問題点を以下にまとめておきます。 ただ、筆者はまだこのシステムではほとんど mcpp と firefox くらいしかコンパイルしていないので、知らない部分が多くあります。 ことに Objective C / Objective C++ についてはまったく知りません。

この OS は GCC がほとんど唯一のコンパイラとなっているため、そのシステムヘッダには GCC-local な仕様への依存が一部に見られます。 ライブラリが glibc ではないせいか、その程度は Linux ほどではありません。 しかし、FreeBSD よりは多いようです。 もう少し整理できるはずだと思われます。

このシステムのもう１つの特徴は Apple によってかなり手を加えられた GCC がシステムコンパイラとなっていることです。 Mac OS X のシステムヘッダや Apple のソースでは、一般の GCC-local な仕様よりもむしろ Apple-GCC-local な仕様への依存のほうが目立ちます。 中でも、Intel-Mac と PowerPC-Mac の双方を１つのマシンでサポートするための拡張仕様が特徴的です。

なお、ここで取り上げるのは Mac OS X Leopard / Apple-GCC 4.0.1 です。

#### 3.9.10.1. #include_next, #warning

GCC-local な仕様である #include_next は多くはありませんが、`/usr/include/` の `float.h, stdarg.h, varargs.h` および `/Developer/SDKs/MacOSX10.*.sdk/usr/include/` の同名のファイルで使われています。 それらはいずれもコンパイラが GCC であるか Metrowerks であるかによって include されるヘッダファイルを振り分けるためのものです。 GCC だと `stdarg.h` 等の同名のヘッダファイルが #include_next されるようになっています。
また、Linux と同様に、GCC の version-specific な include directory では `limits.h` に #include_next がありますが、`syslimits.h` のものは削除されて、少しだけ整理されています。

このディレクティブは控え目に使われていますが、`float.h, stdarg.h` が GCC と Metrowerks しか想定していないのは問題です。 FreeBSD と同じところまで portable に書くことはできるはずです。*1
また、`/usr/include` 内のヘッダファイルで GCC 用に #include_next を書くのはナンセンスです。 GCC ではこのディレクトリのほうが version-specific な include directory より優先順位が低いからです。 したがって、この #include_next は決して実行されることはありません。

#warning は `/usr/include/` の `objc/, wx-2.8/wx/` 等のディレクトリやそれに対応する `/Developer/SDKs/MacOSX*.sdk/usr/include/` 内のディレクトリに時々見られます。 多くは obsolete ないし deprecated な使い方やファイルに関するウォーニングのようです。
`/usr/include/c++/VERSION/backward/` およびそれに対応する `/Developer/SDKs/MacOSX*.sdk/` 内の `backward_warning.h` はやはり deprecated なヘッダを使うなという #warning を実行するためのファイルですが、このディレクトリ内のヘッダファイルはすべてこのファイルを include するようになっています。 これは Linux や FreeBSD と同じです。

注:

*1 mcpp の compiler-independent-build でこれらのヘッダを使う方法については、3.9.9.4 およびその注 3 を参照。

#### 3.9.10.2. 'defined' に展開されるマクロ

`/usr/include/sys/cdefs.h` およびそれに対応する `/Developer/SDKs/MacOSX*.sdk/` 内の同名のヘッダファイルには、次のようなマクロ定義があります。

    #define __DARWIN_NO_LONG_LONG   (defined(__STRICT_ANSI__) \
                    && (__STDC_VERSION__-0 < 199901L) \
                    && !defined(__GNUG__))

そして、このマクロが `stdlib.h` 等で次のように使われています。

    #if __DARWIN_NO_LONG_LONG

このマクロは次のように定義すべきです。*1

    #if     defined(__STRICT_ANSI__) \
                    && (__STDC_VERSION__-0 < 199901L) \
                    && !defined(__GNUG__)
    #define __DARWIN_NO_LONG_LONG   1
    #endif

注:

*1 その理由は 3.9.4.6, 3.9.8.6 を参照。

#### 3.9.10.3. #endif 行のトークン

`/System/Library/Frameworks/Kerberos.framework/Headers` の `gssapi.h, krb5.h, profile.h` には次のような奇怪な #endif 行があります。

    #endif \* __KERBEROS5__ */

この `\* __KERBEROS5__ */` というのはコメントのようですが、なぜわざわざこんな書き方を発明するのか、理解に苦しみます。 他のシステムの GCC ではこれにはウォーニングが出ますが、Apple-GCC ではたとえ `-pedantic` 等のオプションを付けてもウォーニングは出ません。 Apple-GCC では次の書き方についてもウォーニングが出ません。 C90 以前の感覚をいまだにひきずっています。

    #endif __KERBEROS5__

#### 3.9.10.4. マクロの諸問題

次のような特殊なマクロの使い方は、glibc のソースや Linux のシステムヘッダなどでしばしば見られるものですが、いずれも firefox 3.0b3pre のソースをコンパイルした限りでは、そこから include される Mac OS X のシステムヘッダ中には見られません。

- GCC 仕様の可変引数マクロ
  C99 の仕様の可変引数マクロはシステムヘッダでは稀に使われていますが、GCC 仕様のものは見られません。
- マクロ呼び出しのカラ引数
- Function-like マクロの名前に置換される object-like マクロ

#### 3.9.10.5. Apple-GCC の独自仕様

Apple-GCC には一般の GCC と異なる独自の仕様がいくつかあります。

- Intel-Mac と PowerPC-Mac の双方のバイナリを１つのマシンで生成するためのもの

  Mac OS X には x86 用と ppc 用の双方のコンパイラがインストールされます（片方はネイティブコンパイラ、他方はクロスコンパイラ）。 `-arch` という独自のオプションがあり、`-arch i386 -arch ppc` というふうに複数の CPU を指定すると、gcc がその回数だけ呼び出されて、それぞれの CPU 用のバイナリが生成され、さらにそれらを１つにバンドルした universal binary というものが生成されます。 また、`-mmacosx-version-min=` というオプションもあり、これを `-isysroot` や `--sysroot` オプションと合わせて使うことで、バイナリの互換性の範囲を Mac OS X のある程度の旧バージョンにまで広げることができるようになっています。 これらの拡張仕様は Mac OS X 用のバイナリ・パッケージを作る場合などに便利です。

  プリプロセスに関しては、CPU によって一部の事前定義マクロが違うことに注意が必要です。

- "framework" ディレクトリ

  Mac OS には NeXTstep 以来の framework ディレクトリというものがあります。 framework はヘッダファイル・ライブラリ・ドキュメント・その他の共有リソースを１つにまとめたものです。 ここにあるヘッダファイルを include するには次のようなディレクティブを使います。

      #include <Kerberos/Kerberos.h>

  これは次のディレクティブと同じ形をしています。

      #include <sys/stat.h>

  このディレクティブで include されるのはどれかの include directory（この場合は `/usr/include`）の中の `sys/stat.h` というファイルですが、`<Kerberos/Kerberos.h>` のほうはそうではありません。 `Kerberos` はディレクトリ名でさえもありません。 こちらは `/System/Library/Frameworks` という framework directory の中の `Kerberos.framework/Headers/Kerberos.h` というファイルなのです。 そして、`Kerberos.framework/Headers` は `Kerberos.framework/Versions/Current/Headers` への symbolic-link となっています。 これは最も単純な例で、framework header のありかはもっとずっと複雑なものも多くあります。

  いったいだれがこんな複雑なシステムを考えたのでしょうか。 プリプロセッサは path-list をあれこれ組み立てながらシステムヘッダをサーチしなけらばならないので、かなりの負荷がかかります。 ヘッダによってはそこからさらに多くの他のヘッダを include するものもあります。

- "header map" ファイル

  Mac OS X の IDE である Xcode.app では "header map file" というものが使われます。 これは include されるヘッダファイルをあらかじめサーチして、その path-list を *.hmap という名前のファイルに記録しておき、プリプロセスではそれを include directory のように参照するというものです。 header map file を生成するのは Xcode.app のツールですが、それを include directory のように読むのは Apple-GCC の拡張仕様です。

  framework directory 中のヘッダファイルのサーチは負荷が大きいので、それを軽減するために考えられたものでしょうか。 しかし、header map file は独自仕様のバイナリファイルであり、透明性を欠いています。 むしろ framework directory のほうを整理して単純明快にしてもらいたいものです。

- #endif 行のトークン

  前節で見たように、#endif 行に何があろうとどんなオプションが指定されようと、Apple-GCC はウォーニングさえ出しません。 時代錯誤の感覚です。

- コメント中の非 ASCII 文字

  これは GCC の問題ではありませんが、framework header ではコメント中に ISO-8859-* (?) の copyright mark (0xA9) 等の非 ASCII 文字がしばしば使われています。 これはコメント中とは言え、multibyte character を使う環境では迷惑です。 文字の encoding に関する意識を少しは持ってもらいたいものです。 この種の文字は Linux の /usr/include 等でも時に見られますが、Mac OS の framework header には特に多いようです。

### 3.9.11. firefox 3.0b3pre ソースのプリプロセス

firefox の developing version である 3.0b3pre (2008/01) すなわち 3.0-beta3-prerelease のソースを、Linux/x86 + GCC 4.1.2 および Max OS X + GCC 4.0.1 でプリプロセッサを mcpp V.2.7 に置き換えてコンパイルしてみました。 mcpp は -K オプションのテストを兼ねて、すべて -Kv オプションを付けて実行し、その出力を cc1 (cc1plus) に渡しています。 その結果、コンパイルが正常に終了し、firefox のバイナリが生成されるのを確かめることができました。*1

firefox のソースのプリプロセス上の portability は全体としてはかなり高いものです。 glibc のソースによく見られるような GCC-local な仕様への依存はあまり見られません。 Linux と Mac OS X の GCC、Windows の Visual C を公式なターゲットとしているだけのことはあります。

とは言え、プリプロセスの portability の問題は GCC と Visual C さえ通ればすむわけでもありません。 以下に具体的に検討してみます。 なお、GCC の問題については重複を避けるため、ここでは説明しません。 3.9.4, 3.9.8 等を参照してください。 firefox と対比するため glibc をしばしば引き合いに出していますが、glibc についてのコメントもこれらのセクションにあります。*2

注:

*1 ソースは mozilla.org の CVS リポジトリから checkout したものである。 これをコンパイルした１つの動機は mcpp の -K オプションをテストするためであった。 このオプションは Taras Glek の提案によるものであるが、彼は mozilla project で C/C++ ソースの refactoring に取り組んでいるので、mcpp の作者も firefox のソースをこのオプションのテストに使ったのである。 -K (-Kv) オプションについては 2.4 を参照。

*2 firefox のコーディング・ガイドラインは次のところにある。 しかし、内容はかなり古いものである。
portable-cpp

#### 3.9.11.1. GCC-local な仕様はほとんど使われない

glibc のソースで時々使われている次のような GCC-local な仕様は、firefox のソースには見られません。 ただし、Linux 上でコンパイルする場合は、システムヘッダに GCC2 仕様の可変引数マクロなどが出てきますが、それは別です。

- GCC 仕様の可変引数マクロ
  それどころか C99 仕様の可変引数マクロさえもまだ使われていません。

次のものは glibc でも最近のバージョンでは使われなくなっているものですが、firefox でも使われていません。

- 行をまたぐ文字列リテラル
- #warning
- -isystem, -I- オプション

#### 3.9.11.2. #include_next

しかし、`#include_next` だけは１つのディレクトリに限ってたくさんあります。 これは configure によって生成される `config/system_wrappers` というディレクトリです。 この中に生成されるのはすべて同じ形をした約 900 本のヘッダファイルです。 例えば `stdio.h` は次のようになっています。

    #pragma GCC system_header
    #pragma GCC visibility push(default)
    #include_next <stdio.h>
    #pragma GCC visibility pop

これは GCC 4.* の `#pragma GCC visibility *` の機能を利用するためのものです。 一方で、`config/gcc_hidden.h` というファイルがあります。 これは次のようなもので、これが多くの translation unit で -include オプションによって冒頭に読み込まれます。

    #pragma GCC visibility push(hidden)

system_wrappers ディレクトリは最優先の include ディレクトリでなければならないので、常に最初の -I オプションで指定しなければならないというのが注意すべき点ですが、この #include_next の使い方は簡明であり、特に問題はないと思われます。

ただ、`nsprpub` ディレクトリでは -include gcc_hidden.h の代わりに -fvisibility=hidden オプションが指定される場合がしばしばありますが、その場合は system_wrappers のヘッダファイルは使われません。 このディレクトリはまだ整理されていないようです。

#### 3.9.11.3. C99 を指定せずに C99 の仕様を使う

目立つのは C99 を指定せずに C99 の仕様を使うものです。 GCC ではソースファイル名が *.c だとデフォルトでは gnu89 という仕様が使われますが、これは C90 にいくつかの C99 仕様と GCC 独自の仕様を加えた折衷的なものです。 firefox のソースには、これを利用して次のような C99 の仕様を暗黙のうちに使っているものがあります。

- マクロ呼び出しのカラ引数

  Linux のシステムヘッダを別とすれば、firefox 自身のソースでのカラ引数はわずかですが、まず次の３本のファイルにあります。 カラ引数が実際に使われているのは `NS_ENSURE_TRUE` という１つのマクロだけです。

  `layout/style/nsHTMLStyleSheet.cpp, layout/generic/nsObjectFrame.cpp, intl/uconv/src/nsGREResProperties.cpp`

  また、`gfx/cairo/cairo/src` の次のファイルにもあります。 これも実際には `slim_hidden_ulp2` という１つのマクロだけです。

  `cairoint.h, cairo-font-face.c, cairo-font-options.c, cairo-ft-font.c, cairo-ft-private.h, cairo-image-surface.c, cairo-matrix.c, cairo-matrix.c, cairo-pattern.c, cairo-scaled-font.c, cairo-surface.c, cairo-xlib-surface.c, cairo.c`

  これらのカラ引数は Linux では使われますが、Mac OS X では使われません。 いずれにしても、特に紛らわしいものではありません。

- C90 の規定を超える translation limits

  identifier の長さ、#include のネスト、マクロ定義の数などが C90 の最低保証限度を超える場合がしばしばあります。
  31 バイトを超える長い identifier はことに `gfx/cairo/cairo/src/` に多く見られます。 #include のネストが 8 レベルを超えたり、マクロ定義が 1024 個を超える場合もありますが、Linux や Mac OS X ではシステムヘッダだけでこの現象がしばしば起こるのでやむをえないでしょう。

- C のソースであるにもかかわらず `//` コメントを使っているもの

  ときどき見られます。 これは上記の ガイドライン が禁止しているものですが、いまはほとんど問題はないでしょう。

これらの仕様は Visual C 2005, 2008 でも使えます。 GCC では -std=c99 というオプションがあるので、それを使いたいところです。 しかし、Visual C には規格のバージョンを指定するオプションはないので、暗黙のうちに使うしかありません。 したがって、暗黙のうちに C99 の仕様を使うことについては、主要な処理系の現状では firefox のソースを責めることはできません。*1

ところで、暗黙のうちに C99 仕様を使っていながら、可変引数マクロはなぜか使われていません。 Visual C でも 2005 で使えるようになったのですが、2003 までは使えなかったので、避けていたのでしょうか。

注:

*1 C++ では GCC は "gnu++98" という仕様がデフォルトである。 これは C++98 + GCC extensions とされているが、実際には C99 の仕様が混在している。 一方、Visual C は C では C90、C++ では C++98 に準拠とされているが、実際にはどちらも C99 仕様と独自の仕様が混在しており、Visual C 2005, 2008 ではことにそうである。 すなわち、GCC も Visual C も諸規格と独自仕様が混在しているのである。 とりわけ Visual C は規格のバージョンを指定できないのが困るところである。

#### 3.9.11.4. Function-like マクロの名前に置換される object-like マクロ

Function-like マクロの名前に置換される object-like マクロの定義は他のプログラムにもよく見られるものです。 firefox のソースにもあまり多くはありませんが、次のところに見られます。

`content/base/src/nsTextFragment.h, modules/libimg/png/mozpngconf.h, modules/libjar/zipstub.h, modules/libpr0n/src/imgLoader.h, nsprpub/pr/include/obsolete/protypes.h, nsprpub/pr/include/private/primpl.h, nsprpub/pr/include/prtypes.h, parser/expat/lib/xmlparse.c, security/nss/lib/jar/jarver.c security/nss/lib/util/secport.h, xpcom/glue/nsISupportsImpl.h`

また、firefox を make することで多くのヘッダファイルへのリンクが開発環境用のディレクトリに作られ、それらは firefox の開発環境をインストールすると /usr/include/firefox-VERSION/ に入っていきますが、その中には上記のファイルへのシンボリックリンクがいくつかあります。 configure によって生成される `mozilla-config.h` にもこの種のマクロ定義があります。

これらはやはり function-like マクロとして書いたほうが、ソースがわかりやすくなります。 実際、firefox のソースにもその書き方は多くあります。 人によって書き方が異なるのでしょうが、これについてはコーディング・ガイドラインを決めたほうが良さそうです。

#### 3.9.11.5. 'defined' に展開されるマクロ

glibc で時に見られる置換リスト中に 'defined' というトークンの出てくるマクロが、firefox にも１つだけあります。

`modules/oji/src/nsJVMConfigManagerUnix.cpp` というファイルでは次のようなマクロを定義して、

    #define NS_COMPILER_GNUC3 defined(__GXX_ABI_VERSION) && \
                              (__GXX_ABI_VERSION >= 102) /* G++ V3 ABI */

自分自身の中で次のように使っています。

    #if (NS_COMPILER_GNUC3)

このマクロは廃止して、#if 行は次のように改めるべきです。

    #if defined(__GXX_ABI_VERSION) && (__GXX_ABI_VERSION >= 102) /* G++ V3 ABI */

このファイルが GCC でしかコンパイルされないものだとは言え、処理系の誤仕様を利用するのは良いことではありません。*1

注:

*1 mcpp も V.2.7 からは GCC-specific-build では #if 行のマクロに出てくる 'defined' を GCC と同様に処理するようにした。 しかし、ウォーニングが出るので、ソースを修正したほうが良い。

#### 3.9.11.6. #endif のうしろの token sequences

`jpeg` ディレクトリの中の次のファイルには、何と #endif 行にコメントがコメントマークなしに書かれている行があります。 いずれも最近のアプデートで発生したものです。

    jmorecfg.h, jconfig.h, jdapimin.c, jdcolor.c, jdmaster.c

この書き方は 1990 年代の中ごろまでは UNIX 系のソフトウェアに見られましたが、いまではほぼ消失しているものです。 かの glibc の世界でさえも使われていません。 さすがに GCC もこれにはウォーニングを出します。 しかし、これらのソースはそんなことにはおかまいなしのようです。 Apple-GCC はウォーニングを出しませんが、これは Mac OS 上でエディットされたものなのでしょうか。

#### 3.9.11.7. プリプロセスを要するアセンブラソース

アセンブラソースは *.s (*.asm) という名前になっていて、中にはマクロが使われているものもありますが、プリプロセッサが呼び出されることは原則としてありません。

しかし、Mac OS X / ppc では１つだけ例外があり、`xpcom/reflect/xptcall/src/md/unix/xptcinvoke_asm_ppc_rhapsody.s` でプリプロセッサが呼び出されます。 このファイルにはたった１行の #if ブロックが１つあるからですが、今となっては必要ないと思われるブロックです。

#### 3.9.11.8. -include オプション

firefox のコンパイルでは configure で mozilla-config.h というヘッダファイルが生成されますが、大半のソースのコンパイルでこのヘッダを -include オプションで指定しています。 config/gcc_hidden.h についても同様です。 なぜこれらをソースの冒頭で #include しないのでしょうか？

#### 3.9.11.9. マクロの再定義

マクロが黙って再定義されることが稀にあります。

- firefox の make では多くの場合 -DZLIB_INTERNAL というオプションが付加されて gcc (c++) が呼び出されます。 すなわち、`ZLIB_INTERNAL` は 1 に定義されます。 ところがこのマクロは `modules/zlib/src/` の何本かのソースで

      #define ZLIB_INTERNAL

  と 0 個のトークンに定義されているのです。 そして、次のように使われています。

      #    ifdef ZLIB_INTERNAL

  この場合は実害はありませんが、マクロ定義が違っているのは感心しません。 たぶん Makefile のオプションのほうが余計なのでしょう。

- `xpcom/build/nsXPCOMPrivate.h` では `MAXPATHLEN` というマクロの定義が `/usr/include/sys/param.h` と異なるという問題があります。 これは関連するヘッダファイルによって `/usr/include/sys/param.h` を include したりしなかったり一定していないためです。 ヘッダファイルの整理が必要です。

- Mac OS X では `netwerk/dns/src/nsIDNKitInterface.h` で `/usr/include/assert.h` で定義された `assert` マクロが再定義されます。 これは `#undef assert` を先に書くべきです。

- Mac OS X では `modules/libreg/src/VerReg.c` で `VR_FILE_SEP` というマクロの奇妙な二重定義が起こります。

      #if defined(XP_MAC) || defined(XP_MACOSX)
      #define VR_FILE_SEP ':'
      #endif
      #ifdef XP_UNIX
      #define VR_FILE_SEP '/'
      #endif

  Mac OS X では configure で `XP_MACOSX` と `XP_UNIX` の双方が定義されるからです。 意図的な二重定義のように見えますが、紛らわしいので、`XP_UNIX` が優先することがわかるように次のように書いたほうが良いでしょう。

      #ifdef XP_UNIX
      #define VR_FILE_SEP '/'
      #elif defined(XP_MAC) || defined(XP_MACOSX)
      #define VR_FILE_SEP ':'
      #endif

#### 3.9.11.10. 長大なコメント

次のファイルには数百行以上におよぶ長大なコメントがあります。

`extensions/universalchardet/src/base/Big5Freq.tab, extensions/universalchardet/src/base/EUCKRFreq.tab,intl/unicharutil/src/ignorables_abjadpoints.x-ccmap, layout/generic/punct_marks.ccmap`

ことに `intl/uconv/ucv*/` というディレクトリには長大なコメントを持つファイルが多数あります。 中には１つのコメントが 8000 行を超えるものまであります！ それらはすべて *.uf, *.ut という名前になっています。 いずれも unicode とアジアの各種 encoding との間の mapping table のようで、ツールによって自動生成されるものです。 C/C++ のソースには見えませんが、C++ のソースから include されます。 コメントはある種のドキュメントか他のツールのための表のようです。

長大なドキュメントや表をコメントとしてソース中に含めるのは、いかがなものでしょうか。 ソースツリーに含めるとしても、ソースとは別のファイルに分離すべきでしょう。

以上のファイルはいずれも Linux では使われますが、Mac OS X では使われません。 他方で Mac OS X では framework ディレクトリ中のシステムヘッダがしばしば使われますが、その中にはコメントが大半を占めている奇妙なファイルが時々見られます。

#### 3.9.11.11. 改行コードの混在

firefox のソースの改行コードは [LF] ですが、[CR][LF] の行が少し混ざっているファイルが数本あります。 これらはいずれも短いブロックで、ソースにパッチを挿入する時に、その部分だけ [CR][LF] になってしまったもののようです。 ソースを Windows 上で編集する時は、改行コード変換ツールでチェックすべきでしょう。

## 3.10. Visual C++ のシステムヘッダの問題

Visual C++ 2003, 2005, 2008 でいくつかのサンプルプログラムのプリプロセスに mcpp を使ってみました。このシステムのシステムヘッダには、プリプロセス上の互換性が問題となるようなものはごく少ないようです。次のようなものはありますが、これらは他の処理系でもしばしば見られるもので、特に問題となるものではありません。

1. C99 の仕様はほとんど実装されていなかったころから、C で // コメントが多用されてきた。
2. Function-like マクロの名前に展開される object-like マクロの定義が時々見られる。
3. Visual C++ 2003 では limits.h に間違ったマクロ定義が１つあった （Visual C++ 2005 では直された。 cpp-test.html#5.1.3.1 の注2 を参照）。

Linux のシステムヘッダや glibc には GCC local な仕様がしばしば使われていますが、Visual C++ のシステムヘッダには Visual C++ local な書き方はあまり見られません。

### 3.10.1. コメントを生成するマクロ？

しかし、Visual C++ には１つだけとんでもないマクロがあります。Visual C++ 2003 の `Vc7/PlatformSDK/Include/wtypes.h` には次のようなマクロ定義があります。*1

    #define _VARIANT_BOOL   /##/

そして、`Vc7/PlatformSDK/Include/` の `oaidl.h, propidl.h` で次のように使われています。

    _VARIANT_BOOL bool;

これはいったい何でしょうか？
これは `_VARIANT_BOOL` が // に展開されて、その結果、この行がコメントアウトされることを期待しているもののようです。そして、実際に Visual C の cl.exe ではそうなってしまいます！

しかし、// はトークン (preprocessing-token) ではありません。また、マクロの定義や展開は、ソースがトークンに分解されコメントが１個のスペースに変換されたあとのフェーズで処理されるものです。したがって、マクロによってコメントを生成することは決してできないのです。このマクロは // に展開されたところで、// は有効な preprocessing-token ではないので結果は undefined となるはずのものです。

mcpp でこれらのヘッダファイルを使うためには、このマクロ定義をコメントアウトし、数ヵ所ある `_VARIANT_BOOL` 云々のところを次のように書き換えなければなりません。

    #if !__STDC__ && (_MSC_VER <= 1000)
        _VARIANT_BOOL bool;
    #endif

Visual C 5.0 以降のバージョンしか使わないのであれば、この行は次のように本当にコメントアウトしてかまいません。

    // _VARIANT_BOOL bool;

このマクロは論外ですが、それ以上に問題なのは、これをコメントとして処理してしまう Visual C / cl.exe のプリプロセスの実装です。この例には、このプリプロセッサの次のような深刻な問題が露呈しています。

1. 少なくともこの例では Token-base ではなく文字ベースのプリプロセスがされている。
2. マクロの展開結果がコメントとして扱われており、translation phases が混乱している。

おそらく、cl.exe のプリプロセッサは非常に古い、どちらかと言えば文字ベースのプリプロセッサのソースを元にしているのでしょう。それに部分的に手を加えながらバージョンアップを繰り返してきていることが推測されます。

こうした非常に古いプログラム構造を持っていると推測されるプリプロセッサは多くあります。3.9 で見た GCC 2 / cpp もその１つです。こうしたプリプロセッサでは、部分的に手を加えれば加えるほどプログラム構造がゴチャゴチャしてくるので、いくら改良してもあるところで品質は頭打ちとなります。古いソースを捨てて、初めから書き直さない限り、すっきりしたプリプロセッサにはならないと思われます。

GCC 3 / cpp ではソースが新しく書き直されて、GCC 2 とは別のプリプロセッサとなりました。mcpp も、DECUS cpp という古いプリプロセッサのソースから出発しながら、出発してまもなく全面的に書き直されたものです。

注:

*1 Visual C++ 2005 express edition には Platform SDK は含まれていないが、"Platform SDK for Windows 2003" をダウンロードして使うことができる。その `PlatformSDK/Include` ディレクトリの `wtypes.h, oaidl.h, propidl.h` でも、このマクロは同じである。
Visual C++ 2008 express edition の `Microsoft SDKs/Windows/v6.0A/Include` ディレクトリの同名のヘッダファイルでも同様である。

### 3.10.2. Identifier 中の '$'

もう一つの問題は、Visual C++ 2008 のシステムヘッダではマクロ名に '$' を使うケースが突然増えたことです。 これは Visual C++ 2005 までにもなくはありませんでしたが、例外的なものにとどまっていました。 しかし、2008 ではあちこちに出てきています。

最も目立つのは `Microsoft Visual Studio 9.0/VC/include/sal.h` というヘッダです。 これは Visual C++ の SAL (standard source code annotation language) なるものをソース中に記述するためのマクロを定義しているものです。 ここでは '$' を含む名前が大量に使われています。 このヘッダは多くの標準ヘッダファイルから `Microsoft Visual Studio 9.0/VC/include/crtdefs.h` というヘッダを介して #include されます。 したがって、多くのソースのコンパイルではこれらのマクロが知らないうちに使われることになります。

-Za オプションを付けて cl（コンパイラ）を起動すると、SAL は無効になり、sal.h の '$' を含む名前はすべて消えるようにはなっていますが、なぜこんな名前が必要なのか理解に苦しみます。 GCC でも identifier に '$' が使えるのがデフォルトになっていますが、実際には使われている例はほとんど見掛けなくなっています。

`Microsoft SDKs/Windows/v6.0A/Include` ディレクトリにもこの種の名前を持つヘッダファイルがあります。 `specstrings*.h` というヘッダです。 これらのヘッダは WinDef.h を介して Windows.h から include されますが、こちらは -Za オプションを指定しても '$' が消えるようには書かれておらず、単にエラーになるだけです。 したがって、Windows.h を include するソースは -Za オプションでコンパイルすることはできません。

# 4. 処理系定義の仕様

Ｃ言語のプリプロセス仕様を逐一ここに書くわけにはゆきません。cpp-test.html に Standard C のプリプロセスについて詳しい解説を書いてあるので、そちらを読んでください。mcpp の各種モードの動作仕様については、 2.1 を見てください。ここでは Standard C で処理系定義とされているものを含めて、プリプロセスの周辺のいくつかの仕様を述べます。さらにこまかな処理系定義仕様については、5 診断メッセージに書いてあります。

## 4.1. 終了時の status 値

mcpp 終了時に親プロセスに返す値は internal.H というヘッダで定義されています。エラーがなかった場合は 0 を返し、エラーがあった場合は errno != 0 なら errno を errno == 0 なら 1 を返します。

## 4.2. Include directory のサーチパス

#include directive で include するファイルは次の順序でサーチされます。

1. #include ディレクティブの引数が "file-name" または <file-name> の形でない場合、それがマクロであればそれを展開する。その結果は "file-name", <file-name> のどちらかの形でなければならない。そうでない場合はエラーとなる。
2. "file-name" の形でも <file-name> の形でも file-name がフルパスリストであれば、そのままオープンする。オープンできなければエラーとする。
3. フルパスリストでなくて "file-name" の形であれば、次のディレクトリ（からの相対パス）と解釈してサーチする。-I1 オプションでは 3.1、-I2 では 3.2、-I3 ではその双方（この順で）となる。デフォルトでは、UNIX 系の処理系, GCC, Visual C 用では 3.2、その他では原則として 3.1 である。ただし、Borland C 版では 3.1+3.2である。 Compiler-independent 版では 3.2 である。
   3.1. カレントディレクトリ（もちろん mcpp 起動時の）。#include がネストされていても、常にカレントディレクトリを基準とする。
   3.2. ソースファイル（インクルード元）のあるディレクトリ。#include がネストされている場合、ヘッダファイルが別ディレクトリにあると、そのたびに基準がズレてゆく。
   GCC 版ではさらに -iqoute オプションで指定されたディレクトリがサーチされる。 Visual C 版ではソースファイルの親（インクルード元）ファイルのディレクトリも順次さかのぼって、サーチされる。 それで発見できなければ、<file-name> の形と同様のサーチをする。
4. フルパスリストでなくて <file-name> の形であれば、次のディレクトリをサーチする。これらのディレクトリそのものが相対パスで指定されている場合は、カレントディレクトリからの相対パスと解釈する。これらを順にすべてサーチしてもファイルをオープンできなければエラーとする。
   4.1. mcpp 起動時に -I <directory> オプションで指定されたディレクトリ。複数あれば指定された順に（左から）サーチする。
   4.2. GCC 版では -isystem オプションで指定されたディレクトリ。複数あれば指定された順に（左から）サーチする。
   4.3. 環境変数で指定されたディレクトリ。この環境変数の名前は、noconfig.H (configed.H) の ENV_C_INCLUDE_DIR で定義されている。C++ では ENV_CPLUS_INCLUDE_DIR が定義されていればその環境変数が先に使われる。GCC 版では C_INCLUDE_PATH（C++ では CPLUS_INCLUDE_PATH も）、その他では `INCLUDE`（C++ では `CPLUS_INCLUDE` も）をデフォルトの環境変数名としている。環境変数で複数のディレクトリが separator で区切って指定されていれば、それらを最初のものから順にサーチする（separator は Windows では ;、その他では :）。
   4.4. noconfig.H (configed.H) のマクロ CPLUS_INCLUDE_DIR? で定義された implementation-specific なディレクトリ。
   4.5. system.c の set_sys_dirs() で指定された site-specific なディレクトリ(UNIX 系では `/usr/local/include`)。
   4.6. noconfig.H (configed.H) のマクロ C_INCLUDE_DIR? で定義された implementation-specific なディレクトリ。
   4.7. system-specific なディレクトリ（UNIX 系では `/usr/include`）。

-I- (GCC では -nostdinc、Visual C では -X) オプションを指定すると、上記の 4.4 以降のサーチは行われません。

パスの基準をカレントディレクトリとするのは、ANSI C Rationale では委員会の「意図 (intent)」であるとされています。基準ディレクトリが動くことがなく仕様が明確なので、妥当だと思われます。しかし、UNIX 系の処理系等では、少なくとも #include "header" の形式では include 元のソースファイルのあるディレクトリを基準とする習慣があるようです。mcpp も compiler-independent 版では大勢に従ってソースファイルのディレクトリを基準としました。

## 4.3. Header name の構築法

Header-name という pp-token の構築法と、そこから実際の include file のファイル名を取り出す方法は、次の通りです。

1. ソース中に文字列リテラルの形式で書かれていれば、それをそのまま header-name とする。ソース中にマクロで書かれていて、それを展開した結果が文字列リテラルになった場合も、同様である。文字列リテラルの形式の header-name では、単にその両端の " をとったものをファイル名とする。
2. ソース中に <filename> の形で書かれていれば、それをそのまま header-name とする。ソース中にマクロで書かれていて、それを展開した結果が <filename> の形になった場合も、同様である。マクロ中の space は複数の spaces を１個の space に圧縮した上で保存される。単に両端の <, > をとったものをファイル名とする。
3. どの場合でも、Windows では path-delimiter として \ も / も使えるが、\ は / に変換する。

## 4.4. #if 式の評価

#if 式の評価はホスト処理系（mcpp をコンパイルした処理系）とターゲット処理系（mcpp を使う処理系）が持つ最大の整数型によって決まります。双方の型が異なる時は、小さいほうの型が #if 式の型になります。Compiler-independent 版の mcpp ではターゲット処理系は存在しないので、ホスト処理系によって決まります。

pre-Standard モードでは #if 式は (signed) long だけで評価します。

long long を持たない処理系の Standard モードでは、long および unsigned long で行います。

long long を持つ処理系の Standard モードでは、#if 式は long long / unsigned long long で評価します。C90, C++98 では long / unsigned long で評価するのが規定ですが、mcpp では C90, C++98 でも long long / unsigned long long で評価し、long / unsigned long の範囲を超える場合はウォーニングを出します。*1

Visual C, Borland C 5.5 では long long はありませんが、それと同サイズの __int64 という型があるので、#if 式は __int64 / unsigned __int64 で評価します（ただし、Visual C++ 2002 までと Borland C 5.5 では LL, ULL という suffix が使えないので、これらの suffix は #if 行では使えるが地の文で使ってはいけない）。

また、-+ オプションで C++ のプリプロセスをする時は、#if 式中の true, false という pp-token の値はそれぞれ 1L, 0L と評価します。

Standard モードでの具体的な評価のしかたを、以下に説明します。long long を持たない処理系の場合は、以下の 4.4, 4.5 の記載はすべて、long long / unsigned long long をそれぞれ long / unsigned long と読み替えてください。Pre-Standard モードではすべて long と読み替えてください。

1. 個々の整数定数トークン（文字定数を含む）は、数値トークンに接尾子 U が付いていれば unsigned long long で評価する（pre-Standard モードでは接尾子 U は認知しない）。
2. そうでなければ、long long の非負の範囲におさまれば long long で評価する。
3. そうでなくて unsigned long long の範囲に入れば unsigned long long で評価する。
4. それも越える値は out of range のエラーとする。
5. 二項演算は被演算数のどちらかが符号なしであれば符号なしで、そうでなければ符号つきで行う。

どちらにしても整数定数トークンは常に非負の値をとります。
pre-Standard モードでは整数定数トークンの評価は非負の long の範囲で行い、それを越える値は out of range とします。その演算もすべて long で行います。

また、ホストの unsigned long long のほうがターゲットよりも範囲が狭い場合は、それを超える値は out of range となります。
定数同士の演算結果が範囲外となった場合は、long long では out of range のエラーとなり、unsigned long long ではウォーニングが出ます。演算の中間結果についても同様です。

負数の右ビットシフトや負数を含む割り算には移植性がないので、ウォーニングを出します。符号なし型と符号つき型の混合演算によって符号つき型の負の値が符号なし型の正の値に変換された場合も、ウォーニングを出します。実際の演算は、ホスト処理系のコンパイラ本体の仕様に従います。

C90, C++98 ではプリプロセスでの #if 式の評価はすべて long / unsigned long で（C99 ではその処理系の最大の整数型で）行うことになっています。mcpp では C90, C++98 でも long long / unsigned long long で評価し、long / unsigned long の範囲を超える場合はウォーニングを出します。どちらにしても、コンパイラ本体での if (expression) の評価の仕方よりは大ざっぱなものです。符号拡張が関係する場合には、コンパイラ本体とは違う結果になることが往々にしてあります。

また、Standard C のプリプロセスでは keyword というものが存在しないので、sizeof やキャストは使えません。もちろん、変数や列挙定数や浮動小数点数は使えません。Standard モードでは #if 式に defined 演算子が使え、#elif ディレクティブも使えます。あとはコンパイラ本体での if (expression) と同様に、各演算子の優先順位とグルーピング規則（いわゆる結合規則）に従って評価が行われます。２項演算子の多くでは、両辺を同型にするための算術変換が行われ、片方が unsigned long long の場合は他方は long long であっても unsigned long long に変換されます。

注:

*1 mcpp V.2.5 までは C90, C++98 では内部的には long long / unsigned long long で評価しながら、long / unsigned long の範囲を超える場合はエラーとしていたが、V.2.6 からは GCC や Visual C との互換性のためにエラーをウォーニングに格下げした。

## 4.5. #if 式での文字定数の評価

#if 式の定数トークンとしては識別子（マクロ、非マクロ）、整数の数値トークン、文字定数がありますが、このうち文字定数の評価の仕方はほとんど implementation-defined であり、portability はあまりありません。#if 'const' と compiler-proper での if ('const') との間でさえも結果が違う場合があります（Standard C でも、これが同じであることは保証されていない）。

POSTSTD モードではこのほとんど意味のない #if 式中の文字定数の評価は行いません（エラーとなる）。

文字定数の評価は他の整数定数トークンと同様に、long long, unsigned long long の範囲で常に正の値に評価します。pre-Standard モードでは long の範囲です。

Single character でない multi-byte character および wide character は、encoding が UTF-8 の場合は４バイト、それ以外ではすべて２バイトの型で評価します。UTF-8 はサイズが可変なので、４バイトの型で評価します。EUC-JP の３バイト encoding には対応していません（３バイト文字は１バイト＋２バイトの２文字として認識される。その結果、値は正しく評価されることになる）。２バイトの encoding でありながら、wchar_t が４バイトの型である処理系もありますが、mcpp は wchar_t には関知しません。以下では２バイトの multi-byte character encoding の場合について説明します。

'字' というような multi-byte character constant は ((１バイト目の値 << 8) + ２バイト目の値) と評価します（8 は <limits.h> の `CHAR_BIT` の値）。

'ab', '\x12\x3', '\x123\x45' というような multi-character character constant では、'a', 'b', '\x12', '\x3', '\x123', '\x45' 等をそれぞれ１バイトとして [0, 0xFF] の範囲で評価し、その結果を上位バイトから順次 8 ずつ左シフトさせながら足してゆきます（0xFF は <limits.h> の `UCHAR_MAX` の値）。１つの escape sequence の値が 0xFF を超えた時は、out of range のエラーとなります。したがって、文字セットが ASCII であれば、この３つのトークンの値はそれぞれ 0x6162, 0x1203, エラーとなります。

L'字' は '字' と同じ値となります。L'ab', L'\x12\x3', L'\x123\x45' 等の multi-character wide character constant については、L'a', L'b', L'\x12', L'\x3', L'\x123', L'\x45' をそれぞれ１つの wide character として [0, 0xFFFF] の範囲で評価し、その結果を上位の wide character から順次 16 ずつ左シフトさせながら足してゆきます。１つの escape sequence の値が２バイト符号なし整数の最大値を超えた時は、out of range エラーとなります。したがって、文字セットが ASCII であれば、この３つのトークンの値はそれぞれ 0x00610062, 0x00120003, 0x01230045 となります。

Multi-character character constant, multi-character wide character constant の値が unsigned long long の範囲を超えた時は out of range エラーとなります。

`__STDC_VERSION__` または `__cplusplus` の値が 199901L 以上の場合は、\uxxxx, \Uxxxxxxxx の形の UCN (universal-character-name) を１６進 escape sequence として評価します（こういう評価をしても何の意味もないが、しかしこう評価するしかないのである）。

ターゲット処理系のコンパイラ本体で char や wchar_t が符号ありの場合は、#if 式での文字定数の評価とコンパイラ本体での if (expression) による文字定数の評価とは、結果が違ってくることがあります。範囲エラーとなる範囲も違う可能性があります。また、multi-character character constant, multi-byte character constant の評価は、プリプロセッサだけでなくコンパイラ本体でも処理系によってまちまちです。`CHAR_BIT` が８であっても、'ab' を 'a' * 256 + 'b' と評価するか、それとも 'a' + 'b' * 256 と評価するかさえも、Standard C では決められていません。

一般に、#if 式では文字定数はそれに代わる手段がある限りは使うべきではありません。それに代わる手段がない場合というのは、私には思い付きませんが。

## 4.6. #if sizeof (type)

Standard C ではプリプロセスは実行時環境やコンパイラ本体の仕様から独立した文字通りのプリプロセスとして規定が明確にされ、その結果、#if 行では sizeof とキャストは使えないことになりました。しかし、pre-Standard モードでは #if 行で sizeof (type) が使えるようになっています。これは DECUS cpp を継承して、それに long long, long double の処理を追加する等の手を加えたものです（さすがにキャストを実装するのは煩雑なので、やっていない。やる気もない）。

eval.c の S_CHAR 等の S_* というマクロでは各型のサイズが定義されていますが、クロス処理系で使う場合は、もしホストとターゲットとでこれらの型の扱いが異なるなら、その値としてターゲット処理系のこれらの型のサイズを整数値で直接書く必要があります。

mcpp の #if sizeof には手抜きがあります。char, short, int, long, long long の頭に付く signed, unsigned は単に無視します。また、sizeof (void *) はサポートしません。いささか中途半端ですが、こういう後ろ向きの機能のために system.H のフラグを増やして煩雑にしたくないのです。どうせキャストもサポートしないのだから sizeof は削除しようかとも思いましたが、せっかく旧版にあったものなので、若干の手を加えただけで残してあります。

## 4.7. White-space sequence の扱い

mcpp は translation phase 3 の tokenization に際して、token separator としての複数の white spaces の sequence は、改行コード以外は原則として one space に圧縮します。 しかし、STD モードで -k または -K オプションが指定されたときは、圧縮せずにそのまま出力します。 また、行末の white space sequence は削除します。

行頭の white spaces は POSTSTD モードでは削除し、他のモードでは特別扱いで、そのまま出力します。 後者は人間が出力を読む場合のつごうに合わせてあります。*1

ただし、これはプリプロセスの中間段階の話です。その後に phase 4 があって、マクロ展開とプリプロセスディレクティブ行の処理が行われます。マクロ展開の後ではその前後に複数の spaces ができることがあります。もちろん、space がいくつあろうと、コンパイルの結果は何も変わりません。

Standard C では translation phase 3 でこれを one space に圧縮するかどうかは implementation-defined とされていますが、通常はユーザはまったく気にする必要はありません。Portability が問題になるのは、preprocessing directive 行に <vertical-tab> または <form-feed> がある場合だけです。この場合は Standard C では undefined です。mcpp ではこれらは space に変換します。

注:

*1 V.2.6.3 までは原則として one space に圧縮していたが、V.2.6.4 から変更した。

## 4.8. mcpp 実行プログラムのデフォルトの仕様

noconfig ディレクトリにある各処理系用の差分ファイルと makefile を使ってデフォルトの設定でコンパイルした場合の mcpp 実行プログラムの仕様をここに書いておきます。Configure スクリプトで設定を生成してコンパイルした場合は configure の結果によって違ってきますが、OS と処理系のバージョンが同一であれば、少なくともインクルードディレクトリ以外は同じ結果になるはずです。

Compiler-independent 版の mcpp の仕様には処理系による相違はほとんどありませんが、OS と CPU による相違が少しあります。

mcpp には compiler-independent-build と compiler-specific-build とがあり、そのどちらでもいくつかの動作モードがあります。それについては 2.1 を見てください。ここでは STD モードを中心に説明します。

これらの差分ファイルと makefile は次の処理系用のものです。

> FreeBSD 6.3 | GCC V.3.4
> Vine Linux 4.2 / x86 | GCC V.2.95, V.3.2, V.3.3, V.3.4, V.4.1
> Debian GNU/Linux 4.0 / x86 | GCC V.4.1
> Ubuntu Linux 8.04 / x86_64 | GCC V.4.2
> Fedora Linux 9 / x86 | GCC V.4.3
> Mac OS X Leopard / x86 | GCC V.4.0
> CygWIN | 1.3.10 (GCC V.2.95), 1.5.18 (GCC 3.4)
> MinGW & MSYS | GCC 3.4
> WIN32 | LCC-Win32 2003-08, 2006-03
> WIN32 | Visual C++ 2003, 2005, 2008
> WIN32 | Borland C++ V.5.5

このほか、私の持っていない次の処理系についても、ユーザから contribute された差分ファイルが収録されています。

> WIN32 | Visual C++ V.6.0, 2002
> WIN32 | Borland C++ V.5.9 (C++Builder 2007)

いずれもそれらの処理系自身でコンパイルされます。

noconfig.H, system.H で定義されるマクロのうち、次のものはどの処理系用もすべて同じ設定にしてあります。

DIGRAPHS_INIT == FALSE でコンパイルされているので、digraph は -2 (-digraphs) オプションで有効となります。
TRIGRAPHS_INIT == FALSE としているので、trigraph は -3 (-trigraphs) オプションで有効となります。
OK_UCN は TRUE にしているので、C99, C++ で UCN (universal character name) が使えます。
OK_MBIDENT は FALSE としているので、識別子中に multi-byte-character は使えません。
STDC は 1 としているので、`__STDC__` の初期値は 1 となります。

各種の translation limits は次のようにしています。

> NMACPARS（マクロの引数の最大数） | 255
> NEXP （#if 式中の副式の最大ネストレベル） | 256
> BLK_NEST（#if section の最大ネストレベル） | 256
> RESCAN_LIMIT（マクロ再走査の最大ネストレベル） | 64
> IDMAX （identifier の有効長） | 1024
> INCLUDE_NEST（#include の最大ネストレベル） | 256
> NBUFF（ソースの最大行長）*1 | 65536
> NWORK（出力の最大行長） | 65536
> NMACWORK（マクロ展開等の内部バッファのサイズ） | 262144

ただし、GCC 版、Visual C 版では出力の最大行長は NWORK ではなく NMACWORK です。

次のマクロは OS によって異なった設定にしています。build type には関係しません。

MBCHAR （デフォルトの multi-byte character encoding）

> FreeBSD, Linux, Mac OS X | EUC_JP
> Win32, CygWIN, MinGW | SJIS

次のマクロは処理系によって異なった設定にしています。

STDC_VERSION （`__STDC_VERSION__` の初期値）

> Compiler-independent, GCC 2 | 199409L
> その他 | 0L

HAVE_DIGRAPHS （digraphs をそのまま出力するか）

> Compiler-independent, GCC, Visual C | TRUE
> その他 | FALSE

EXPAND_PRAGMA （C99 で #pragma 行の引数をマクロ展開するか）

> Visual C, Borland C | TRUE
> その他 | FALSE

GCC 2.7-2.95 では `__STDC_VERSION__` は 199409L となっていましたが、3.*, 4.* では `__STDC_VERSION__` はデフォルトでは事前定義されず、実行時オプションに応じて定義されるようになりました。mcpp の GCC 用の設定はこれに対応したものです。

STDC_VERSION が 0L のものでは、`__STDC_VERSION__` はデフォルトでは 0L に pre-define されます。-V199409L オプションで `__STDC__` が 1 で `__STDC_VERSION__` が 199409L、事前定義マクロは '_' で始まるものだけという、厳密な C95 モードとなります。また、-V199901L オプションで C99 モードとなります。

C99 モードでは `__STDC_HOSTED__` が 1 に pre-define されます。`__STDC_ISO_10646__`, `__STDC_IEC_559__`, `__STDC_IEC_559_COMPLEX__` は mcpp 自身は pre-define しません。処理系のシステムヘッダに任せます。実際には、glibc 2 / x86 のシステムではシステムヘッダによって `__STDC_IEC_559__`, `__STDC_IEC_559_COMPLEX__` が 1 に定義され、他の処理系ではどれも定義されません。

HAVE_DIGRAPHS が FALSE のものでは（digraph を実装していない処理系）、digraph は mcpp で通常の token に変換されてから出力されます。

EXPAND_PRAGMA が TRUE であっても、STDC, MCPP, GCC のどれかで始まる #pragma 行はマクロ展開しません。

Include ディレクトリは次のように設定してあります。
まず、UNIX 等で言ういわゆる system-specific なものおよび site-specific なものは次の通りです。これは compiler-independent 版でも同じです。

> FreeBSD, Linux, Mac OS X, CygWIN | /usr/include, /usr/local/include

Mac OS X ではこのほか、framework ディレクトリが /System/Library/Frameworks, /Library/Frameworks に設定されます。

MinGW では /mingw/include が標準の include ディレクトリになります。

処理系やそのバージョンによって異なる implementation-specific なものについては *.dif ファイルを見てください。Compiler-independent 版ではこれらは定義されません。Windows の処理系ではこれらは特に設定せず、環境変数を参照します。環境変数は INCLUDE, CPLUS_INCLUDE です。Compiler-independent 版でも同様。
これでつごうが悪ければ、設定を変えて mcpp をリコンパイルするか、環境変数で指定するか、-I オプションで指定するかしてください。

mcpp は GCC 版, Visual C 版以外では、プリプロセスした結果が NWORK-1 を超える時は、これ以下の長さに行を分割して出力します。文字列リテラルの長さは NWORK-2 以下でなければなりません。

念のために繰り返しますが、以上の italic で表示されているマクロはいずれも mcpp をコンパイルした時のものであり、mcpp の実行プログラムが持っている組み込みマクロではありません。

入力ファイルを指定せずに mcpp を起動して、#pragma MCPP put_defines と打ち込むと、組み込みマクロの一覧が表示されます。

`__STDC__` が 1 以上の状態では、_ で始まらない組み込みマクロは削除されます。-N (-undef) オプションでは `__MCPP` 以外がすべて削除されます。その上で -D オプションで設定し直してもかまいません。処理系のバージョンが少し違うだけで include directory が違わない場合は、mcpp をリコンパイルしなくても、この方法でバージョンマクロを再定義することで別バージョンに対応させることができます。-N や -U を使わなくても、-D で特定のマクロだけ再定義することができます。

-+ (-lang-c++) オプションで C++ のプリプロセスを指定した時は、`__cplusplus` が事前定義されますが、その初期値は 1L です。そのほかにさらにいくつかのマクロが事前定義されます。

GCC では V.3.2 までは事前定義マクロと言っても GCC が事前定義するものは少なく、多くは gcc から cpp に -D オプションで渡されるものでした。それとの互換性のためには mcpp で定義する必要はないのですが、mcpp を（"pre-preprocess" 等で）単体で動かす時の便宜のために mcpp 内で定義しています。
GCC V.3.3 以降は突然、60 個から 70 個のマクロが事前定義されるようになりました。mcpp V.2.5 以降でも GCC 専用版ではやむなくこれを取り込んでいます。したがって、GCC V.3.3 以降用の mcpp では上記のほかに多くのマクロが事前定義されます。その内容は mcpp をインストールする時に生成される mcpp_g*.h というヘッダファイルでわかります。

FreeBSD, Linux, CygWIN, MinGW / GCC, LCC-Win32, Visual C 2008 は long long を持っているので、#if 式は long long, unsigned long long で評価します。Visual C 6.0, 2002, 2003, 2005, Borland C 5.5 では long long はありませんが、__int64, unsigned __int64 という型があるのでこれを使います。

これらの処理系では long の範囲はいずれも

    [-2147483647-1, 2147483647] ([-0x7fffffff-1, 0x7fffffff])

です。unsigned long はいずれも

    [0, 4294967295] ([0, 0xffffffff])

の範囲です。

long long を持つ処理系ではいずれも long long の範囲は

    [-9223372036854775807-1, 9223372036854775807]
    ([-0x7fffffffffffffff-1, 0x7fffffffffffffff])

で、unsigned long long の範囲は

    [0, 18446744073709551615] ([0, 0xffffffffffffffff])

です。

これらの処理系本体ではいずれも符号つき整数型の内部表現は２の補数であり、ビット演算もそれに対応しているので、mcpp の #if 式でも同様です。
負の整数の右シフトはいずれも算術シフトであり、mcpp の #if 式でも同様です（１ビットの右シフトで値が符号つきのまま 1/2 になる）。
整数の除算・剰余算で operand の片方または双方が負である場合はいずれも Standard C の ldiv() 関数と同じ代数的演算が行われるので、mcpp の #if 式でも同様です。

これらのシステム（ＯＳ）ではいずれも基本文字セットが ASCII なので、mcpp でも同様です。

私が書いた kmmalloc というメモリー管理ルーチンがあり、malloc(), free(), realloc() 等を含んでいますが、CygWIN, Visual C 2005, 2008 以外ではこれがインストールされている場合は、make する時に MALLOC=KMMALLOC（または -DKMMALLOC=1）というオプションを指定するとこれが link されます。ヒープメモリのデバッグ用ルーチンもリンクされるようになっています。errno の番号 EFREEP, EFREEBLK, EALLOCBLK, EFREEWRT, ETRAILWRT には Linux, LCC-Win32 ではそれぞれ 2120, 2121, 2122, 2123, 2124 を、それ以外ではそれぞれ 120, 121, 122, 123, 124 を割り当てています（ mcpp-porting.html#4.extra 参照）。*2

GNU のシステムおよび Visual C 以外では、環境変数 TZ はあらかじめ JST-9 にセットしておく必要があります。そうしないと、`__DATE__`, `__TIME__` マクロの値がズレてしまいます。

注:

*1 これは <backslash><newline> による行接続をした後の行長にも、コメントを a space に変換した後の行長にも適用される。コメントが複数行にまたがっている場合、コメントの変換によってそれが１行に連結されることに注意。

*2 CygWIN 1.3.10, 1.5.18 では malloc() に _malloc_r() 等といった内部ルーチンがあり、他のライブラリ関数にはそれらを呼び出すものがいくつかある。そのため、他の malloc() を使うことができない。Visual C 2005, 2008 でも、プログラム終了処理のルーチンが Visual C の malloc() の内部ルーチンを呼び出すので、やはり他の malloc() を使うことができない。

# 5. 診断メッセージ

## 5.1. 診断メッセージの形式

mcpp が出す診断メッセージとその意味は、以下の通りです。診断メッセージはいずれも標準エラー出力に出力され、-Q オプションでカレントディレクトリ中の mcpp.err というファイルにリダイレクトされます。

診断メッセージは次の形をとっています。

1. "filename:line: " に "fatal error: ", "error: ", "warning: " のどれかが続き、さらに 5.3 - 5.9 のメッセージのうちのどれかが続く。"filename:line: " に始まる１行の診断メッセージというのはいささか窮屈な仕様であるが、UNIX 上のＣ処理系での伝統的な診断形式で、各種のツールがこれを前提としているので採用している。画面上の１行におさまらないことがしばしばある。
2. マクロ展開中であれば、そのマクロ呼び出しが表示される。ネストされたマクロ呼び出しであれば、それぞれのマクロ名が表示される。そのマクロ定義も表示され、そのマクロ定義のあるソースファイル名と行番号も表示される。
3. ソースファイル名と行番号とその行が表示される。ファイルが include されたものであれば、include 元のファイルの名前と行番号と行が順次表示される。

表示される行は通常は、ソースの「物理行」が行末の \ によって接続されたあとの「論理行」からさらにコメントを a space に変換した後のものであり、コメントが行をまたいでいる場合は複数の論理行が連結されたものとなる。行番号は連結された最後の物理行の番号である。ただし、コメント処理等の前の translation phase でのエラー等では、その phase の行が表示される。

ただし、-j オプションを指定した時は、上記の２と３は出力しません。

診断メッセージには次の３つのレベルがあります。

> fatal error | プリプロセスをこれ以上続けても意味がない場合
> error | 構文や使用法が間違っている場合
> warning | Portability のない場合や間違いの可能性のある場合

Warning にはさらに次の５つのクラスがあります。Class 1, 2 以外はやや特殊なものです。

> class 1 | 間違いの可能性のある、または portability を欠いたソース
> class 2 | 規格上は問題があるが実際にはたぶん問題のないソース
> class 4 | 実際にはたぶん問題がない portability に関する warning
> class 8 | スキップされる #if group や、#if 式中の評価をスキップされる副式等についてのお節介な warning
> class 16 | trigraph, digraph についての warning

mcpp はきわめて多種の診断メッセージを用意しています。STD モードでは次のような種類にのぼっています。

> fatal error | 17 種
> error | 76 種
> warning class 1 | 49 種
> warning class 2 | 15 種
> warning class 4 | 17 種
> warning class 8 | 30 種
> warning class 16 | 2 種

これらについて、原則としてその行の中の問題の部分を具体的に指摘します。

なお、以下の説明では、診断メッセージで引用されるソース中のトークンや数値の部分には例として何かのトークンをはめこんでいます。そのうち、数値のかわりにマクロ名を書いているところは、実際にはそのマクロを展開した値が表示されます。
また、場合によってエラーとして出たりウォーニングとして出たりするメッセージもあります。以下の説明では同一のメッセージについては最初に記載するところでだけ解説を加え、あとは単にメッセージを並べるだけにします。

## 5.2. Translation limits

以下のエラーの中には、バッファのオーバーフロー等の mcpp の仕様上の制限によるものがあります。バッファサイズ等の translation limits は system.H のマクロで定義されています。必要な場合はその定義を大きくして mcpp をリコンパイルしてください（しかし、メモリの少ないシステムでは、あまり仕様を大きくすると out of memory が発生しやすくなるので、ほどほどに）。

## 5.3. Fatal error

I/O エラーやメモリ不足等、プリプロセスをそれ以上続けることができない場合、バッファオーバーフロー等、プリプロセスを続けても意味がない場合に、このエラーメッセージが出て、プリプロセスを中止します。親プロセスには「失敗」の状態値を返します。

### 5.3.1. mcpp 自身のバグ

- `Bug:`
  このメッセージは数種ありますが、いずれも mcpp 自身のバグを意味します。これが出ることはないと思いますが、もし出たらぜひその状況をご連絡ください。

### 5.3.2. 物理的エラー

- `File read error`
  ソースファイル読み込みのエラーです。ディスクかファイルシステムが傷んでいるのでしょう。
- `File write error`
  出力ファイルの書き出しエラーです。ディスクかファイルシステムが傷んでいるかいっぱいになっているのでしょう。
- `Out of memory (required size is 0x1234 bytes)`
  メモリが足りなくなりました（mcpp がヒープから 0x1234 bytes を取得しようとしたが、できなかった）。メモリの少ないシステムで長大なマクロ定義があまりにも多くある場合に発生します。ソースファイルを分割して、1 translation unit のマクロ定義を減らしてください。

### 5.3.3. Translation limits と内部バッファのエラー

- `Too long header name "long-file-name"`
  Include すべきファイルのパスリスト（指定のディレクトリのパスリストと連結したもの）が PATHMAX のサイズを超えています。
- `Too long source line`
  ソースの物理行の長さが NBUFF-2 を超えています。Ｃのソースではないのでしょう。
- `Too long logical line`
  ソースの物理行を行末の \ によって接続した論理行の長さが NBUFF-2 を超えています。あまりにも長大なマクロを定義した時に発生することがあります。そのようなコードはマクロではなく関数として書くべきでしょう。
- `Too long line spliced by comments`
  コメントを a space に変換したあとの行の長さが NBUFF-2 を超えています。行をまたぐコメントによって多くの行を連結した場合に発生します。コメントを分離してください。
- `Too long token`
  プリプロセス後の行に NWORK-2 を超える長さのトークンが含まれています。mcpp はコンパイラ本体の受け取れる行長 NWORK に出力行を分割しようとしますが、極端に長いトークンがあると分割しても出力できないことがあります。

次の４つのエラーはトークンがさほど長くなくても、マクロ展開中にそのトークンのところでバッファオーバーフローになった場合にも起こります。その場合はマクロ呼び出しを分割してください。

- `Too long quotation "long-string"`
  文字列リテラル、文字定数または header-name が長すぎます。文字列リテラルであれば分割してください。隣接する文字列リテラルは Standard C の処理系であればコンパイラ本体が連結してくれます。
- `Too long pp-number token "1234567890toolong"`
  Preprocessing-number token が長すぎます。Standard モードの場合です。
- `Too long number token "12345678901234......"`
  Number token が長すぎます。pre-Standard モードの場合です。
- `Buffer overflow scanning token "token"`
  token をスキャンしているうちにバッファオーバーフローとなりました。文字列リテラル、文字定数、header-name、pp-number 以外のトークンでは、こちらのメッセージが出ます。
- `More than BLK_NEST nesting of #if (#ifdef) sections`
  #if, #ifdef, #ifndef 等のネストが BLK_NEST を超えています（BLK_NEST 等のマクロ名の部分は実際にはその値が表示される。以下同様）。#if section を分割してください。
- `More than INCLUDE_NEST nesting of #include`
  #include のネストが INCLUDE_NEST を超えています。#include が無限再帰になっているのでしょう。

### 5.3.4. #pragma MCPP preprocessed に関するエラー

- `This is not the preprocessed source`
  #pragma MCPP preprocessed がありますが、これは mcpp によってプリプロセスされたソースファイルではありません。
- `This preprocessed file is corrupted`
  これは mcpp によってプリプロセスされたソースファイルのようですが、壊れていて使えません。

## 5.4. Error

文法的な間違いがある場合にこのエラーメッセージが出ます。
Standard C では、violation of syntax rule or constraint があった場合は処理系は何らかの診断メッセージを出さなければならないことになっていますが、Standard モードではこの violation に対しては原則としてエラーメッセージが出ます。一部はウォーニングです。

また、Standard C で undefined とされているものの多くについても error メッセージまたは warning が出ます。Undefined でありながら error も warning も出ないのは、次のものだけです。

1. 文字列リテラルの形の header name 中の ' と /*。これは単なる文字として扱われる。実際にはこれは、ファイルをオープンしようとしてエラーになるはずである（<, > で囲まれた header name の中ではこれらは文字定数およびコメントの開始と解釈されるので、何らかのエラーとなる）。header name の中では \ も undefined であるが、これもチェックしない。実際にはやはりファイルのオープンでエラーになるであろう（Windows 版では \ は class 2 の warning を出した上で / に変換して処理する）。
2. #undef defined。defined という名前を #undef するのは undefined であるが、mcpp では defined という名前のマクロを定義することはできないようになっているので、それが取り消されることもない。
3. コメント中に illegal な multi-byte character sequence があった場合は undefined であるが、これは実害はないので診断しない（文字列リテラル、文字定数、header name 中の illegal multi-byte character sequence にはウォーニングが出る）。
4. _ で始まる identifier は処理系のために予約されており、ユーザプログラムで定義すると結果は undefined であるが、プリプロセッサではユーザプログラムかそうでないかを必ずしも判別できないので、診断しない。
5. C99 でオプションとして規定されている事前定義マクロのうち、`__STDC_ISO_10646__`, `__STDC_IEC_559__`, `__STDC_IEC_559_COMPLEX__` は #define したり #undef したりすると undefined であるが、診断しない。これらのマクロは処理系のヘッダファイルで定義されることになるであろうが、プリプロセッサはユーザプログラムかどうかを必ずしも判別できないからである。
6. C99 の UCN については、translation phase 2 で <backslash><newline> を削除した結果 UCN に相当する sequence ができた場合、および文字列リテラルの連結によって UCN sequence が生成された場合は undefined であるが、これは診断しない（いずれも UCN として扱う）。

Standard C のプリプロセスで具体的に何が violation of syntax rule or constraint で、何が undefined で何が unspecified で何が implementation-defined であるかについては、cpp-test.html を参照してください。

Fatal でない error メッセージが出てもプリプロセスは続けます。終了すると、エラーの数を表示し、親プロセスに「失敗」の状態値を返します。

### 5.4.1. 文字とトークンに関するエラー

- `Illegal control character 0x1b, skipped the character`
  文字列リテラル、文字定数、header name、コメント以外のところに white space 以外のコントロールコードがあります。その文字をスキップして処理を続けます。

次はトークンのエラーです。初めの４つはいずれもその行をスキップして処理を続けます。初めの３つは文字列リテラル等のトークンで、論理行の行末までに引用符が閉じられていないことを示します。

    #error  I can't understand.

などと、preprocessing-token sequence の形を成さないテキストを文字列リテラルでもコメントでもないところに書くと、この種のエラーとなります。Pre-processing-token は本来の（コンパイラ本体での）Ｃの token よりおおまかなもので、文字が source character set に含まれてさえいればほとんどの character sequence が何らかの pp-token sequence として通るので、preprocessing-token エラーとなるのはこれだけです。

なお、スキップされる #if group の中でも pp-token エラーはエラーとなります。

- `Unterminated string literal`
  文字列リテラルが完結していません。文字列リテラルは論理行をまたぐことはできません。必要なら、複数行に文字列リテラルを書いて、処理系にそれを連結させてください。このエラーは # 演算子による文字列化で発生することもありますが、その場合は行はスキップしません。
  OLDPREP モードではこれはエラーになりません（行末がリテラルの終わりとみなす）。
  また、-a (-lang-asm, -x assembler-with-cpp) オプションで起動された場合は、行をまたぐ文字列リテラルと判断して、\n を挿入して次の行と連結するので、エラーにはなりません（ウォーニングは出る）。
- `Unterminated character constant 't understand.`
  文字定数が完結していません。OLDPREP モードおよび lang-asm モードではこれはエラーになりません（行末がリテラルの終わりとみなす）。
- `Unterminated header name <header.h`
  header-name が完結してしません。<, > で囲まれる header-name 中に " または ' があった場合はこのエラーではなく上記２つのエラーとなります。また、<, > で囲まれる header-name 中に /* があった場合はそこから後がコメントと解釈されます。
- `Empty character constant ''`
  文字定数の中身がカラです。 -a オプションが指定されたときは、これはエラーにはならずウォーニングが出ます。
- `Illegal UCN sequence`
  std モードで `__STDC_VERSION__` の値が 199901L 以上の場合、または C++ の場合は、UCN が認識されますが、識別子中の \u, \U で始まる１６進 sequence のケタ数が、それぞれ４ケタ、８ケタに満ちていません。（#if 式中の文字定数でこれが起こったときは、Undefined escape sequence のウォーニングとなる。それ以外の場合は診断されない）。
- `UCN cannot specify the value "0000007f"`
  UCN は１６進で [0, 9f], [d800, dfff] の範囲の値を指定することはできません。ただし、前者のうち 0x24 ($), 0x40 (@), 0x60 (`) は可です。前者はこの３文字を除くとすべて basic source character set の値と一致するので、使えません。また、後者は special characters のための reserved area となっています。ただし、C++ では後者の制限はありません（なぜか規格が微妙に違っている）。しかし、C++ でも -V199901L としてマクロ `__cplusplus` を 199901L 以上に事前定義した場合は、これに関しては C99 と同じ仕様になります。
- `Illegal multi-byte character sequence "XY"`
  OK_MBIDENT == TRUE でコンパイルした Standard モードの場合、C99 では identifier 中に multi-byte character が使えますが、multi-byte character として認められない character sequence があるとエラーになります（identifier 以外の pp-token ではウォーニング）。

### 5.4.2. 完結しないソースファイルのエラー

次のメッセージはソースファイルが完結しない #if section、マクロ呼び出し等で終わっている場合に出ます。そのファイルで入力が終わりの場合（include されたファイルでない場合）は、"End of file" ではなく "End of input" と表示されます。
これらの診断メッセージは mcpp のモードによって、エラーとなる場合とウォーニングとなる場合とあります。
Standard モードではこれらはすべてエラーです。そのマクロ呼び出しはスキップし、#if section の対応関係はそのファイルが include された時の初期状態に戻します。
pre-Standard モードではすべてウォーニングです。pre-Standard モードでも、OLDPREP モードでは unterminated macro call 以外はウォーニングさえも出ません。

- `End of file within #if (#ifdef) section started at line 123`
  123 行の #if (#ifdef, #ifndef) に対応する #endif がありません。
- `End of file within macro call started at line 123`
  123 行で始まったマクロ呼び出しがファイルの終わりまでに完結していません。引数のカッコが対応していない場合やトークンエラーがあった時に、残りの部分を引数として読んでいってファイルの最後まで達することがあります（その前に Buffer overflow となるかもしれない）。また、マクロ展開の仕様がモードによって異なるので、あるモードでは意図通りに展開されるマクロが他のモードではこのエラーになることがあります。

### 5.4.3. Preprocessing group 等の対応関係のエラー

次は #if, #else 等の group の対応関係のエラーです。これらの行は無視して（それまでの group が続いているものとして）処理を続けます。これらのチェックはたとえスキップされる #if group の中にあっても行われます。
なお、#if (#ifdef) section とは #if, #ifdef, #ifndef から #endif まで、#if (#elif, #else) group とは１つの #if (#ifdef) section のうちの #if (#ifdef, #ifndef), #elif, #else, #endif 等ではさまれた１つの行ブロックを指します。

- `Already seen #else at line 123`
  123 行に #else が現れた後にまた #else (#elif) が現れています。#endif を書き忘れたのかもしれません。
- `Not in a #if (#ifdef) section`
  #if (#ifdef, #ifndef) なしに #else (#elif, #endif) が現れています。
- `Not in a #if (#ifdef) section in a source file`
  Include されたソースファイルの中で、#if (#ifdef, #ifndef) なしに #else (#elif, #endif) が現れています。Include されたファイルが元ファイルの中にあればエラーにならないが、単一のファイルの中ではバランスがとれていないという場合です。Standard モードの時だけです（pre-Standard モードではウォーニング）。

次の２つは #asm, #endasm の対応関係のエラーです。もちろん、pre-Standard モードの特定の処理系の場合だけです。

- `In #asm block started at line 123`
  123 行から始まった #asm ブロックの中にまた #asm があります。#asm は入れ子にできません。たぶん、#endasm を書き忘れたのでしょう。
- `Without #asm`
  #asm ブロックの中ではないところに #endasm があります。

### 5.4.4. ディレクティブ行の単純な構文エラー

これ以降（5.4.12 まで）のエラーはスキップされる #if group の中では起こりません（-W8 オプションで起動すると、Unknown directive についてはウォーニングを出す）。
次は # で始まるディレクティブ行の単純な文法エラーです。これらの行は無視して処理を続けます（すなわち、#if を section の開始とみなさず、#line では行番号は変わらない等）。#include, #line 行の引数がマクロであれば、それを展開したうえでチェックが行われます（pre-Standard モードでは展開しない）。
下記のメッセージそのものにはディレクティブ名が出てきませんが、これに続いて表示されるソース行でディレクティブがわかります（ディレクティブ行はコメントが space に変換されると、必ず１行になる）。

- `Illegal #directive "123"`
  # に続くトークンが名前ではありません。# に続くのはディレクティブ名でなければなりません（しかし、OLDPREP モードでは #123 は #line 123 と同じものとして扱う）。
- `Unknown #directive "pseudo-directive"`
  pseudo-directive というディレクティブは実装されていません。-a (-lang-asm, -x assembler-with-cpp) オプションで起動された場合は、エラーにはならずウォーニングになります。
- `No argument`
  #if, #elif, #ifdef, #ifndef, #assert, #line の引数がありません。
- `No header name`
  #include 行の引数がありません。または引数がマクロですが、それを展開したところ引数にはトークンが何もなくなりました。
- `Not a header name "UNDEFINED_MACRO"`
  引数が header name ではありません。header name を定義するはずのマクロが定義されていない場合などです。<, > または ", " で囲まれたものが header name です。
- `Not an identifier "123"`
  #ifdef, #ifndef, #define, #undef には identifier の引数が必要ですが、123 は identifier ではありません。
- `No identifier`
  #define, #undef の引数がありません。
- `No line number`
  #line の引数がマクロですが、それを展開したところ 引数には何もトークンが残らなくなってしまいました。
- `Not a line number "name"`
  #line の第一の引数が数値トークン（preprocessing number）ではありません。
- `Line number "0x123" isn't a decimal digits sequence`
  #line の第一引数は十進整数トークンでなければなりません。Standard モードの場合です（pre-Standard モードはウォーニングは出すが、１６進、８進等の整数トークンも認める）。
- `Line number "2147483648" is out of range of [1,2147483647]`
  #line の第一引数は [1,2147483647] の範囲になければなりません。0 もエラーです。Standard モードの場合です。
- `Not a file name "name"`
  #line の第二引数がある場合は通常の文字列リテラルでなければなりません。Identifier やワイド文字列リテラル等ではいけません。

次のエラーは Standard モードの場合だけで、これらのディレクティブは無視されます。OLDPREP モードではエラーもウォーニングも出ず、KR モードではウォーニングとなり、この "junk" がなかったものとしてプリプロセスを続けます。

- `Excessive token sequence "junk"`
  #else, #endif, #asm, #endasm の行に余計なテキスト junk があります。#ifdef, #ifndef, #include, #line, #undef の行の正しい引数の後ろに余計なテキスト junk があります。それらはコメントとして書いてください。

### 5.4.5. #if 式の構文エラー等

次は #if, #elif, #assert ディレクティブ中の式の構文に関するエラーです。

#if (#elif) でエラーが起こった時は、その #if (#elif) 行は偽と評価されたものとして（すなわちその group をスキップして）、プリプロセスを続けます。

スキップされる #if (#ifdef, #ifndef, #elif, #else) group については、それがＣの legal な preprocessing token で成り立っているかどうかと、#if 等の group の対応関係はチェックしますが、その他の文法エラーはエラーにはなりません。

#if 行そのものの中では、評価をスキップされる部分式があります。例えば #if a || b のような式で a が真である場合は、b の評価は行われません。しかし、次の１４種の文法エラーないし translation limit のエラーはたとえ評価をスキップされる部分式中にあってもチェックされます。

- `More than NEXP*2-1 constants stacked at "12"`
  #if 式の評価で、スタックに積まれている定数が 12 のところで NEXP*2-1 以上になりました。#if 式のネストが深すぎます。
- `More than NEXP*3-1 operators and parens stacked at "+"`
  #if 式の評価で、スタックに積まれている演算子とカッコの合計が + のところで NEXP*3-1 以上になりました（カッコは一対を２つと数える）。#if 式のネストが深すぎます。
- `Misplaced constant "12"`
  #if 式の定数のあるべきでない位置に定数 12 があります。#if ではキャストは使えませんが、(int)0x8000 などとキャストを使ったりすると、int がマクロとして定義されていない identifier とみなされ 0 と評価されるので、(0)0x8000 となり、このエラーが発生します。
- `Operator ">" in incorrect context`
  #if 式の妙な位置に > という演算子があります。マクロ MACRO が０個のトークンに定義されている時に、#if MACRO > 0 などとすると、マクロ展開の結果 #if > 0 となるので、このエラーとなります（この場合はその直前に Macro "MACRO" is expanded to 0 token というウォーニングが出るので、それとわかる）。
- `Unterminated expression`
  #if 式が完結していません。MACRO が０個のトークンに定義されている時の #if a || MACRO などの場合です。
- `Excessive ")"`
  #if 式に ( に対応しない余計な ) があります。
- `Missing ")"`
  #if 式で ( に対応する ) がありません。
- `Misplaced ":", previous operator is "+"`
  ? に対応しない : があります。
- `Bad defined syntax`
  #if defined の文法が間違っています。かっこの対応がとれていなかったり、 identifier が引数になっていなかったりの場合です。マクロ展開の結果、このエラーが起こった場合は、このメッセージに続いてその展開結果を表示します。
- `Can't use a string literal "string"`
  #if 式の定数には文字列リテラルは使えません。
- `Can't use a character constant 'a'`
  POSTSTD モードでは #if 式の定数に文字定数、ワイド文字定数は使えません。
- `Can't use the operator "++"`
  #if 式に使えない演算子があります。=, ++ 等です。
- `Not an integer "1.23"`
  #if 行の定数には整数（文字定数を含む）しか使えません。
- `Can't use the character 0x24`
  #if 式に使えない文字（コード 0x24）があります。 identifier, operator, punctuator, string literal, character constant, preprocessing number のどの preprocessing token にも合致しない、その文字１字のトークンです（コントロールコードはその前にチェックされているので、それ以外の文字）。 $ を identifier 中に認める処理系でも、オプションによってはこれが使えなくなります。 もちろん、スキップされる group の中にあるものはチェックされません。

次は #if sizeof に関するエラーです。もちろん、pre-Standard の場合だけです。

- `sizeof: Syntax error`
  #if sizeof の文法が間違っています。かっこがなかったり、かっこが多すぎたり、かっこの対応がとれていなかったり、引数がなかったりする場合です。
- `sizeof: No type specified`
  #if sizeof (type) の type が指定されていません。sizeof (*) などです。sizeof ((*)()) は関数へのポインタのサイズを求める legal な構文です。

### 5.4.6. #if 式の評価に関するエラー

次のエラーは評価をスキップされる部分式にある場合は起こりません（-W8 オプションではこれらについてもウォーニングが出る）。

#if 式は C99 ではその処理系の持つ最大の整数型で、C90, C++98 では long / unsigned long で評価するのが規定ですが、mcpp では C90, C++98 でも long long / unsigned long long で評価します。ただし、C90, C++98 で long / unsigned long の範囲を超えた場合はウォーニングを出します。
long long のない処理系では、この節の long long / unsigned long long は long / unsigned long と読み替えてください。pre-Standard ではすべて (signed) long と読み替えてください。POSTSTD では #if 式に文字定数は使えないので、別のエラーになります。

- `Constant "123456789012345678901" is out of range`
  整数定数の値が unsigned long long で表現できる範囲を超えています。
- `Integer character constant 'abcdefghi' is out of range`
  文字定数 'abcdefghi' の値が unsigned long long で表現できる範囲を超えています。
- `Wide character constant L'abcde' is out of range`
  ワイド文字定数 L'abcde' の値が unsigned long long で表現できる範囲を超えています。STD モードの時だけです。
- `8 bits can't represent escape sequence '\x123'`
  文字定数中の１つの escape sequence の値が 8 bits で表現できる範囲（[0, 0xFF]）を超えています。
- `16 bits can't represent escape sequence L'\x12345'`
  ワイド文字定数中の１つの escape sequence の値が 16 bits （UTF-8 の場合は 32 bits）で表現できる範囲を超えています。STD モードの時だけです。
- `Division by zero`
  #if 式に 0 による割り算があります。割り算は / によるものと % によるものがあります。#if dividend / divisor .. で divisor がマクロとして定義されていない場合などに起こります。#if defined divisor && (dividend / divisor ..) と書くことで、このエラーを避けることができます。
- `Result of "op" is out of range`
  演算子 op による演算の結果が long long の範囲外となりました。op は２項演算子 *, /, %, +, - のどれかです。整数の内部表現が２の補数の場合は、単項 '-' 演算子も -`LLONG_MIN` でオーバーフローとなります。Unsigned long long はオーバーフローすることがないのでエラーにはなりませんが、代数的な演算結果が範囲外となる場合はウォーニングが出ます。

次は sizeof に関するエラーです。スキップされる部分式では出ません（-W8 オプションではウォーニングが出る）。pre-Standard の場合です。

- `sizeof: Unknown type "type"`
  #if sizeof (type) の type が変です。
- `sizeof: Illegal type combination with "type"`
  #if sizeof (long float) 等と、型の組み合わせが変です。

### 5.4.7. #define のエラー

次は #define に関するエラーです。 マクロは定義されません。

#, ## 演算子に関するエラーは Standard モードのものです。
`__VA_ARGS__` に関するエラーも Standard モードの場合です。 可変引数マクロは C99 の仕様ですが、GCC, Visual C++ 2005, 2008 との互換性のために C90 でも C++ でも有効としています（ただし、ウォーニングが出る）。

- `"defined" shouldn't be defined`
  defined という名前のマクロは定義できません。これは Standard モードでチェックされます。
- `"__STDC__" shouldn't be redefined`
  マクロ `__STDC__` は #define できません。`__STDC_VERSION__`, `__FILE__`, `__LINE__`, `__DATE__`, `__TIME__` も（C99 モードでの `__STDC_HOSTED__` と-+ オプションでの `__cplusplus` も）同様です。これは Standard モードでチェックされます。
- `"__VA_ARGS__" shouldn't be defined`
  C99 では可変引数マクロの定義の置換リスト中に `__VA_ARGS__` というパラメータが使われますが、この identifier はマクロとして定義することはできません。
- `More than NMACPARS parameters`
  マクロ定義でパラメータの数が NMACPARS を超えています。
- `Empty parameter`
  マクロ定義にカラのパラメータがあります。
- `Illegal parameter "123"`
  マクロ定義で identifier 以外のトークンがパラメータに使われています。 Standard モードでは、identifier であっても `__VA_ARGS__` はパラメータとして使えません。
- `Duplicate parameter name "a"`
  マクロ定義で a というパラメータ名が重複しています。
- `Missing "," or ")" in parameter list "(a,b"`
  マクロ定義でパラメータリストを閉じる ) がありません。またはパラメータの直後に ',' でも ')' でもない別のトークンがあります。
- `No token before ##`
  マクロ定義の置換リスト中の ## 演算子の前にトークンがありません。
- `No token after ##`
  マクロ定義の置換リスト中の ## 演算子の後にトークンがありません。
- `## after ##`
  マクロ定義の置換リスト中に ## ## というトークンの並びがあります。この定義はエラーではないという解釈もあるかもしれませんが、## というトークンが他のトークンと連結されると必ず valid でないトークンとなるので、このマクロの展開時には必ずエラーになります。mcpp ではこれはマクロ定義時にエラーにします。
- `Not a formal parameter "id"`
  関数様マクロの定義で # 演算子のオペランド id がパラメータ名ではありません。
- `"..." isn't the last parameter`
  "`...`" というパラメータはマクロ定義の最後のパラメータでなければなりません。（pre-Standard モードでは `...` は Illegal parameter エラーになる）。
- `"__VA_ARGS__" without corresponding "..."`
  置換リスト中の `__VA_ARGS__` という identifier は `...` というパラメータがある場合しか使えません。

GCC-specific-build の STD モードでは GCC2 仕様の可変引数マクロが使えますが、そのマクロ定義で `__VA_ARGS__` が使われていると、次のエラーになります。 `__VA_ARGS__` を使うなら GCC3 仕様か C99 仕様で書かなければなりません。

- `__VA_ARGS__ cannot be used in GCC2-spec variadic macro`

### 5.4.8. #undef のエラー

次は #undef に関するエラーです。

- `"__STDC__" shouldn't be undefined`
  マクロ `__STDC__` は #undef できません。 `__STDC_VERSION__`, `__FILE__`, `__LINE__`, `__DATE__`, `__TIME__` も（C99 モードでの `__STDC_HOSTED__` と -+ オプションでの `__cplusplus` も）同様です。これは Standard モードの場合にチェックされます。

### 5.4.9. マクロ展開のエラー

次はマクロ展開に関するエラーです。それらのマクロ定義も表示され、そのマクロ定義のあるソースファイル名と行番号も表示されます。#, ## 演算子に関するエラーは Standard モードだけです。

- `Less than necessary N argument(s) in macro call "macro( a)"`
  マクロ呼び出しの引数が足りません。macro には引数は N 個必要です。足りない引数には 0 個のトークンを割り当てて、処理を続けます。パラメータが１個で引数がゼロの場合はカラ引数と引数の欠落との区別がつきませんが、これはエラーにしません。OLDPREP モードではこれはウォーニングです。
- `More than necessary N argument(s) in macro call "macro( a, b, c)"`
  マクロ呼び出しの引数が多すぎます。macro の引数は N 個のはずです。余計な引数を捨てて、処理を続けます。OLDPREP モードではこれはウォーニングです。
- `Not a valid preprocessing token "+12"`
  ## 演算子によって２つの pp-token を連結したところ、"+12" という invalid なものになりました。これは後で切り離されるかもしれませんが、このまま処理を続けます。-lang-asm (-x assembler-with-cpp, -a) オプションでは、これはウォーニングです。
- `Not a valid string literal "\\"str\""`
  # 演算子によってマクロ呼び出しの引数を文字列化したところ、有効な（単一の）文字列リテラルとはならず、"\\"str\"" という token sequence になりました。リテラルの外に \ のあることが原因です（リテラルの外に \ がある引数を文字列化すると、Unterminated string literal のエラーになることもあり、何のエラーにならないこともある）。このまま処理を続けますが、たぶんコンパイルフェーズで再度エラーになるでしょう。POSTSTD モードではこのエラーは発生しません（Unterminated string literal はありうる）。

以下のエラーでは、そのマクロ呼び出しはスキップされます。

- `Buffer overflow expanding macro "macro" at "something"`
  マクロの展開中に something のところでバッファがオーバーフローしました。マクロを分割してください。
- `Unterminated macro call "macro( a, (b, c)"`
  マクロ呼び出しが完結していません。このエラーが起こるのはたいていは、ディレクティブ行でのマクロ呼び出しがその行で完結していない場合です。また、Standard モードでは引数中のマクロは引数の置換に先立って展開されますが、その時にそのマクロ呼び出しはその引数の中で完結していなければなりません。POSTSTD モードではマクロの置換リスト中にあるマクロ呼び出しが置換リストの中で完結していない場合も、このエラーとなります。
- `Rescanning macro "macro" more than RESCAN_LIMIT times at "something"`
  マクロのネストが深すぎて、展開中に "something" のところで再走査の回数が RESCAN_LIMIT を超えました。Standard モードの場合だけですが、まず起こりえません。
- `Recursive macro definition of "macro" to "macro"`
  マクロの定義が再帰的です。これは pre-Standard モードの時だけ起こりうるエラーです。再走査の回数が PRESTD_RESCAN_LIMIT に達すると再帰的マクロ定義とみなします。

次のエラーは STD モードで -K オプションを指定したときだけのものです。 ともに、マクロが極端に複雑なため、macro notification のためのバッファが足りなくなったことを意味しています。 実際にはまず起こり得ません。

- `Too many magics nested in macro argument`
- `Too many nested macros in tracing MACRO`

### 5.4.10. #error, #assert

- `#error`
  #error ディレクティブが実行されました。その #error 行が表示されます（引数そのものにトークンエラー（unterminated string 等）があった場合は #error は実行されない）。#error は Standard モードの場合だけです。
- `Preprocessing assertion failed:`
  #assert ディレクティブが実行されました。この言葉に続いて #assert 行の引数が出力されます。引数の式そのものにエラーがあった場合は、assertion が失敗したものとみなします。#assert は pre-Standard モードで COMPILER != GNUC の場合だけです。

### 5.4.11. #include の失敗

- `Can't open include file "file-name"`
  Include すべきファイルが存在しない場合にこのエラーが発生します。たぶんファイル名のスペルミスか include directory の指定の間違いでしょう。

### 5.4.12. その他のエラー

次の２つは Standard モードの C99 だけのものです。 C++ でも -V199901L オプションで起動した場合は同様です。

- `Operand of _Pragma() is not a string literal`
  _Pragma() operator の引数は１個の文字列リテラルまたはワイド文字列リテラルでなければなりません。
- `_Pragma operator found in directive line`
  _Pragma() operator は #pragma ディレクティブの代わりになるものです。 ディレクティブ行の中で使うことはできません。

## 5.5. Warning (class 1)

文法的には間違いではないが何かの書き間違いの可能性がある場合や portability の問題のある場合に、warning が出ます。Warning には 1, 2, 4, 8, 16 の５つの class があります。mcpp の起動時に -W <n> というオプションを指定することで、これらが有効になります。<n> は 1, 2, 4, 8, 16 のうちの任意のものの OR をとったものです。なお、以下の説明で -W4 等と言っているのは、-W<n> で <n> & 4 が真の場合のことで、1|4, 1|2|4, 2|4, 1|4|8, 4|8, 4|16 等を含みます。

Standard モードでは Standard C で undefined とされている動作を引き起こすソースの多くは error にしますが、一部については warning を出します。

同様に Standard モードでは Standard C で unspecified とされている仕様を使うソースに対しては、次の点以外は必ず warning を出します。

1. #if 式中の sub-expression の評価順序については、warning は出さない。||, &&, ? : 以外の演算子に関しては operand の評価順序は unspecified であるが、#if 式は副作用を生じないので、この評価順序は結果には影響しないからである。mcpp では、整数定数トークンの評価は常に出現と同時に左から右に行い、それらの間の演算は常に演算子のグルーピングの規則に従って、その項の値が必要になった時に初めて行う。

Standard モードでは、implementation-defined とされている動作の多くについても warning を出します。Implementation-defined でありながら warning の出ないのは、次の点だけです。

1. #include directive で include するファイルを探す場所、および #include の引数から header-name という pp-token を構築する方法。これに毎度 warning を出していたのではうるさい。header-name はマクロでなければ、ソースのトークンが space の有無も含めてそのまま使われる。マクロであれば、それを展開した結果が space の有無も含めてそのまま使われる（POSTSTD モードでは、マクロ展開によって pp-token 間に space が挿入されるが、その上で < から > までを space を削除してくっつけたものを header-name と解釈する。どちらにしても POSTSTD では <, > による header-name は obsolescent feature である）。Warning は出さないが、その代わりに、#pragma MCPP debug path, #debug path でサーチパスを表示する。
2. #if 式での single byte 文字定数（'a' 等）の評価と、単一の multi- byte character のワイド文字定数（L'字' 等）の評価。これは基本文字セットが同一であっても、single byte のカタカナとか、符号の有無とか、漢字の en-coding とかによって portability はごく限られるのであるが、キリがない。UCN についても同様である。
3. #if 式で負数がからむビット演算は整数の内部表現によって結果の値が異なるが、大半のマシンは２の補数の表現をとっているので、それを前提とすれば portability の問題はほとんど存在しない。ただし、負数の右ビットシフトおよび operand の片方または双方が負数の除算は portability が乏しいので、warning を出す。
4. Token separator としての複数の white spaces の sequence。Standard C では translation phase 3 でこれを one space に圧縮するかどうかは implementation-defined とされているが、通常はユーザはまったく気にする必要はない。Portability が問題になるのは、preprocessing directive 行に <vertical-tab> または <form-feed> がある場合だけである。mcpp ではこれらは space に変換するが、その時は warning を出す。複数の space, tab の sequence は one space に黙って圧縮する。
5. 処理系独自の組み込みマクロについては warning は出さない。
6. #pragma sub-directive についても原則として warning は出さない。mcpp 自身が処理する #pragma once, #pragma __setlocale, #pragma MCPP * で引数が間違っている場合は、warning を出す。また、GCC V.3 での #pragma GCC poison (dependency) のように、処理系付属のプリプロセッサは処理するが mcpp は処理しない #pragma についても warning を出す。
7. C99 では、UCN sequence が # 演算子によって文字列化される場合、\ を \\ というふうに重ねるかどうかは implementation-defined となっているが、これについては warning は出さない。mcpp では \ は重ねない。

したがって、mcpp ではプリプロセスのレベルでの portability のチェックをほぼ完全に行うことができます。

POSTSTD モードでは、 2.1 にある仕様の違いを除けば STD モードと同様です。

ウォーニングがいくつ出ても、「成功」の状態値を返します。-W0 のオプションで起動すると、ウォーニングは出ません。

### 5.5.1. 文字、トークンおよびコメントに関するウォーニング

- `Converted [CR+LF] to [LF]`
  改行コードを [CR+LF] から [LF] に変換しました。Windows 用のソースファイルを UNIX 系のシステムでコンパイルすると、このウォーニングが出て、改行コードが自動的に変換されます。このウォーニングは１回しか出ません。Compiler-independent-build ではクラス１で、compiler-specific-build ではクラス２です。
- `Illegal control character 0x1b in quotation`
  文字列リテラル、文字定数、header name 中に white space でないコントロールコードがあります。これらはコンパイラ本体でエラーになるかもしれません。そうでなくても、感心しないソースです。コントロールコードは文字列リテラル、文字定数では escape sequence で書くべきです。
- `Illegal multi-byte character sequence "XY" in quotation`
  文字列リテラル、文字定数、header name 中の XY の１バイト目は multi-byte character（漢字）の１バイト目ですが、２バイト目は multi-byte character の２バイト目ではありません（"XY" の表示は化けるはず）。これは multi-byte character とみなさず、１バイト目を single byte character として、２バイト目は次の文字として扱います。
  いわゆる外字も規定の範囲のコードであれば、ウォーニングは出ません。また、規定の範囲にも実際には文字のない穴がところどころにありますが、mcpp は通常はそこまではチェックしません。規定の範囲は次の通りです。

  > encoding | first byte | second byte
  > shift-JIS | 0x81-0x9f, 0xe0-0xfc | 0x40-0x7e, 0x80-0xfc
  > EUC-JP | 0x8e, 0xa1-0xfe | 0xa1-0xfe
  > KS C 5601 | 0xa1-0xfe | 0xa1-0xfe
  > GB 2312-80 | 0xa1-0xfe | 0xa1-0xfe
  > Big Five | 0xa1-0xfe | 0x40-0x7e, 0xa1-0xfe
  > ISO-2022-JP | 0x21-0x7e | 0x21-0x7e

  ISO-2022-JP には文字コードのほかに shift sequence というものがあります。Shift sequence を別にすると、UTF-8 以外はすべて multi-byte character は２バイトです。
  UTF-8 では multi-byte character は２バイトまたは３バイト、稀に４バイトであり、大半の漢字は３バイトで encode されます。 １バイト目は 0xc2-0xef、２バイト目以降は 0x80-0xbf の範囲にありますが、詳細は省略します。どちらにしても、各バイトはこの範囲になければなりません。 また、この範囲内にも illegal な sequence が存在しますが、mcpp はこれにウォーニングを出します。。
  なお、EUC-JP の 0x8f + 0xa1-0xfe + 0xa1-0xfe の３バイト encoding （JIS X 0212 のいわゆる補助漢字）は１文字としては認識できず、0x8f, 0xa1-0xfe + 0xa1-0xfe の２文字として認識されますが、その結果、ウォーニングも出ず、#if 式中のワイド文字定数の評価以外は、正しく動作します。EUC-JP では１バイト目が 0x8e の文字（JIS X 0201 のいわゆる半角カタカナ）は２バイト encoding であるので、multi-byte character として扱われます。
  このウォーニングはスキップされる #if group の中では出ません。
- `"/*" in comment`
  コメント中に /* という sequence があります。意図して書いたのでなければ、コメントの閉じ忘れでしょう。コメントはネストできません。
- `Too long identifier, truncated to "very_long_identifier"`
  Identifier の長さが IDMAX を超えているので、IDMAX に縮めました。
- `Illegal digit in octal number "089"`
  ８進の数値トークン中に 8 または 9 の文字があります。pre-Standard モードでだけ出ます。Standard モードでは通常の行の数値トークンについては正誤の判定はしません。#if 式にこのトークンがあれば、それは Not an integer エラーの一種となります。
- `Unterminated string literal, catenated to the next line`
  論理行中で閉じていない文字列リテラルは通常はエラーですが、-lang-asm (-x assembler-with-cpp, -a) オプションで起動された場合は、これは行をまたぐ文字列リテラルと解釈して、'\n' を挿入して次の行と連結します。こういうソースの書き方をするメリットは何もないので、「隣接する文字列リテラルの連結」の機能を使って書いてください。
- `Unterminated character constant 't understand.`
  文字定数が完結していません。 lang-asm モードではこれはエラーにはなりません。
- `Empty character constant ''`
  文字定数の中身がカラです。 lang-asm モードではこれはウォーニングですが、他のモードではエラーになります。

### 5.5.2. 完結しないソースファイルのウォーニング

ソースファイルの最後の行が中途半端である場合に、次のウォーニングが出ます。old_prep モードではウォーニングも出ません。

- `End of file with no newline, supplemented newline`
  ファイルが改行コードのない行で終わっていてはなりません。改行コードを補います。
- `End of file with \, deleted the \`
  ファイルが <backslash><newline> で終わっていてはなりません。<backslash> を削除します。
- `End of file with unterminated comment, terminated the comment`
  コメントの閉じ忘れです。コメントを閉じます。

次のウォーニングは pre-Standard モードでしか出ません（Standard モードではエラー）。入力の終わりでない場合は、これらのウォーニングを無視して処理を続けますが、その結果はさらに妙なエラーを引き起こすでしょう。OLDPREP モードでは unterminated macro call 以外はウォーニングさえも出ません。

- `End of file within #if (#ifdef) section started at line 123`
- `End of file within macro call started at line 123`
- `End of file with unterminated #asm block started at line 123`
  123 行の #asm に対応する #endasm がありません。

### 5.5.3. ディレクティブ行に関する各種のウォーニング

- `The macro is redefined`
  これに続いて以前の定義のあるファイル名と行番号が表示されます。
  マクロが以前とは違った内容で再定義されました。 ソースが整理されていないに違いありません。同じ名前のマクロの定義が重複している場合は、次の条件を満たしていないと再定義となり、このウォーニングが出ます。
  1. パラメータの数が同じ。
  2. 置換リストが同じ（ただし、トークン間の１個以上の white spaces はいくつあっても１つとみなす。POSTSTD では token separator があってもなくても自動的に a space に変換するので、token separator の違いは問題にならない）。
  3. STD モードではパラメータ名も同じ。POSTSTD ではこれはチェックしない。pre-Standard モードでもチェックしない。
- `Unknown argument "name"`
  #pragma MCPP debug, #debug の引数として "name" は実装されていません。
- `No argument`
  #pragma MCPP debug, #debug の引数がありません。
- `Not an identifier "123"`
  #pragma MCPP debug, #debug の引数が identifier ではありません。

次は Standard モードの場合だけです。

- `"and" is defined as macro`
  C++ で and がマクロとして定義されました。
  C95 では <iso646.h> でマクロとして定義される "and" 等の１１種の名前は、C++ では operator token です。C++ ではこれはマクロとして定義することができないのですが、これを operator として実装していない処理系でも使えるように、マクロとして定義することを許しながらウォーニングを出します。

次は STD モードの場合だけです。

- `No space between macro name "MACRO" and repl-text`
  #define 行のマクロ名と置換のリストとの間にスペースがありません。通常はありえないことですが、

      #define THIS$AND$THAT(a, b)     ((a) + (b))

  というふうにマクロ名に規格外の文字が使われていると、

      #define THIS    $AND$THAT(a, b) ((a) + (b))

  と解釈されて、このウォーニングが出ます。

次は lang-asm モードの場合だけです。

- `Illegal #directive "+"`
- `Unknown #directive "pseudo-directive"`
  これらは lang-asm モードでなければエラーになるところですが、lang-asm モードに限ってウォーニングが出るだけです。

以下の #pragma に関するウォーニングは Standard モードの場合だけです。ウォーニングは出てもその行は原則としてそのまま出力されますが、#pragma MCPP, #pragma GCC で始まる行のうち、プリプロセスで処理されるべきものは出力しません。 #pragma GCC visibility * のようにコンパイラやリンカのための行は、ウォーニングなしにそのまま出力されます。

- `No sub-directive`
  #pragma 行に何の引数もありません。この行は無視されます。
- `Unknown encoding "encoding"`
  #pragma __setlocale( "encoding") で指定された "encoding" という encoding は実装していません。encoding 名については 2.8 を見てください。
- `Too long encoding name "encoding"`
  #pragma __setlocale( "long-long-encoding") で指定された "long-long-encoding" という encoding 名は 19 バイトを超えているので、無視します。
- `Bad push_macro syntax`
- `Bad pop_macro syntax`
  #pragma MCPP push_macro, #pragma MCPP pop_macro, #pragma push_macro, #pragma pop_macro の構文が間違っています。これらの #pragma では引数はマクロ名を ", " で囲み、さらにそれを (, ) で囲んで、("MACRO") としなければなりません（Visual C 互換のための冗長な仕様である）。
- `"MACRO" has not been defined`
  #pragma MCPP push_macro, #pragma MCPP pop_macro, #pragma push_macro, #pragma pop_macro の引数である ("MACRO") の MACRO はそもそもマクロとして定義されていません。
- `"MACRO" is already pushed`
  #pragma MCPP push_macro( "MACRO") の MACRO はすでに push され、さらにその後で #undef されています。MACRO の再定義がないと push できません。
- `"MACRO" has not been pushed`
  #pragma MCPP pop_macro( "MACRO") の MACRO は push されていません。すでに pop されたのかもしれません。

GCC 版では次のウォーニングが出ます。

- `Ignored #ident`
- `Ignored #sccs`
  #ident, #sccs の行は無視します。

ただし、GCC 用では #pragma GCC に poison, dependency のどれかが続く行は class 2 のウォーニングを出したうえで捨てます。これは GCC V.3 ではプリプロセッサが処理するものですが、mcpp は処理しません。

次は pre-Standard モードで出ます（Standard モードではエラー）。

- `Not in a #if (#ifdef) section in a source file`
- `Line number "0x123" isn't a decimal digits sequence`

次は KR モードの場合と、Standard モードの #pragma once, #pragma MCPP push_macro, #pragma MCPP pop_macro, #pragma push_macro, #pragma pop_macro, #pragma __setlocale, #pragma setlocale, #pragma MCPP put_defines, #pragma MCPP debug, #pragma MCPP end_debug の場合、および GCC 用での STD モードの #endif 行の場合だけ出ます（Standard モードのその他の場合はエラー、OLDPREP ではエラーもウォーニングも出ない）。

- `Excessive token sequence "junk"`

### 5.5.4. #if 式に関するウォーニング

次の３つは #if, #elif, #assert の引数に関するウォーニングです。

- `Macro "MACRO" is expanded to "defined"`
  #if 式でマクロ MACRO を展開したところ defined になりました。これは identifier ではなく演算子として扱いますが、あやしげなマクロです（Standard C では undefined）。
- `Macro "MACRO" is expanded to "sizeof"`
  #if 式でマクロ MACRO を展開したところ sizeof になりました。これは identifier ではなく演算子として扱いますが、あやしげなマクロです。pre-Standard の場合です。
- `Macro "MACRO" is expanded to 0 token`
  マクロ MACRO が０個のトークンに展開されました。#if 式でこれが発生すると、たいていは何らかのエラーになります。このウォーニングはエラーの原因を明らかにするためのものです。

次も #if, #elif, #assert の引数に関するウォーニングですが、評価をスキップされる部分式では出ません（-W8 オプションでは出る）。

- `Undefined escape sequence '\x'`
  \x という escape sequence はありません。単なる \x という２バイトの sequence として評価します（\x に１６進文字列が続く escape sequence はもちろんある）。UCN のケタ数が足りない場合も同様です。

次は #if (#elif, #assert) 行の定数式の演算と型に関するウォーニングです。やはりスキップされる部分式に関しては出ません（-W8 オプションでは出る）。

Standard モードでは #if 式は C90, C++98 でも long long / unsigned long long で評価します。ただし、C90, C++98 で long / unsigned long の範囲を超えた場合はウォーニングを出します。LL という suffix についても同様に、C99 以外ではウォーニングが出ます。これらのウォーニングは compiler-independent-build では class 1 で、compiler-specific-build では class 2 です。
POSTSTD モードでは文字定数は #if 式には使えないので、ウォーニングも出ません（エラーになる）。

- `Constant "123456789012" is out of range of (unsigned) long`
  整数定数の値が (unsigned) long で表現できる範囲を超えています。
- `Integer character constant 'abcde' is out of range of unsigned long`
  文字定数 'abcde' の値が unsigned long で表現できる範囲を超えています。
- `Wide character constant L'abc' is out of range of unsigned long`
  ワイド文字定数 L'abc' の値が unsigned long で表現できる範囲を超えています。
- `Result of "op" is out of range of (unsigned) long`
  演算子 op による演算の結果が (unsigned) long の範囲外となりました。op は２項演算子 *, /, %, +, - のどれかです。整数の内部表現が２の補数の場合は、単項 - 演算子も -`LONG_MIN` でオーバーフローとなります。Unsigned long はオーバーフローすることがないのでエラーにはなりませんが、代数的な演算結果が範囲外となる場合はウォーニングが出ます。
- `LL suffix is used in other than C99 mode "123LL"`
  C99 以外のモードで LL という接尾子が使われています。
- `Shift count "40" is larger than bit count of long`
  ビットシフト演算子 <<, >> の右 operand の値が long のビット幅を超えています。
- `Negative value "-1" is converted to positive "18446744073709551615"`
  符号なしと符号つきとの混合演算の結果、符号つきの負数が符号なしの正数に変換されました。これはエラーではありませんが、たぶんソースの何らかの間違いでしょう。２項演算子 *, /, %, +, -, <, >, <=, >=, ==, !=, &, ^, | の両辺、および３項演算子 ? : の第２・第３ operand については、その片方が符号なしの場合は、他方は符号つきでも符号なしに変換されます。
- `Illegal shift count "-1"`
  ビットシフト演算子 <<, >> の右 operand の値が負数です。または long long のビット幅を超えています。これもソースの間違いでしょう。
- `"op" of negative number isn't portable`
  ２項演算子 op の結果は、operand の一方または双方が負数である場合は portability がありません。op は /, %, >> のどれかです。左 operand が負数の場合の >> 演算子は、算術シフト命令を持つ CPU 上の処理系の間では portability があるはずですが（１ビットのシフトで２で割った結果になる）、そうでない CPU 上の処理系との間では portability がありません。

### 5.5.5. マクロ展開に関するウォーニング

これらのウォーニングではそのマクロ定義が表示され、そのマクロ定義のあるソースファイル名と行番号も表示されます。

- `Macro started at line 123 swallowed directive-like line`
  123 行から始まったマクロが # で始まる行を引数として読み込みました。たぶん、マクロ呼び出しの間違いでしょう。マクロがなければ、# で始まる行は directive line として解釈されるはずです。スキップされる #if group ではマクロがあっても展開されないので、この行は directive line として解釈されます。
- `Replacement text "sub(" of macro "head" involved subsequent text`
  マクロ "head" の置換リスト "sub(" の再走査でマクロ呼び出しの後ろのテキストが取り込まれました。これは K&R 1st. から Standard C に至るまでエラーではありませんが、もし意図せずにこの種のマクロを使ってこのウォーニングが出たなら、それはマクロ定義またはマクロ呼び出しの間違いです。意図して使ったのであれば異常なマクロです。
  このウォーニングは STD モードの場合だけです。COMPAT モードではこのウォーニングは class 8 でしか出ません。pre-Standard モードでも同じ現象は起こりますが、ウォーニングは出ません。POSTSTD では再走査で置換リストの後ろのテキストは取り込まないので、このウォーニングは決して出ません（unterminated macro call のエラーになる場合と、まったく違った展開結果になる場合とある）。
- `Less than necessary N argument(s) in macro call "macro( a)"`
  マクロ呼び出しの引数が足りません。通常はこれはエラーですが、可変引数マクロで引数が一つだけ足りなかった場合はウォーニングにとどめています。GCC の可変引数マクロと C99 のそれとの間の移植上の障害を減らすためです。
- `Removed ',' preceding the absent variable argument`
  GCC3 仕様の可変引数マクロの呼び出しで可変引数が 0 個であるため、置換リストで可変引数の直前にあるコンマを削除しました。Standard モードの GCC-specific-build の場合だけです。
- `Old style predefined macro "linux" is used`
  '_' で始まらない規格違反の事前定義マクロが使われました。 Standard モードの GCC-specific-build の場合だけです。

次の２つは OLDPREP モードだけです（他のモードではエラー）。

- `Less than necessary N argument(s) in macro call "macro( a)"`
- `More than necessary N argument(s) in macro call "macro( a, b, c)"`

### 5.5.6. 行番号に関するウォーニング

次は行番号に関するウォーニングです。

- `Line number "32768" is out of range of [1,32767]`
  C90, C++ では #line の第一引数は [1,32767] の範囲になければなりません。0 もエラーです。`__STDC_VERSION__` >= 199901L または `__cplusplus` >= 199901L の場合はこの引数の有効範囲は [1,2147483647] です。したがって、C90, C++ では [32768,2147483647] の範囲はエラーではなくウォーニングにとどめています。
  Standard モードの場合です。

C90 では #line で 32767 よりは小さいがそれに近い番号を指定した場合、その時点ではエラーにならないものの、いずれこの範囲をオーバーします。オーバーした場合、mcpp では warning を出した上で行番号をそのまま増やし続けていきますが、しかし、コンパイラ本体によってはこれを受け取れないかもしれません。#line の指定がむやみに大きいことが問題です。

- `Line number 32768 got beyond range`
  ソースの行番号が 32768 に達しました。その時点で１回だけ warning が出ます。
- `Line number 32769 is out of range`
  マクロ `__LINE__` を展開したところ、32767 を超えました。

### 5.5.7. #pragma MCPP warning (#warning)

- `#warning`
- `#pragma MCPP warning`
  #pragma MCPP warning (#warning) ディレクティブが実行されました。その行が表示されます（引数そのものにトークンエラー（Unterminated string 等）があった場合は #pragma MCPP warning は実行されない）。このディレクティブは便宜上 warning level 1 のところに掲載していますが、実際には warning level に関係なく必ず表示されます。
  #pragma MCPP warning は Standard モードの場合で、pre-Standard モードでは #warning です。

## 5.6. Warning (class 2)

間違いではないが portability に問題のあるケースについてのウォーニングです。

- `Converted [CR+LF] to [LF]`
  改行コードを [CR+LF] から [LF] に変換しました。このウォーニングは compiler-independent-build ではクラス１で、compiler-specific-build ではクラス２です。

#if 式は Standard モードでは C90, C++98 でも long long / unsigned long long で評価します。ただし、C90, C++98 で long / unsigned long の範囲を超えた場合はウォーニングを出します。LL という suffix についても同様に、C99 以外ではウォーニングが出ます。Visual C, Borland C の compiler-specific-build では I64 という suffix が使えますが、これについても同様です。これらのウォーニングは compiler-independent-build では class 1 で、compiler-specific-build では class 2 です。

- `Constant "123456789012" is out of range of (unsigned) long`
- `Integer character constant 'abcde' is out of range of unsigned long`
- `Wide character constant L'abc' is out of range of unsigned long`
- `Result of "op" is out of range of (unsigned) long`
- `LL suffix is used in other than C99 mode "123LL"`
- `I64 suffix is used in other than C99 mode "123i64"`
- `Shift count "40" is larger than bit count of long`

次の５つは Standard モードの場合だけです。

- `Parsed "//" as comment`
  // から行末までをコメントとして解釈します。C99 および C++ では合法ですが、C90 でもウォーニングを出した上でコメントとして扱います。
- `Variable argument macro is defined`
  可変引数マクロは C99 の仕様です。C90, C++ で可変引数マクロが定義されました。
- `Empty argument in macro call "MACRO( a, ,"`
  マクロ呼び出しにカラの引数があります。mcpp ではその引数は０個の pp-token sequence であるとみなして reasonable な処理をします。しかし、カラ引数は C99 では合法であるものの C90 では undefined であり、portability がありません（',' さえもないのはカラ引数ではなく引数がないとみなし、エラーとする。０個の引数と１個のカラ引数とは構文上、区別がつかないが、どちらであってもエラーにはしない）。ソースにカラ引数を書くのは一般には良いスタイルではありません。可能な場合は、

      #define EMPTY

  等として、この `EMPTY` をカラ引数の部分に書くのが良いでしょう。
- `Skipped the #pragma line`
  GCC V.3 では #pragma GCC <args> という形の pragma があり、その中にはプリプロセッサが処理するものもありますが、mcpp は #pragma GCC poison および dependency はサポートしていません。
  処理系付属のプリプロセッサは処理するが mcpp は処理しない#pragma については、このウォーニングが出ます。
- `Not a valid preprocessing token "+12"`
  ## 演算子によって２つの pp-token を連結したところ、"+12" という invalid なものになりました。通常はエラーですが、-lang-asm (-x assembler-with-cpp, -a) オプションで起動された場合はエラーにはなりません。

次は POSTSTD モードの場合だけです。

- `Header-name enclosed by <, > is an obsolescent feature <stdio.h>`
  <stdio.h> という形の header name は廃止したい仕様です。"stdio.h" を使ってください。

次の２つのウォーニングは特定のシステムだけのものです。それらのシステムでは正しいプログラムですが、portability がないので、念のためにウォーニングを出します。

- `#include_next is not allowed by Standard`
- `#warning is not allowed by Standard`
  これらのディレクティブは GCC では有効ですが、規格外のものであり、portability がありません。
- `GCC2-spec variadic macro is defined`
  GCC2 仕様の可変引数マクロが定義されました。 GCC-specific-build では使えますが、このマクロには portability がありません。
- `Converted \ to /`
  #include directive の header name 中に \ が含まれているので、これを / に変換して処理します。これは Windows 等のＯＳでは正規の path-delimiter ですが、規定では undefined です。/ を使ったほうが間違いがありません。このウォーニングは１回しか出ません。Windows 上で動く mcpp だけのものです（ただし、\ が " の直前にある場合はこの " を文字列リテラルの delimiter とは解釈しないので、unterminated string literal のエラーとなる）。
- `'$' in identifier "THIS$AND$THAT"`
  Identifier 中に '$' が含まれています。このウォーニングは１回しか出ません。DOLLAR_IN_NAME を TRUE にしてコンパイルされた mcpp でだけ出ます。それらのシステムでは '$' は identifier 中の有効な文字ですが、portability はありません。他のシステムでは '$' は１文字だけの pp-token となるので、THIS$AND$THAT は THIS $ AND $ THAT の５つの pp-token に分解されます（その結果、compile phase でエラーになるはずである）。

## 5.7. Warning (class 4)

Standard C ではいくつかの translation limits について、最低限保証すべき値を規定しています。プリプロセッサはこの値を超えた translation limits を持っているほうが仕様が良いとも言えますが、しかしそれに依存するソースは portability が制限されます。mcpp ではこれらの translation limits は system.H のマクロを定義することで任意に設定できるようになっていますが、Standard モードではこの値が Standard C の最小値を超えている場合は、そのことを利用するソースに対してはウォーニングを出します。しかし、処理系の標準ヘッダやソースによっては頻発する結果になるので、class 1, 2 から外してあります。

- `Logical source line longer than 509 bytes`
  ソースの論理行の長さが 509 バイトを超えています。
- `Quotation longer than 509 bytes "very_very_long_string"`
  文字列リテラル、文字定数、header name の長さが 509 バイトを超えています。
- `More than 8 nesting of #include`
  #include のネストが８レベルを超えました。９レベルになった時だけこのウォーニングが出ます。
- `More than 8 nesting of #if (#ifdef) sections`
  #if, #ifdef, #ifndef のネストが８レベルを超えました。９レベルになった時だけこのウォーニングが出ます。
- `More than 1024 macros defined`
  定義されているマクロが 1025 個に達しました。この数には pre-defined マクロも header-file で定義されたマクロも含まれています。
- `String literal longer than 509 bytes "very_very_long_string"`
  # 演算子を使って定義されたマクロの展開によって、509 バイトを超える長さの文字列リテラルが生成されました。

次のウォーニングはスキップされる #if group では出ません。

- `More than 32 nesting of parens in #if expression`
  #if 式のカッコのネストが３２レベルを超えました。３３レベルになった時だけ出ます。
- `More than 31 parameters`
  マクロ定義のパラメータの数が３１を超えました。
- `Identifier longer than 31 bytes "very_very_long_name"`
  Identifier の長さが３１バイトを超えています。

`__STDC_VERSION__` >= 199901L の場合はこれらの translation limits は次の通りです。Identifier の長さでは、UCN と multi-byte-character はそれぞれ１文字と数えます（ソースのバイト数ではない。奇妙な規定である）。

> ソースの論理行の長さ | 4095 バイト
> 文字列リテラル、文字定数、header name の長さ | 4095 バイト
> Identifier の長さ | 63 文字
> #include のネスト | 15 レベル
> #if, #ifdef, #ifndef のネスト | 63 レベル
> #if 式のカッコのネスト | 63 レベル
> マクロのパラメータの数 | 127 個
> 定義できるマクロの数 | 4095 個

-+ オプションで C++ のプリプロセスを指定した時は、次のような translation limits とします。ただし、マクロのパラメータの最大数は mcpp では 255 までしか実装できないので、256 個ではエラーとなります。

> ソースの論理行の長さ | 65536 バイト
> 文字列リテラル、文字定数、header name の長さ | 65536 バイト
> Identifier の長さ | 1024 文字
> #include のネスト | 256 レベル
> #if, #ifdef, #ifndef のネスト | 256 レベル
> #if 式のカッコのネスト | 256 レベル
> マクロのパラメータの数 | 256 個
> 定義できるマクロの数 | 65536 個

次のウォーニングも実際にはうるさいので、class 1, 2 から外してあります。

- `Converted 0x0c to a space`
  ソース中の token separator としての [FF], [VT], [CR] （[CR][LF] ではない単独の [CR]）のコードは space に変換します。これが directive 行にある場合は Standard C では undefined です。コメント、文字列リテラル、文字定数中のコードは変換しません（変換してもよいのであるが、文字セットは処理系依存なので、mcpp ではあまり制約を課さない。必要なチェックはコンパイラ本体で行われるであろう）。他方で、token separator としての [TAB] も a space に変換しますが、これはコンパイル結果に何の影響も与えないので（プリプロセッサにとってもコンパイラ本体にとっても [TAB] は space と同じ意味しか持たない）、ウォーニングは出しません。[FF] は実際のソースに時々見られるものです。「改ページ」を意図しているのでしょうが、感心しないスタイルです。
- `Undefined symbol "name", evaluated to 0`
  #if 行で identifier "name" はマクロとして値が定義されていません。0 と評価します。これは決してエラーではありませんが、プログラムの間違いである可能性があります。#if defined の引数にはこのウォーニングは出ません。#if name .. とするところを #if defined name && (name ..) とするか、または mcpp を起動する時に -D name=0 というオプションを付けることで、このウォーニングを避けることができます。C++ では true, false というトークンは特別扱いで、ウォーニングなしにそれぞれ 1, 0 と評価します。
- `Multi-character wide character constant L'ab' isn't portable`
  ワイド文字定数の値は、同じ基本文字セットの処理系間でさえもワイド文字の encoding が処理系依存であり、その上、Multi-character の評価の仕方も処理系依存なので、これを使った #if 式は移植性がありません。STD モードでだけ出ます。POSTSTD では #if 式中の文字定数は認めないのでエラーになります。次のものも同様です。
- `Multi-character or multi-byte character constant '字' isn't portable`
  Multi-character character constant と multi-byte character character constant の値の評価の仕方は処理系依存なので、これを使った #if 式は移植性がありません。STD モードで出ます。

次の２つは Standard モードの場合だけ出ます。

- `Macro with mixing of ## and # operators isn't portable`
  関数様マクロ定義の置換リスト中に ## # というトークンの並びがありますが、この２つの演算子の優先順位は Standard C では unspecified なので、移植性がありません。mcpp では # を先に適用します。なお、関数様マクロ定義で逆向きの # ## というトークンの並びがあるとエラーになります。# 演算子のオペランドはパラメータでなければならないからです。
- `Macro with multiple ## operators isn't portable`
  マクロ定義の置換リスト中に複数の ## 演算子が間に１つのトークンまたはパラメータだけをはさんでありますが、## 演算子の評価順序は Standard C では unspecified なので、このマクロは移植性のない場合があります。mcpp では ## 演算子は左から右へ順に適用していきます。

次は STD モードで -k または -K オプションを指定したときだけのものです。

- `Too long comment, discarded up to here`
  コメントが極端に長いので、ここまでの部分を割愛します。 行をまたぐコメントが 256 行以上になると、そこまでを捨てて、次行を１行目としてカウントします。 長大なドキュメントをソースに含めるのは、良いスタイルではありません。

## 5.8. Warning (class 8)

ソースの間違いである可能性は少ないが念のために注意を促す意味で、このメッセージが出ます。これをチェックするのは -W8 のオプションで起動された場合だけです。

スキップされる #if group の中の preprocessing directive は通常は #if, #ifdef, #ifndef, #elif, #else, #endif の対応関係しかチェックしませんが、-W8 では Illegal directive, Unknown directive のチェックもします。また、Standard モードでは #if のネストが８レベルを超えた場合もウォーニングを出します。

- `Illegal #directive "+" (in skipped block)`
- `Unknown #directive "pseudo-directive" (in skipped block)`
- `Ignored #ident (in skipped block)`
- `Ignored #sccs (in skipped block)`
- `More than 8 nesting of #if (#ifdef) sections (in skipped block)`
- `#include_next is not allowed by Standard (in skipped block)`
- `#warning is not allowed by Standard (in skipped block)`

次は #if 式に関するウォーニングです。例えば #if a || b という式では、a が真であれば b の評価は行われません。しかし、-W8 として起動すると、評価されない部分式に関してもこれらのウォーニングが出されます。この場合はいずれも (in non-evaluated sub-expression) というただし書きが付けられます。

- `Constant "123456789012345678901" is out of range`
- `Constant "123456789012" is out of range of (unsigned) long`
- `LL suffix is used in other than C99 mode "123LL"`
- `I64 suffix is used in other than C99 mode "123i64"`
- `Shift count "40" is larger than bit count of long`
- `Integer character constant 'abcdefghi' is out of range`
- `Integer character constant 'abcde' is out of range of unsigned long`
- `Wide character constant L'abcdef' is out of range`
- `Wide character constant L'abc' is out of range of unsigned long`
- `8 bits can't represent escape sequence 'x123'`
- `16 bits can't represent escape sequence L'x12345'`
- `Division by zero`
- `Undefined symbol "name", evaluated to 0`
- `sizeof: Unknown type "type"`
- `sizeof: Illegal type combination with "type"`
- `Multi-character wide character constant L'ab' isn't portable`
- `Multi-character or multi-byte character constant '字' isn't portable`
- `Undefined escape sequence '\x'`
- `UCN cannot specify the value "0000007f"`
- `Negative value "-1" is converted to positive "18446744073709551615"`
- `Result of "op" is out of range`
- `Result of "op" is out of range of (unsigned) long`
- `Illegal shift count "-1"`
- `"op" of negative number isn't portable`
- `sizeof is disallowed in C Standard`
  #if sizeof が実装されるのは pre-Standard モードだけですが、その場合でもこれは Standard C では使えませんよというお節介をします。
- `"MACRO" wasn't defined`
  #undef に定義されていない名前を指定しています。これは少なくとも Standard C ではエラーではありません。
- `Macro "macro" needs arguments`
  引数付きマクロとして定義されているものと同じ名前が単独で現れています。展開せず、そのまま残します。pre-Standard モードの場合だけ出ます（Standard モードでは何ら問題ないので、ウォーニングは出ない）。
- `Replacement text "sub(" of macro "head" involved subsequent text`
  マクロ "head" の置換リスト "sub(" の再走査でマクロ呼び出しの後ろのテキストが取り込まれました。このウォーニングは STD モードでは class 1 ですが、COMPAT モードでは class 8 です。

## 5.9. Warning (class 16)

Trigraph と digraph は使う必要のない環境ではまったく使わないものです。その環境でもしこれらが検出されれば、注意を要するでしょう。-W16 オプションはこれを検出するものです。他方で、trigraph あるいは digraph が常用されているソースではこのウォーニングが頻発することになってうるさいでしょうから、これを他のウォーニングとは別のクラスにしてあります。どちらにしても、これらは trigraph あるいは digraph が有効な状態でだけ検出されます。Digraph は Standard モードの場合で、trigraph は STD モードだけです。

- `2 trigraph(s) converted`
  この物理行中の２つの trigraph sequence を変換しました。本当に trigraph のつもりで書いたのでしょうか？
- `2 digraph(s) converted`
  この行中の２つの digraph sequence を変換しました。本当に digraph のつもりで書いたのでしょうか？
  STD モードでは digraph 処理のできない処理系では、プリプロセスが終わってから digraph を次のように通常の token に変換して出力します。

      <%  ->  {           <:  ->  [           %:      ->  #
      %>  ->  }           :>  ->  [           %:%:    ->  ##

  しかし、POSTSTD モードでは translation phase 1 で通常の pp-token に変換してしまいます。この違いは、digraph が # 演算子による文字列化の対象になった時に現れます。デフォルトでは digraph sequence のまま文字列化しますが、POSTSTD では通常の pp-token に変換されたものが文字列化されます。また、文字列リテラルの中に digraph sequence に相当する character sequence があった場合、デフォルトではそのままですが、POSTSTD ではこれも対応する pp-token の character sequence に変換されます。
  STD モードではこのウォーニングは digraph の「変換」を対象としているので、preprocessing-directive 行に現れて消えてしまう digraph はカウントされません。

## 5.10. 診断メッセージ索引

診断メッセージ | Fatal error | Error | Warning class
--- | --- | --- | ---
| 1 | 2 | 4 | 8 | 16
`"..." isn't the last parameter` |  | 5.4.7
`"/*" in comment` |  |  | 5.5.1
`"and" is defined as macro` |  |  | 5.5.3
`"defined" shouldn't be defined` |  | 5.4.7
`"MACRO" has not been defined` |  |  | 5.5.3
`"MACRO" has not been pushed` |  |  | 5.5.3
`"MACRO" is already pushed` |  |  | 5.5.3
`"MACRO" wasn't defined` |  |  |  |  |  | 5.8
`"op" of negative number isn't portable` |  |  | 5.5.4 |  |  | 5.8
`"__STDC__" shouldn't be redefined` |  | 5.4.7
`"__STDC__" shouldn't be undefined` |  | 5.4.8
`"__VA_ARGS__" without corresponding "..."` |  | 5.4.7
`"__VA_ARGS__" cannot be used in GCC2-spec variadic macro` |  | 5.4.7
`## after ##` |  | 5.4.7
`#error` |  | 5.4.10
`#include_next is not allowed by Standard` |  |  |  | 5.6 |  | 5.8
`#warning` |  |  | 5.5.7
`'$' in identifier "THIS$AND$THAT"` |  |  |  | 5.6
`16 bits can't represent escape sequence L'\x12345'` |  | 5.4.6 |  |  |  | 5.8
`2 digraph(s) converted` |  |  |  |  |  |  | 5.9
`2 trigraph(s) converted` |  |  |  |  |  |  | 5.9
`8 bits can't represent escape sequence '\x123'` |  | 5.4.6 |  |  |  | 5.8
`_Pragma operator found in directive line` |  | 5.4.12
`Already seen #else at line 123` |  | 5.4.3
`Bad defined syntax` |  | 5.4.5
`Bad pop_macro syntax` |  |  | 5.5.3
`Bad push_macro syntax` |  |  | 5.5.3
`Buffer overflow expanding macro "macro" at "something"` |  | 5.4.9
`Buffer overflow scanning token "token"` | 5.3.3
`Bug:` | 5.3.1
`Can't open include file "file-name"` |  | 5.4.11
`Can't use a character constant 'a'` |  | 5.4.5
`Can't use a string literal "string"` |  | 5.4.5
`Can't use the character 0x24` |  | 5.4.5
`Can't use the operator "++"` |  | 5.4.5
`Constant "123456789012" is out of range of (unsigned) long` |  |  | 5.5.4 | 5.6 |  | 5.8
`Constant "1234567890123456789012" is out of range` |  | 5.4.6 |  |  |  | 5.8
`Converted 0x0c to a space` |  |  |  |  | 5.7
`Converted [CR+LF] to [LF]` |  |  | 5.5.1 | 5.6
`Converted \ to /` |  |  |  | 5.6
`Division by zero` |  | 5.4.6 |  |  |  | 5.8
`Duplicate parameter names "a"` |  | 5.4.7
`Empty argument in macro call "MACRO( a, ,"` |  |  |  | 5.6
`Empty character constant ''` |  | 5.4.1 | 5.5.1
`Empty parameter` |  | 5.4.7
`End of file with no newline, supplemented the newline` |  |  | 5.5.2
`End of file with unterminated #asm block started at line 123` |  | 5.4.2 | 5.5.2
`End of file with unterminated comment, terminated the comment` |  |  | 5.5.2
`End of file with \, deleted the \` |  |  | 5.5.2
`End of file within #if (#ifdef) section started at line 123` |  | 5.4.2 | 5.5.2
`End of file within macro call started at line 123` |  | 5.4.2 | 5.5.2
`Excessive ")"` |  | 5.4.5
`Excessive token sequence "junk"` |  | 5.4.4 | 5.5.3
`File read error` | 5.3.2
`File write error` | 5.3.2
`Header-name enclosed by <, > is an obsolescent feature` |  |  |  | 5.6
`GCC2-spec variadic macro is defined` |  |  |  | 5.6
`I64 suffix is used in other than C99 mode "123i64"` |  |  |  | 5.6 |  | 5.8
`Identifier longer than 31 bytes "very_very_long_name"` |  |  |  |  | 5.7
`Ignored #ident` |  |  | 5.5.3 |  |  | 5.8
`Ignored #sccs` |  |  | 5.5.3 |  |  | 5.8
`Illegal #directive "123"` |  | 5.4.4 | 5.5.3 |  |  | 5.8
`Illegal control character 0x1b in quotation` |  |  | 5.5.1
`Illegal control character 0x1b, skipped the character` |  | 5.4.1
`Illegal digit in octal number "089"` |  |  | 5.5.1
`Illegal multi-byte character sequence "XY" in quotation` |  |  | 5.5.1
`Illegal multi-byte character sequence "XY"` |  | 5.4.1
`Illegal parameter "123"` |  | 5.4.7
`Illegal shift count "-1"` |  |  | 5.5.4 |  |  | 5.8
`Illegal UCN sequence` |  | 5.4.1
`In #asm block started at line 123` |  | 5.4.3
`Integer character constant 'abcde' is out of range of unsigned long` |  |  | 5.5.4 | 5.6 |  | 5.8
`Integer character constant 'abcdefghi' is out of range` |  | 5.4.6 |  |  |  | 5.8
`Less than necessary N argument(s) in macro call "macro( a)"` |  | 5.4.9 | 5.5.5
`Line number "0x123" isn't a decimal digits sequence` |  | 5.4.4 | 5.5.6
`Line number "2147483648" is out of range of 1,2147483647` |  | 5.4.4
`Line number "32768" got beyond range` |  |  | 5.5.6
`Line number "32768" is out of range of 1,32767` |  |  | 5.5.6
`Line number "32769" is out of range` |  |  | 5.5.6
`LL suffix is used in other than C99 mode "123LL"` |  |  | 5.5.4 | 5.6 |  | 5.8
`Logical source line longer than 509 bytes` |  |  |  |  | 5.7
`Macro "MACRO" is expanded to "defined"` |  |  | 5.5.4
`Macro "MACRO" is expanded to "sizeof"` |  |  | 5.5.4
`Macro "MACRO" is expanded to 0 token` |  |  | 5.5.4
`Macro "macro" needs arguments` |  |  |  |  |  | 5.8
`Macro started at line 123 swallowed directive-like line` |  |  | 5.5.5
`Macro with mixing of ## and # operators isn't portable` |  |  |  |  | 5.7
`Macro with multiple ## operators isn't portable` |  |  |  |  | 5.7
`Misplaced ":", previous operator is "+"` |  | 5.4.5
`Misplaced constant "12"` |  | 5.4.5
`Missing ")"` |  | 5.4.5
`Missing "," or ")" in parameter list "(a,b"` |  | 5.4.7
`More than 1024 macros defined` |  |  |  |  | 5.7
`More than 31 parameters` |  |  |  |  | 5.7
`More than 32 nesting of parens in #if expression` |  |  |  |  | 5.7
`More than 8 nesting of #if (#ifdef) sections` |  |  |  |  | 5.7 | 5.8
`More than 8 nesting of #include` |  |  |  |  | 5.7
`More than BLK_NEST nesting of #if (#ifdef) sections` | 5.3.3
`More than INCLUDE_NEST nesting of #include` | 5.3.3
`More than necessary N argument(s) in macro call "macro( a, b, c)` |  | 5.4.9
`More than NEXP*2-1 constants stacked at "12"` |  | 5.4.5
`More than NEXP*3-1 operators and parens stacked at "+"` |  | 5.4.5
`More than NMACPARS parameters` |  | 5.4.7
`Multi-character or multi-byte character constant '字' isn't portable` |  |  |  |  | 5.7 | 5.8
`Multi-character wide character constant L'ab' isn't portable` |  |  |  |  | 5.7 | 5.8
`Negative value "-1" is converted to positive "18446744073709551615"` |  |  | 5.5.4 |  |  | 5.8
`No argument` |  | 5.4.4 | 5.5.3
`No header name` |  | 5.4.4
`No identifier` |  | 5.4.4
`No line number` |  | 5.4.4
`No space between macro name "MACRO" and repl-text` |  |  | 5.5.3
`No sub-directive` |  |  | 5.5.3
`No token after ##` |  | 5.4.7
`No token before ##` |  | 5.4.7
`Not a file name "name"` |  | 5.4.4
`Not a formal parameter "id"` |  | 5.4.7
`Not a header name "UNDEFINED_MACRO"` |  | 5.4.4
`Not a line number "name"` |  | 5.4.4
`Not a valid preprocessing token "+12"` |  | 5.4.9 |  | 5.6
`Not a valid string literal` |  | 5.4.9
`Not an identifier "123"` |  | 5.4.4 | 5.5.3
`Not an integer "1.23"` |  | 5.4.5
`Not in a #if (#ifdef) section` |  | 5.4.3
`Not in a #if (#ifdef) section in a source file` |  | 5.4.3 | 5.5.3
`Old style predefined macro "linux" is used` |  |  | 5.5.5
`Operand of _Pragma() is not a string literal` |  | 5.4.12
`Operator ">" in incorrect context` |  | 5.4.5
`Out of memory (required size is 0x1234 bytes)` | 5.3.2
`Parsed "//" as comment` |  |  |  | 5.6
`Preprocessing assertion failed` |  | 5.4.10
`Quotation longer than 509 bytes "very_very_long_string"` |  |  |  |  | 5.7
`Recursive macro definition of "macro" to "macro"` |  | 5.4.9
`Removed ',' preceding the absent variable argument` |  |  | 5.5.5
`Replacement text "sub(" of macro "head" involved subsequent text` |  |  | 5.5.5 |  |  | 5.8
`Rescanning macro "macro" more than RESCAN_LIMIT times at "something"` |  | 5.4.9
`Result of "op" is out of range` |  | 5.4.6 |  |  |  | 5.8
`Result of "op" is out of range of (unsigned) long` |  |  | 5.5.4 | 5.6 |  | 5.8
`Shift count "40" is larger than bit count of long` |  |  | 5.5.4 | 5.6 |  | 5.8
`sizeof is disallowed in C Standard` |  |  |  |  |  | 5.8
`sizeof: Illegal type combination with "type"` |  | 5.4.6 |  |  |  | 5.8
`sizeof: No type specified` |  | 5.4.5
`sizeof: Syntax error` |  | 5.4.5
`sizeof: Unknown type "type"` |  | 5.4.6 |  |  |  | 5.8
`Skipped the #pragma line` |  |  |  | 5.6
`String literal longer than 509 bytes "very_very_long_string"` |  |  |  |  | 5.7
`The macro is redefined` |  |  | 5.5.4
`This is not a preprocessed source` | 5.3.4
`This preprocessed file is corrupted` | 5.3.4
`Too long comment, discarded up to here` |  |  |  |  | 5.7
`Too long header name "long-file-name"` | 5.3.3
`Too long identifier, truncated to "very_long_identifier"` |  |  | 5.5.1
`Too long line spliced by comments` | 5.3.3
`Too long logical line` | 5.3.3
`Too long number token "12345678901234"` | 5.3.3
`Too long pp-number token "1234toolong"` | 5.3.3
`Too long quotation "long-string"` | 5.3.3
`Too long source line` | 5.3.3
`Too long token` | 5.3.3
`Too many magics nested in macro argument` |  | 5.4.9
`Too many nested macros in tracing MACRO` |  | 5.4.9
`UCN cannot specify the value "0000007f"` |  | 5.4.1 |  |  |  | 5.8
`Undefined escape sequence '\x'` |  |  | 5.5.4 |  |  | 5.8
`Undefined symbol "name", evaluated to 0` |  |  |  |  | 5.7 | 5.8
`Unknown #directive "pseudo-directive"` |  | 5.4.4 | 5.5.4 |  |  | 5.8
`Unknown argument "name"` |  |  | 5.5.3
`Unterminated character constant 't understand.` |  | 5.4.1
`Unterminated expression` |  | 5.4.5
`Unterminated header name` |  | 5.4.1
`Unterminated macro call "macro( a, (b,c)"` |  | 5.4.9
`Unterminated string literal` |  | 5.4.1
`Unterminated string literal, catenated to the next line` |  |  | 5.5.1
`Variable argument macro is defined` |  |  |  | 5.6
`Wide character constant L'abc' is out of range of unsigned long` |  |  | 5.5.4 | 5.6 |  | 5.8
`Wide character constant L'abc' is out of range` |  | 5.4.6 |  |  |  | 5.8

# 6. バグ報告等

プリプロセスの Standard C 適合度を検証するための Validation Suite を mcpp のソースとともに公開しています。Standard C のプリプロセスのすべての規定を検証できるものにしたつもりです。もちろん、mcpp はこれを使ってチェックしてあります。それも多くの処理系でコンパイルしてチェックしてあります。したがって、バグや誤仕様はほとんどないつもりですが、しかし、まだいくつか残っている恐れは十分あります。

もし、不可解な動作が発見されたら、ぜひご報告ください。
もし、 "Bug: ..." という診断メッセージが出たら、それは間違いなく mcpp または処理系の（たぶん mcpp の）バグです。また、たとえむちゃくちゃな「ソース」でも、それを食わせることで mcpp が暴走するなら、それもバグです。

バグ報告には次のようなデータを付けてくださるようお願いします。

1. mcpp を移植した処理系。
2. バグと思われるものを再現できるなるべく短いサンプルソース。
3. その処理結果。

バグ報告のほかにも、mcpp の使い勝手、診断メッセージ、このドキュメントの書き方、などについてご意見をお寄せください。
ご意見と情報は

http://mcpp.sourceforge.net/

の "Open Discussion Forum" またはメールでお願いします。