M C P P - M A N U A L . T X T == How to use MCPP == 松井 潔 kmatsui@t3.rim.or.jp V.2.0 1998/08 First released. kmatsui V.2.1 1998/09 Updated according to C99 1998/08 draft. kmatsui V.2.2 1998/11 Updated according to C++98 Standard. kmatsui V.2.3 prerelease 1 2002/08 Updated according to C99 Standard. Added porting to Linux / GNU C, CygWIN and LCC-Win32. GNU C-compatible features augmented. kmatsui V.2.3 prerelease 2 2002/12 Added porting to GNU C V.3.2. Revised some wording. kmatsui V.2.3 release 2003/02 Finally released. kmatsui V.2.3 patch 1 2003/03 Slightly modified. kmatsui V.2.4 prerelease 2003/11 Added porting to Visual C++. Added #pragma __preprocess, #pragma __preprocessed kmatsui V.2.4 release 2004/02 Extended multi-byte character handling. Added porting to Plan 9 / pcc. kmatsui V.2.4.1 2004/03 Revised recursive macro expansion, and added -c option. kmatsui V.2.5 2005/03 Absorbed POST_STANDARD into STANDARD as an execution time option, absorbed OLD_PREPROCESSOR setting as an execution option of PRE_STANDARD. Renamed most of #pragma __* directives as #pragma MCPP *. Added porting to GNU C V.3.3 and 3.4, changed some options accordingly.. Removed documents on older compiler-systems (DJGPP, compiler-systems on MS-DOS except Borland C 4.0). kmatsui ☆ 目次 ☆ 1.概要 [1.1] OSや処理系を選ばない portable なソース [1.2] 正確な Standard C モードに加えてその他の各種モードも 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 を使うには 3.拡張機能と互換性 [3.1] #pragma MCPP put_defines, #pragma MCPP preprocess, #pragma MCPP preprocessed, #put_defines, #preprocess, #preprocessed [3.1.1] ヘッダファイルの pre-preprocess [3.2] #pragma once [3.2.1] ヘッダファイルに #pragma once を書き込むツール [3.3] #pragma MCPP include_next, #pragma MCPP warning, #include_next, #warning [3.4] #pragma MCPP push_macro, #pragma MCPP pop_macro, #pragma push_macro, #pragma pop_macro, #pragma __setlocale, #pragma setlocale [3.5] #pragma MCPP debug, #pragma MCPP end_debug, #debug, #end_debug [3.6] #assert, #asm, #endasm [3.7] C99 の新機能(_Pragma() 演算子、可変引数マクロ等) [3.8] Borland C の asm 文その他の特殊な構文 [3.9] GNU C / cpp との互換性 [3.9.1] FreeBSD 2 / kernel ソースのプリプロセス [3.9.2] FreeBSD 2 / libc ソースのプリプロセス [3.9.3] GNU C 2 / cpp の仕様の問題 [3.9.4] Linux / glibc 2.1 ソースのプリプロセス [3.9.5] GNU C 2 で MCPP を使うには [3.9.6] GNU C 3.2 ソースのプリプロセス [3.9.7] GNU C 3 で MCPP を使うには [3.10] Visual C++ .net のシステムヘッダの問題 [3.10.1] コメントを生成するマクロ ? 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.バグ報告等 [6.1] バグかどうか? [6.2] malloc() 関連のバグチェック [6.3] バグ報告を [6.4] 改善のご意見を ☆ 1.概要 ☆ この MCPP version 2.* は Martin Minow の DECUS cpp を元に kmatsui (松 井 潔)が全面的に書き直したCプリプロセッサです。MCPP という名前は Matsui cpp という意味です。これはソースで提供するもので、各処理系で使う にはその処理系に合わせてソースに若干の変更を加えた上でコンパイルして MCPP の実行プログラムを作る必要があります。* このマニュアルでは、すでに特定の処理系に移植された実行プログラムの仕様 を説明しています(移植方法に関する記載も一部に含んでいる)。さらに詳細を 知りたい方、ソースから何らかの処理系に移植してみたい方は、ソースと mcpp- porting.txt というドキュメントを参照してください。 これらのソース、ドキュメントはすべて free software として提供します。 マニュアルの内容に入る前に、まず MCPP のソースの特徴を紹介しておきます (この [1.1] - [1.2] は mcpp-porting.txt と重複している)。 * MCPP の実行プログラムは各処理系のプリプロセッサと置換して使うもので ある。したがって、実行プログラムの名前はその処理系のプリプロセッサの 名前になる。多くの処理系ではそれは cpp である。 [1.1] OSや処理系を選ばない portable なソース GNU/Linux, DOS/Windows 等の多くのOSをサポートしている portable なプ リプロセッサであり、そのソースは Standard C (ANSI/ISO/JIS C) の処理系は もちろんのこと、C++ の処理系でも、K&R 1st. の処理系でも十分コンパイルで きる広い portability を持っている。 ライブラリ関数は古典的なものしか使っていない。一部のライブラリ関数には Cによるソースも添付している。Standard C の処理系がないため、あるいは処 理系が Standard C に準拠していない部分があるため Standard C のプリプロセ ッサがコンパイルできない、などというやっかいな問題は起こらない。 各処理系に移植するためには、多くの場合、ヘッダファイル中のいくつかのマ クロ定義を書き替えてコンパイルするだけですむ。最悪の場合でも、system.c というソースファイルに数十行書き足す程度である。 MCPP のオブジェクトはメモリを節約しながら動作するので、アドレス空間の 小さい 16 ビットシステムでさえも(制限はあるが)使える。 Multi-byte character(漢字)の処理は日本の EUC-JP, shift-JIS、中国の GB-2312、台湾の Big-5、韓国の KSC-5601 (KSX 1001) に対応している。32 ビ ット以上のシステムでは ISO-2022-JP, UTF-8 も使える。Shift-JIS や Big-5 の場合、コンパイラ本体が漢字を認識しない処理系では、MCPP がそれを補う。 [1.2] 正確な Standard C モードに加えてその他の各種モードも MCPP 自身をコンパイルする時にヘッダファイル system.H 中のマクロを変更 することで、各種の動作仕様のプリプロセッサができあがる。 もちろん Standard C 準拠のモードもあるが、K&R 1st. のモードの実行プロ グラムを作ることもできる。Standard C モードの実行プログラムは C++ のプリ プロセッサとして動作する実行時オプションも持ち、さらには自称 post- Standard 仕様のオプションまである。K&R モードのものでは "Reiser" model cpp の動作オプションもある。 Standard C モードは既存の多くのプリプロセッサと違って、規格を完全に実 装しているつもりである。ISO/IEC 9899:1990 およびその Corrigendum 1:1994, Amendment 1:1995 に対応している。C99 (ISO/IEC 9899:1999) にも対応してい る。Standard C プリプロセスの reference model となるものを目指して作って ある。これらの規格のバージョンは実行時オプションで指定することができる。 コンパイラ本体が Standard C に対応していなくても、プリプロセッサで対応 できるものはすべて実装している。隣接する文字列リテラルの連結をしないコン パイラ本体のために、MCPP にこれを処理させるようにすることができる。 ほかにいくつかの有用な拡張機能も持っている。マクロの展開機序や #if 式 の評価機序をトレースする #pragma MCPP debug もある。ヘッダファイルを "pre-preprocess" しておくこともできる。 いくつかの有用な実行時オプションも備えている。ウォーニングのレベルを指 定するオプションや、include directory を指定するオプション等である。 ソースにどんな間違いがあっても MCPP は暴走したり見当外れなメッセージを 出したりせず、正確でわかりやすい診断メッセージを出して適切な処理をする。 移植上で問題となる点についても警告を発する。 詳細なドキュメントも付属している。 MCPP の欠点を強いて挙げれば、速度がやや遅いことです。GNU C / cpp に比 べると2倍から3倍の時間がかかります。しかし、Borland C 5.5 / cpp と同じ くらいの速度で、ヘッダファイルの pre-preprocess の機能を使うともう少し速 くなるので、特に遅いほうではありません。正確であること、portable なソー スであること、少ないメモリでも動作すること等のためには、この程度の処理時 間はやむをえないと考えています。 なお、プリプロセッサの Standard C 準拠度をテストするための検証セットで ある "Validation Suite for Standard C Preprocessing"、その解説およびそれ を使ってテストした各種プリプロセッサの採点簿 cpp-test.txt を MCPP ととも に公開しています。これを見ると、「Standard C 準拠」と称する既存のプリプ ロセッサにいかに多くの問題があるかがわかります。*1 MCPP は V.2.3 の開発の途中で、検証セット V.1.3 とともに、情報処理振興 事業協会 (IPA) の平成14年度「未踏ソフトウェア創造事業」に新部 裕・プロ ジェクトマネージャによって採択され、2002/07 - 2003/02 の間は IPA の資金 援助と新部PMの助言のもとに開発が進められました。英語版ドキュメントもこ のプロジェクトの中で、有限会社・ハイウェルに翻訳を委託し、それに私が修正 とテキスト整形を加えてできあがったものです。*2 MCPP はさらに平成15年度にも「未踏ソフトウェア創造事業」に伊知地 宏 PM によって継続して採択され、V.2.4 への update 作業が進められました。 その後も MCPP と検証セットはさらに改良の作業が続けられています。 C言語の規格としては 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 と呼ぶことがあります。 *1 この cpp は V.2.2 までは単に cpp と呼んでいたが、一般の cpp と紛ら わしいので、V.2.3 からは MCPP と呼ぶことにした。このドキュメントでは V.2.2 までのバージョンも MCPP と呼ぶ。また、このドキュメントの名前は V.2.2 までは cpp.man としていたが、V.2.3 からは manual.txt と変更し、 さらに V.2.5 からは mcpp-manual.txt と変更した。私自身の名前も、V.2. 2 までは Psycho としていたが、V.2.3 からは kmatsui と変更した。 *2 「未踏ソフトウェア創造事業」(Exploratory Software Project) の概要は 次のところで知ることができる。 http://www.ipa.go.jp/jinzai/esp/ なお、情報処理振興事業協会 (IPA) は 2004/01 に独立行政法人・情報処理 推進機構 (IPA) に改組された。 MCPP のソースおよびドキュメントと検証セットは最新版を含めて、次の CVS repository に置いている。ここから tar-ball を download できる。 http://cvs.m17n.org/cgi-bin/viewcvs/?cvsroot=matsui-cpp 次のところからは anonymous ftp できる。 ftp://ftp.m17n.org/pub/mcpp/ また、次のところには案内の web page がある。 http://www.m17n.org/mcpp/ cpp V.2.2 および検証セット V.1.2 はベクター社のサイトの次のところに ある。「PACK for WIN GOLD」という CD-ROM にも収録されていた。dos/ prog/c というディレクトリに入れられているが、MS-DOS 専用ではない。ソ ースは UNIX, WIN32/MS-DOS 等に対応している。 http://download.vector.co.jp/pack/dos/prog/c/cpp22src.lzh http://download.vector.co.jp/pack/dos/prog/c/cpp22bin.lzh http://download.vector.co.jp/pack/dos/prog/c/cpp12tst.lzh http://download.vector.co.jp/ は ftp://ftp.vector.co.jp/ でも同じよ うである。 旧版は次のところにもある(ここは会員制の closed なフォーラムである)。 @nifty / FC / lib 2 これらのアーカイブファイル中のテキストファイルは、Vector のものは DOS/Windows 系に合わせて、改行コードは [CR]+[LF]、漢字は shift-JIS で encode してある。m17n.org のものは UNIX 系に合わせて改行コードは [LF]、漢字は EUC-JP である。他のOSで使う場合は変換が必要である。 拙作の convf というツールを使うと、全ファイルを一括して変換コピーで きるので簡単である(バイナリファイルは自動的に判別して無変換でコピー する。Time-stamp や mode は保存される)。ただし、MCPP のパッケージに は特定の multi-byte character encoding をテストするためのファイルも 含まれているので、それらは encoding を変換してはいけない。まず全ファ イルを改行コードだけ変換して一括コピーし、さらに doc ディレクトリだ け改行コードと漢字 encoding の双方を変換して上書きコピーするのが良い。 Convf そのものも MCPP を移植したのと同じ処理系に対応している。ただし、 ファイルを DOS/Windows から他のOSに持っていく場合は、MS-DOS や Windows95 で解凍すると大文字と小文字の区別がなくなってしまうので、ア ーカイブファイルのまま移してから、解凍して変換すること。Convf は次の ところにある。 http://download.vector.co.jp/pack/dos/util/text/conv/code/ convf-1.8.lzh ☆ 2.起動時のオプションと環境設定 ☆ 以下の記載では、 という記法は arg がユーザの入力すべき任意の引数 であることを示し、[arg] は arg が省略可能な引数であることを示します。ど ちらにしても <, >, [, ] の文字そのものは入力してはいけません。 [2.1] オプションは MCPP にどう渡されるか オプションの指定方法を説明する前に、MCPP にオプションを渡す方法につい て触れておきます。 MCPP 起動時のオプションは MCPP のソース system.c 中の do_options() で 設定されますが、各処理系に合わせた設定が必要なので、一般的な決まりはあり ません。 すでに移植した処理系についても、その処理系付属のプリプロセッサのオプシ ョンをそのままシミュレートしているわけではありません。私から見てあまり必 要がないと思われるオプションは実装していません(必要なら簡単に実装できる ので、do_options() に手を入れること)。同じ名前のオプションでも、処理系 付属のプリプロセッサとは仕様が異なる場合もあります。 処理系付属のコンパイラドライバからは通常の方法では MCPP に渡す方法のな いオプションもあります。 Gcc では -Wp というオルマイティオプションを使うと、どんなオプションで も cpp に渡すことができます。例えば、 cc -Wp,-W31,-Q23 とすると、cpp に -W31 -Q23 というオプションが渡されます。Cpp に渡したい オプションを -Wp, に続けて , で区切って並べます。*1, *2 他の処理系でも、もしコンパイラドライバのソースがあれば、この種のオルマ イティオプションを追加したほうが良いでしょう。例えば、-P と指定する と P をとった - が cpp に渡されるようにしておくと、どんなオプション でも使えるようになるので便利です。 MCPP を使うには、処理系のプリプロセッサを置くべき場所に適当な名前で置 いておきます。その時に、処理系付属のプリプロセッサを消してしまうことのな いよう、あらかじめ別名のファイルにコピーしておいてください。 なお、FreeBSD では cpp を置くディレクトリは /usr/libexec です。/usr/ bin/cc は /usr/libexec/cpp を呼び出します。ところがもう一つ /usr/bin/cpp というものがあります。これは -traditional オプションを付加して /usr/libexec/cpp を呼び出す shell-script です。古い Reiser model の cpp を期待しているツールや変則的なソースのために用意されているもののようです。 Cpp を単独で呼び出す場合は、単に cpp とするとこれが動いてしまいます。 -traditional オプションを避けるためには /usr/libexec/cpp とフルパスリス トで呼び出さなければなりません。また、元の /usr/libexec/cpp (GNU C / cpp) は消去せず、cpp_gnuc とでもしておいて、/usr/bin/cpp の呼び出すプログラム を /usr/libexec/cpp_gnuc と書き替えておくのが良いでしょう。*3, *4 Linux, FreeBSD, CygWIN での設定については [3.9.5] も参照してください。 GNU C 3.x での設定については [3.9.7] を参照してください。 *1 -Wa はアセンブラ用の -Wl はリンカ用のオルマイティオプションである。 UNIX / System V / cc のマニュアルを見ると、やはりこれらのオプション がある。GNU C / cc の -W オプションはこれとの互換性のためのものな のであろう。 *2 GNU C V.3 では cpp が cc1 (cc1plus) に吸収されてしまった。そのため、 -Wp で指定したオプションは通常は cc1 (cc1plus) に渡されてしまう。プ リプロセスを cc1 ではなく cpp (cpp0) にさせるためには、gcc の呼び出 しに -no-integrated-cpp というオプションを指定する必要がある。 *3 GNU C V.2.95.3 では FreeBSD でも Linux でも cpp のほかに cpp0 とい うのがある。Gcc が呼び出すのは cpp0 のほうである。VineLinux 2.6 では cpp は cpp0 へのリンクとなっている。 *4 VineLinux 2.6 では cpp のあるディレクトリは /usr/lib/gcc-lib/i386- redhat-linux/2.95.3である。 [2.2] 起動時のオプションの指定法 書式は次の形です。ただし、mcpp という名前は処理系や MCPP の実装によっ ては別の名前になります。 mcpp [- [-]] [in_file] [out_file] [- [-]] out_file(出力パス)が省略された時は(-o オプションが指定されない限り) 標準出力に出力します。in_file(入力パス)も省略された時は標準入力から入 力します。診断メッセージは(-Q オプションが指定されない限り)標準エラー 出力に出力します。 これらのどれかのファイルがオープンできない時は、エラーメッセージを出し て終了します。 MCPP ではオプションの取得には getopt() を使っています。 引数を必要とするオプションに引数がない場合はエラーとなります(-M オプ ションだけは別)。 引数を必要とするオプションでは -I, -I のどちらも有効です (オプション文字と引数の間に space はあってもなくても良い)。 引数のないオプションは -Qi, -Q -i のどちらも有効です(1つの '-' の後 につなげても別々に '-' を付けても良い。ただし、-M はつなげてはいけない)。 同一のオプションが複数回指定された場合、-D, -U, -I, -W オプションはそ れぞれが有効です。-S, -V, -+ は2回目以降は無視されます。-2, -3 はそのた びに仕様が反転します。その他のオプションは最後に指定されたものが有効です。 大文字と小文字は区別されます。 いわゆるスイッチキャラクタは DOS/Windows 等でも - であり、/ ではありま せん。 不正なオプションを指定すると usage 文が表示されるので、mcpp -? 等とす ることで、使えるオプションを確かめることができます。Usage 文のほかにもい くつかのエラーメッセージが出ますが、その内容はいずれも自明のものであるの で、説明は省略します。 [2.3] 共通のオプション MCPP の動作モードや処理系によらない共通のオプションは次の通りです。 -C ソース中のコメントも出力する。これは UNIX の lint でチェックする場合 に必要なのだそうである。lint は使わなくても、デバッグ時に有用なオプ ションである。ただし、コメントはソースの論理行の先頭に移して出力する。 コメントの処理はマクロ展開やディレクティブの処理の前に行われるもので あり、コメントはマクロ呼び出しの途中にあるかもしれないからである。 -D [=[]] -D [=[]] マクロ macro を定義する。事前定義されているマクロでも __STDC__, __STDC_VERSION__, __FILE__, __LINE__, __DATE__, __TIME__, __cplusplus 以外であれば、このオプションで定義が変更できる(C99 の __STDC_HOSTED__ は GNU C V.3 のように -D オプションで定義する処理系 もあるので、例外的に許可する)。= が指定されていれば value に 定義し、省略されていれば 1 に定義する(bcc, bcc32 では省略時には0個 のトークンに定義されるので、それとは違うことに注意)。= の前には空白 を入れてはいけない。= の後に空白があればそのマクロは0個のトークンに 定義される。 このオプションでは、引数つきマクロも定義できる。 このオプションは何回でも指定できる。 -I Include directory のサーチパスの第一位に directory を指定する(サー チパスについては [4.2] を参照)。directory 名に space が含まれる場合 は、directory 名全体を " と " で囲むこと。 -I 1, -I 2, -I 3 #include "header" の形式の(
の形式ではない)ディレクティブ で最初にサーチされるディレクトリの基準を指定する。-I1 ではカレントデ ィレクトリ、-I2 ではソースファイル(インクルード元)のあるディレクト リ、-I3 ではその双方をサーチする(詳細は [4.2] を参照)。 -j 診断メッセージの出力ではソース行等の付加情報は出力せず、1行の診断メ ッセージだけ出力する(デフォルトでは1行の診断メッセージに続いて、そ のソース行が表示される。ソースファイルが include されたものである場 合は、include 元の行も順次表示される。マクロに関する診断メッセージで は、展開前のマクロも順次表示される)。 GNU C の testsuite で私の検証セットを使う時は、このオプションを指定 して、GNU C / cpp と同じ診断メッセージ形式にする必要がある。 -o プリプロセス後のソースを file に出力する。省略時は第二引数が出力パス となるので、無くてもよいオプションであるが、コンパイラドライバによっ てはこのオプションを使うものがある。間違ってワイルドカードが展開され た時の危険を防ぐためであろうか。 -P コンパイラ本体に行番号情報を伝える出力を省略する。Cのプリプロセッサ 以外の用途に流用する場合に使うオプションである。 -Q 診断メッセージを(カレントディレクトリの)mcpp.err という名前のファ イルに出力する(mcpp.err は後ろにアペンドされてゆくので、時々削除す ること)。 -U 事前定義されているマクロ macro を取り消す(__FILE__, __LINE__, __DATE__, __TIME__, __STDC__, __STDC_VERSION__, C99 モードでの __STDC_HOSTED__ および -+ オプションで起動した時の __cplusplus は取 り消せない)。 -v MCPP のバージョンおよびインクルードディレクトリのサーチ順を標準エラ ー出力に出力する。 -W 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 4 は -W 5 と同じである。ただし、-W 0 が指定された場合は、他の -W オ プションはすべてキャンセルされる。このオプションを指定しない時は -W 1 を指定したのと同じである(warning の内容は [5.5] - [5.9] を参照)。 -z #include で取り込まれたファイルのプリプロセス結果は出力しない。しか し、マクロは定義される。 [2.4] MCPP のモードによるオプション MCPP 自身をコンパイルする時に system.H で各種のマクロを設定することで、 種々の異なる動作仕様のプリプロセッサができます(その内容は mcpp-porting. txt の [4.1.3] を参照)。以下の説明で出てくる MODE, STDC, TFLAG_INIT, etc. の大文字名(__ で始まらないもの)はすべて system.H で定義されるマク ロです。[3], [4], [5] でも同様です。これらのマクロはあくまでも MCPP 自身 をコンパイルする時に使われるだけで、できあがった MCPP 実行プログラムには これらのマクロは残ってはいません。勘違いしないようにしてください。 これらのマクロの中でもプリプロセス仕様の根幹を決めるのが MODE というマ クロで、STANDARD, PRE_STANDARD の2つの値のどちらかをとります。このマニ ュアルでは MODE == STANDARD でコンパイルされた MCPP を mcpp_std、MODE == PRE_STANDARD でコンパイルされたものを mcpp_prestd と呼びます。 mcpp_std はデフォルトでは規格準拠の動作をしますが、実行時オプションに よって私が post-Standard mode と称する動作仕様を指定することができます。 これをこのマニュアルでは poststd モードと呼びます。また、mcpp_prestd は デフォルトでは K&R 1st. の動作をしますが、実行時オプションによっていわゆ る "Reiser model cpp" の動作仕様を指定することができます。これをこのマニ ュアルでは oldprep モードと呼びます。その上、mcpp_std には compat モード という特殊な実行時モードもあります。 このマニュアルでは各種の仕様で異なる動作が並べて記載されているので、少 々見にくくなっていますが、がまんしてください。 mcpp_std では次のオプションが使えます。 -+ C++ のプリプロセッサとして動作する。マクロ __cplusplus を事前定義し (その値は system.H で定義されている。デフォルトは 1)、// から論理 行の行末までをコメントと解釈し、::, .*, ->* をそれぞれ単一のトークン として認識する。#if 式中では true, false というトークンはそれぞれ 1, 0 と評価する。__STDC__, __STDC_VERSION__ が定義されていれば、それを 削除する(ただし、GNU C 用では GNU C / cpp との互換性のために __STDC__ は削除しない)。_ で始まらない事前定義マクロも削除する。た だし、extended characters の UCN への変換はしない。*1, *2 -2 Digraphs 処理の初期設定を反転する。DIGRAPHS_INIT == FALSE の場合はこ れで digraph を認識するようになり、逆の場合は認識しなくなる。 -3 Trigraph 処理の初期設定を反転する。TFLAG_INIT == FALSE の場合はこれ で trigraph を認識するようになり、逆の場合は認識しなくなる。 -h マクロ __STDC_HOSTED__ の値を に定義する。 -S C では __STDC__ の値を に変更する。C++ では無視される。 は [0, 9] の範囲の数値でなければならない。 が 1 以上であれば、_ で始まら ない事前定義マクロ(unix, MSDOS 等)を無効にする。 S は __STDC__ の意味である。このオプションを指定しないと、__STDC__ はデフォルトの値となる(通常は 1)。 GNU C 版では -pedantic, -pedantic-errors, -lang-c89 でも -S1 を指定 したと同じことになるので、その次の -S は無視される。 -@post, -@poststd MCPP の "post-Standard mode" と称するプリプロセス仕様の動作をする。 これは規格に次のような変更を加えたものである。 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 という形式の header name は通常は通すが、ウォ ーニングを出す(class 2 のウォーニングオプションで)。マクロで の形の header name を使うと、特殊な場合にはエラーとなるこ とがある。#include "stdio.h" の形式を推奨する。 7.マクロ定義ではマクロ名と置換リストとの間に space が必要という規 定が C99 で追加されたが、この規定には従わない(tokenization の際に自 動的に a space が挿入されるので)。 8.UCN (universal-character-name) は認識しない。また、identifier 中 の multi-byte character も認識しない。 9.C++ では11種の identifier 様 operator は operator としては扱わ ない。 なお、このモードでは -a (-lang-asm, -x assembler-with-cpp) オプショ ンは使えない。 -@compat 再帰的マクロを規格よりもさらに展開する。すなわち、再帰的マクロの展開 に際して、同名マクロの再置換を禁止する範囲を規格よりも狭くとる。 再帰的マクロの展開の規定については cpp-test.txt [2.4.26] を参照のこ と。再帰的マクロの具体例は test-t/recurs.t を参照のこと。*3 mcpp_prestd では次のオプションが使えます。 -@old, -@oldprep 次のような仕様の古い "Reiser model cpp" の動作をする。この仕様をこの マニュアルでは oldprep モードと呼ぶ(old preprocessor の意)。 1.コメントを1個の space でなく0個の space に変換する。この変換は 原則として最後の出力時に行わる。ただし、マクロ定義では定義の直後に行 われる。 2.マクロ定義の置換リスト中に文字列リテラルまたは文字定数があり、そ の中にどれかのパラメータ名と一致する部分がある場合は、そのマクロの呼 び出しの際にこの部分は、パラメータに対応する引数で置き換えられる。一 致する部分というのは、両端の " または ' をはずした中身をトークン列と して見た場合に一致するトークンのことである。 3.#else, #endif の行に何が書いてあってもエラーにせず無視する(対応 する #if MACRO, #ifdef MACRO の MACRO を書いたりする)。 4.リテラルを閉じる " や ' がない場合は、行末で閉じられているとみな す(unterminated string literal, unterminated character constant の エラーが発生しない)。 5.# 123 という行を #line 123 と同じものとして扱う。 *1 C++ で __STDC__ が定義されているのはトラブルの元であり、良い仕様で はない。GNU C のドキュメントによると、ヘッダファイルの多くが __STDC__ が定義されていることを期待しているので、C++ でもこれを定義 しておく必要がある、とのことである。しかし、これはヘッダファイルの書 き方が悪いと言わざるをえない。C90, C99, C++ に共通の部分には、#if __STDC__ || __cplusplus と書くべきなのである。 *2 C++ Standard では C99 と違って、UCN は大々的な扱いを受けており、中 途半端な実装はできない。C 1997/11 draft でもそうであった。しかし、 Unicode をそこまで導入することには、実装の負担が大きすぎること等の問 題があって疑問だからである。 *3 これは GNU C, Visual C++ 等の主要な処理系との互換性のためのオプショ ンである。'compat' は "compatible mode" を意味する。このマニュアルで はこれを compat モードと呼ぶ。 [2.5] 特定の処理系以外の処理系に共通のオプション -a ある種のアセンブラソースに見られる次のような記法をエラーにせずに通す。 1. #APP といった # で始まる行がCのディレクティブに合致しない場合、この行を そのまま出力する。 2. "Jugemjugem gokouno surikire" といった大昔の流儀の行をまたぐ文字列リテラルを "Jugemjugem\ngokouno\nsurikire" という行に連結する。 3. ## 演算子による token の連結でCの pp-token としては無効な token が生成されても、エラーにしない。 これらは GNU のソースなどに時々見られるものですが、GNU 版ではこのオ プションは -x assembler-with-cpp および -lang-asm です。 ただし、このオプションは [2.4] で述べた -@post オプションとは両立し ません。 -I- デフォールトの include directory がキャンセルされ、環境変数で指定さ れた directory および他の -I オプションで指定された directory だけが 有効となる。ただし、GNU C に移植されたものでは、このオプションは -I- ではなく -nostdinc である。GNU C では -I- オプションはまったく違った 意味を持つ([2.6] 参照)。 -N '_' で始まるものも含めてすべての事前定義マクロを無効にする。ただし、 規格で要求されている事前定義マクロおよび __MCPP は除く。規格で要求さ れている事前定義マクロとは、__FILE__, __LINE__, __DATE__, __TIME__, __STDC__, __STDC_VERSION__, C99 の __STDC_HOSTED__ および C++ の __cplusplus である。(__MCPP を特別扱いしているのは、GNU C / gcc が かつてはデフォルトで -undef オプションを使っていたので、それによって これが undefine されるのを防ぐためである。これを undefine したい場合 は -U オプションを使うこと)。 Plan 9 / pcc 用ではこのオプションは -n である。 mcpp_std では -V オプションが使えます。 -V C では __STDC_VERSION__、C++ では __cplusplus という事前定義マクロの 値を指定された値 に変更する。この場合の は long の値 となる(ISO/IEC 9899:1990 / Amendment 1:1995 ではこの値は 199409L、 C99 では 199901L、C++ Standard では 199711L である)。__STDC__ が 0 に定義されていると __STDC_VERSION__ は必ず 0L となり、-V は無効であ る。 C では、このオプションを指定しないと、__STDC_VERSION__ は system.H の STDC_VERSION の値となる(GNU C V.2.7 - V.2.9x では 199409L、それ 以外では 0L)。 -V199901L として __STDC_VERSION__ >= 199901L になった場合は次のよう な C99 の仕様となる([3.7] 参照)。 1. // から行末までをコメントとして扱う。*1 2. Preprocessing-number の中に e+, E+, e-, E- と同様に p+, P+, p-, P- という sequence も認める。これは浮動小数点数のビットパターンを 0x1.FFFFFEp+128 というふうに、16進で表記するためのものである。 3. _Pragma( "foo bar") と書くと #pragma foo bar と書いたのと同じ効 果を持つ _Pragma operator が有効になる。 4. EXPAND_PRAGMA というマクロを TRUE に定義してコンパイルされた MCPP では、#pragma 行の引数は、STDC または MCPP で始まるのでない限り マクロ展開の対象となる(デフォルトでは Visual C 版以外では EXPAND_PRAGMA == FALSE であり、マクロ展開しない)。 5. long long を持つ処理系では #if 式の評価は long long, unsigned long long で行う。 6. 識別子・文字定数・文字列リテラル・pp-number の中にある UCN (universal-character-name) の sequence を通す。 なお、可変引数マクロは C99 の仕様であるが、C90 および C++ でも使える ようにしてある。*2 C++ でも -V199901L として __cplusplus >= 199901L にすると、C99 互換 モードとなり、上記 2,3,4,5 の機能拡張を行う(1 は無条件で有効。6 は ほぼ同様)。これは MCPP 独自の拡張であり、C++ Standard には違反する。 Plan 9 / pcc ではこのオプションは -s である。 なお、これらのマクロ __STDC__, __STDC_VERSION__, __cplusplus の指定 に -D オプションは使えない。一般のユーザ定義マクロと区別するためであ る。 32 ビット以上のシステムでは -e オプションが使えます。 -e Multi-byte character の encoding を に変更する。 については [2.8] を参照のこと。 OK_MAKE == TRUE でコンパイルされた MCPP では次のオプションが使えます。 -M* オプションは makefile 用の依存関係行を出力するものです。複数のソー スファイルがある場合、すべてのソースについてこれらの -M* オプションを付 けて実行して、その出力をマージすると、makefile で必要な依存関係記述行が そろいます。これらのオプションは GNU C / cpp のものに合わせていますが、 少し違いがあります。*3 -M ソースファイルの依存関係を記述する行を出力する。出力先はコマンドライ ンで指定された出力ファイル、それが指定されていなければ標準出力である。 行が長ければ折りたたむ。通常のプリプロセス結果は出力しない。 -MM -M とほぼ同じであるが、次のヘッダは書き出さない。 1. #include の形式で include されるもの。 2. #include "/include/stdio.h" 等と絶対パスで指定されているもの。 3. #include "stdio.h" の形式であるが、カレントディレクトリ(処理系 や -I オプションによってはソースのあるディレクトリ)で発見されず、 システム等の include ディレクトリで発見されたもの(-I オ プションや環境変数等で指定されたディレクトリも含む)。 -MD [FILE] -M とほぼ同じであるが、通常のプリプロセス結果もコマンドラインで指定 されたファイルまたは標準出力に出力されることが違っている。また、依存 関係行の出力先は、 FILE が指定された時はそのファイルとなり、指定され ていない時はソースファイル名の .c を .d に変えたファイルとなる。 -MMD [FILE] -MD とほぼ同じであるが、-MM と同様にシステムヘッダとみなされるものは 書き出さないことが違っている。依存関係行の出力先は -MD [FILE] と同じ。 -MF FILE 出力先を FILE にする。-MD FILE, -MMD FILE に優先する。 -MP "Phony target" も出力する。Phony target というのは、インクルードされ るファイル1つ1つについて、それ自体を依存先を持たないターゲットとし て、次のように記述するものである。 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 *1 // は C90(mcpp_std の場合)でもコメントとして扱うが、ウォーニング を出す。 *2 これは GNU C / cpp との互換性のためである。 *3 GNU C / cpp と違うのは次の点である。 1. -MG オプションはない。オプション指定の仕方が複雑すぎるからである (したがって、どういう仕様かの説明は略)。しかし、-M オプションでも、 インクルードファイルが見つからない場合はエラーにはなるが、依存関係行 は出力されるので、それで代用できる。 2. -MM, -MMD オプションで除外されるヘッダの範囲が広い。上記の -MM オプションの説明にある 2, 3 は GNU C 2 / cpp0 では除外されないもので ある。GNU C 3 / cpp0 では上記の 3 のうち、システムヘッダ・ディレクト リで発見されたものは除外されるようになった。 なお、4.4BSD-Lite では /usr/bin/mkdep というコマンドがある。FreeBSD や Linux に実装されているものは *.d ファイルを生成して .depend とい う名前のファイルにマージする shell-script であるが、cpp -M を呼び出 している。 [2.6] 処理系ごとのオプション GNU C 等では次のオプションが使えます。 -b 行番号情報をCのソースのスタイルで出力する。 プリプロセッサからコンパイラ本体への行番号情報の受け渡しは #line 123 "filename" というCのソースと同じ形式でできる処理系が普通であるが、この形式は使 えない処理系もある。MCPP はそのような処理系に移植されたものでは、そ の処理系のコンパイラ本体の受け取れる形式で行番号情報を出力するのが、 デフォルトの仕様である。また、GNU C ではCソースの形式でもコンパイラ 本体では大丈夫であるが、それを cpp 自身に再度与えると処理できないバ ージョンの cpp もあり、また rpcgen のように GNU C 固有の形式でないと 通らないツールもあるので、デフォルトでは GNU C の形式で出力する。 しかし、これらの処理系でも、このオプションを使うと、Cのソースのスタ イルで行番号情報を出力する。 このオプションは、出力をもう一度プリプロセスする時のためのもので、# pragma MCPP put_defines (#put_defines) と組み合わせてヘッダファイル の「プリプリプロセス」をする時に使う。 MS-DOS の処理系では次のオプションが使えます。 -m メモリモデルを x に指定する。x は t, s, c, m, l, h のどれかである (大文字でも小文字でも可)。これを指定すると、Microsoft C では M_I86mM の m のところをこの x を大文字にしたもので置き換えたマクロが 1 に事前定義され、その他の処理系では __TINY__, __SMALL__, __COMPACT__, __MEDIUM__, __LARGE__, __HUGE__ のうちの対応するマクロ が 1 に事前定義される。OK_SIZE == TRUE で MCPP をコンパイルしてある 場合は、#if sizeof (type) もこのメモリモデルの指定に従って評価される。 デフォルトでは M_I86SM または __SMALL__ のどちらかが 1 になる。 LCC-Win32 では次のオプションが使えます。 -g マクロ __LCCDEBUGLEVEL を に定義する。 -O マクロ __LCCOPTIMLEVEL を 1 に定義する。 Visual C では次のオプションが使えます。 -Fl GNU C の -include と同じ。 -Tc C のソースであることを指示する。指定しなくても同じ。 -Tp -+ オプションと同じ。 -u -N オプションと同じ。 -Wall -W17 (-W1 -W16) と同じ。 -WL -j オプションと同じ。 -w -W0 オプションと同じ。 -X -I- オプションと同じ。 Plan 9 / pcc では次のオプションが使えます。 -i インクルードディレクトリのサーチ順を標準エラー出力に出力する。 -n 他の処理系での -N と同じ。 -s 他の処理系での -V と同じ。 GNU C に移植したものでは、以下の(この [2.6] セクションの終わりまでの) オプションが使えます。なお、GNU C 用では __STDC__ は 1 にしているので、 -S1 オプションは指定してもしなくても同じです。 まず、MCPP のモードによらないオプションは次のとおりです。 -dD, -dM プリプロセスの最後にその時点で有効なマクロ定義を #define 行の形で出 力する。 -dM オプションではプリプロセス結果は出力しない。また、事前定義された マクロも規格の標準事前定義マクロでなければ出力する。-dD オプションで はプリプロセス結果も出力する。また、事前定義マクロは出力しない。*1, *2 -I- このオプションの前と後とで -I オプションの仕様を変える。 このオプションの前に -I で指定されたディレクトリは #include "header.h" の形のヘッダファイルのサーチにだけ使われる。このオプショ ンの後で -I で指定されたディレクトリはすべての #include directive の サーチに使われる。また、#include "header.h" の形でも include 元のデ ィレクトリはサーチしない。 -include メインのソースファイルを処理する前に、 を #include する(ソー スの冒頭に #include と書いたのと同じ結果になる)。 -isystem をインクルードパスの system-specific な directory の前に(site- specific な directory の後に)加える。 -lang-c, -x c C のプリプロセスを行う。指定しなくても同じ。 -nostdinc 他の処理系での -I- オプションと同じ。 -undef -N オプションと同じ。 -Wcomment, -Wcomments, -Wsign-compare -W1 オプションと同じ。指定しなくても同じ。 -Wundef -W4 オプションと同じ。 -Wtrigraphs -W16 オプションと同じ。 -Wall -W17 (-W1 -W16) オプションと同じ(class 2, 4 の warning は GNU C の 標準ヘッダでは頻発しやすくうるさいので、-Wall からはずしてある。 class 8 の warning も通常は余計なお節介である。しかし、これらには portability を確かめる等の有用な使い道がある。使う時は gcc -Wp,-W31 とすること)。 -w -W0 オプションと同じ。 -finput-charset= -e オプションと同じ。 GNU C 版の mcpp_std では次のオプションが使えます。 -digraphs Digraph を認識する(-2 での反転も有効)。 -lang-c89, -std=c89, -std=gnu89, -std=c90 -S1 オプションと同じ。指定しなくても同じ。C90 だけでなく C95 の仕様 も含まれる。 -lang-c99, -lang-c9x, -std=c99, -std=c9x, -std=gnu99, -std=gnu9x -V199901L オプションと同じ。 -lang-c++, -x c++, -std=c++98 C++ のプリプロセスを行う。-+ と同じ。 -lang-asm, -x assembler-with-cpp 他の処理系での -a と同じ。-@post オプションを使うと、このオプション は使えない。 -pedantic, -pedantic-errors -W7 (-W1 -W2 -W4) オプションと同じ。 -std=iso: 規格のバージョンを指定する。 は C では 9899 であり、C++ では 14882 である。 が 9899 の場合は、 は 1990, 199409, 1999, 199901 等である。 が 14882 の場合は、 は 199711 である。 にその他の値が指定された場合は、__STDC_VERSION__ または __cplusplus がその値に定義される(この場合は 200503 というように6ケタで指定する こと)。 -trigraphs Trigraph を認識する(-3 での反転も有効)。 -ansi __STRICT_ANSI__ マクロを 1 に定義する。 GNU C 版の mcpp_prestd では次のオプションが使えます。 -traditional -@old と同じ。 次のオプションはいずれもエラーにはしませんが、何も対応しません(ウォー ニングを出す場合もある)。 -A GNU C ではこのオプションはソースに #assert と書 いたのと同じ結果になるが、#pragma でない拡張ディレクティブは Standard C では認められないものである。幸いいままでのところ gcc はデ フォルトでは -D オプションで同等のマクロを cpp に渡してくれるので、 実際にはほとんどの場合、支障はない(#assert を使っている稀なソースで ない限り)。 -$ -g -idirafter -iprefix , -iwithprefix , -iwithprefixbefore -noprecomp -remap GNU C V.3.3 以降ではプリプロセッサがコンパイラに吸収されて独立したプリ プロセッサが存在しなくなったため、gcc を -no-integrated-cpp オプションを 付けて呼び出しても、プリプロセッサのオプションではないオプションがプリプ ロセッサに渡されてくることがあります。GNU C V.3.3 以降に移植された MCPP では、次のようなもののうち MCPP の認識しないものはこの種のにせオプション として無視します。 -E -c -quiet -W* -m* -f* そのほか、各処理系固有のオプションが少しありますが、それについては usage 文を見てください。 *1 ただし、GNU C V.3.3 以降では大量のマクロが事前定義されるようになっ たが、これらは事前定義マクロとしては扱わない。すなわち、-dD オプショ ンでもこれらのマクロを出力する。 *2 #pragma MCPP put_defines (#put_defines) の出力は -dM オプションとほ ぼ同様である。ただし、次の点が違っている。 1. put_defines では標準事前定義マクロとして規定されているものも、コ メントの形で出力する。 2. put_defines ではそのマクロ定義のあるファイル名と行番号もコメント の形で出力され、また読みやすいように形を整えて出力するが、-d* オプシ ョンでは GNU C と同じ形式で出力する。この形式を想定している makefile も見掛けるからである。 [2.7] 環境変数 MCPP でデフォルトで設定されていない system include ディレクトリは、環 境変数で指定しておく必要があります。デフォルトで設定されている system include ディレクトリについては、[4.8] を見てください。また、include ディ レクトリのサーチ順と環境変数の名前については、[4.2] を見てください。 GNU C ではバージョンによって異なる補助的な system include ディレクトリ が使われます。それらのうち noconfig.H または config.h で指定されていない ものがあれば、それについては環境変数の設定が必要です。 なお、以前の MCPP では GCC_VERSION という環境変数の設定が必要でしたが、 MCPP V.2.5 からはその必要はなくなりました。 環境変数 LC_ALL, LC_CTYPE, LANG については、[2.8] を見てください。 [2.8] Multi-byte character の encoding MCPP は multi-byte character の多様な encoding に対応しています。ただ し、16 ビットシステム用と 32 ビット以上のシステム用とで枠組みが違ってお り、メモリーの少ない 16 ビットシステムでは実装できる encoding が限定され ています。 16 ビットシステムと 32 ビット以上のシステムのどちらでも使える 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) 16 ビットシステムではこのうちのどれか1つだけが実装されます。 32 ビット以上のシステムでは次の encoding も使えます。 ISO-2022-JP1 : 国際規格の日本語 UTF-8 : unicode の encoding の1種 32 ビット以上のシステムではこれらの encoding がすべて実装されます。そ して、実行時に次のようないくつかの方法で実際に使う encoding を指定します。 優先順位はこの順のとおりです。 1. ソース中で #pragma __setlocale( "") で指定された encoding (Visual C 用では #pragma setlocale( "") )。これ を使うと、1本のソースファイルの中でも複数の encoding を使うことがで きる。 2. 実行時オプション -e または -finput-charset= で指定された encoding。 3. 環境変数 LC_ALL, LC_CTYPE, LANG で指定された encoding(優先順位は この順)。 4. MCPP をコンパイルする時に設定されたデフォルトの encoding。 #pragma __setlocale、-e オプション、環境変数で指定できる は 原則として共通で、次のとおりです。右辺の は左辺の 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 による指定はそれらに優先し ます。 GNU C 用では環境変数 LANG に限って、次のものが使えます。これらは GNU C との互換性のために用意しているものです(MCPP に対しては小文字でも良く、 '-' も省略できるが、GNU C のコンパイラに対しては文字通りこのスペルでなけ ればならない)。 EUC-JP : C-EUCJP shift-JIS : C-SJIS ISO-2022-JP1: C-JIS なし : C GNU C はコンパイラ自身をコンパイルした時の configuration によって、環 境変数 LANG の C-* の指定を認識するものと認識しないものとあります (*)。 しかし、コンパイラが認識しない場合は、MCPP がそれを補います。 * GNU C を configure する時に --enable-c-mbchar というオプションを付け ると、環境変数 LANG 等によって encoding を指定できることになっている。 しかし、この configuration は 1998/07 からあるもののようであるが、少 なくとも V.3.2 では正しく動作しない。LANG のほかに LC_ALL, LC_CTYPE でも encoding を指定できることになっているが、実際には診断メッセージ が変わるだけである。Vine Linux 2.6 や FreeBSD 4.7 に入っている V.2. 95 でも同様である。 [2.9] ワンパスコンパイラで MCPP を使うには Visual C, Borland C, LCC-Win32 のようにプリプロセッサがコンパイラから 独立していないいわゆる「ワンパスコンパイラ」が多くなってきています。処理 速度を上げるためだと思われます。しかし、プリプロセスに要する時間は現在の ハードウェアでは小さなものになっています。また、そもそもプリプロセスとい うものは実行時環境や処理系からはほぼ独立した共通のフェーズであることに大 きな意味があるので、「ワンパスコンパイラ」が多くなるのは決して良いことだ とは思えません。プリプロセスの処理系依存の仕様も増える結果になります。 ともあれワンパスコンパイラでは、プリプロセッサを MCPP に置き換えること ができません。したがって、MCPP を使うには、まず MCPP でソースをプリプロ セスし、その出力をコンパイラに渡しますが、コンパイラによって再度ムダにプ リプロセスがされることになります。ムダですが、やむをえません。それでも、 MCPP を使うことはソースチェックのために有効であり、処理系付属のプリプロ セッサにはない機能を使うこともできます。 ワンパスコンパイラで MCPP を使うには、この手順を makefile に書く必要が あります。そのサンプルとしては、MCPP 自身のコンパイルに使う visualc.mak, borlandc.mak, lcc_w32.mak 等の makefile のリコンパイル用の設定を見てくだ さい。 なお、GNU C 3 ではコンパイラがプリプロセス機能を内蔵するようになったも のの、外部プリプロセッサを使うオプションも用意されているので、それを活用 することで MCPP を問題なく使うことができます([3.9.7] 参照)。 [2.10] 統合開発環境で MCPP を使うには GUI のいわゆる「統合開発環境」(IDE) というものは処理系独自の仕様であり、 内部的なインタフェースも通常は公開されていないので、その中で MCPP を使う には困難があります。その上、コンパイラがワンパスコンパイラであると、そこ に MCPP を使うフェーズを挿入するのはさらに困難です。 ここでは Visual C++ .net の IDE で MCPP を使う方法を説明します。 Borland C++ の IDE は私は 4.0 のものしか持っておらず、古すぎるので対応し ていません。また、LCC-Win32 はソースが shareware なので、それを購入して 時間さえかければ対応可能かと思われますが、まだ対応していません。Borland C 版や LCC-Win32 版は、コマンドラインで使ってください。 [2.10.1] Visual C++ .net の IDE で MCPP を使う方法 Visual C++ の IDE は内部的なインタフェースが公開されておらず、しかもコ ンパイラがワンパスコンパイラなので、通常の「プロジェクト」では MCPP を使 うことができません。しかし、MCPP を使う makefile を書いておけば、それを 取り込んで「メイクファイルプロジェクト」を作成することができます。そして、 ソースの編集や検索や、さらにデバッグ機能を含む IDE の大半の機能を使うこ とができます。 「メイクファイルプロジェクト」を作るには次のようにします。この方法は 「Visual C++ .net 2003 ドキュメント」の「メイクファイルプロジェクトの作 成」に書いてあるものです。*1 1、IDE のデバッグ機能を使う権限を持つユーザとしてログインする。*2 2、MCPP を使う makefile を書いておく(visualc.mak を参照)。 3、Visual Studio .net を起動する。*3 4、「新しいプロジェクト」をクリックし、現れた「新しいプロジェクト」のウ ィンドウで「メイクファイル プロジェクト」を選び、「プロジェクト名」と 「場所」を指定して「OK」をクリックする。 5、すると、「メイクファイル アプリケーション ウィザード」のウィンドウが 開くので、「アプリケーションの設定」をクリックし、そこで「ビルドコマンド ライン」「出力」「消去コマンド」「リビルドコマンド」の欄を入力する。これ らの用語はわかりにくいが、MCPP 自身のコンパイルを例にとると次のようなこ とである(生成する MCPP の実行プログラムの名前を mcpp32_std.exe とする)。 「ビルドコマンドライン」: nmake 「出力」 : mcpp32_std.exe 「消去コマンド」 : nmake clean 「リビルドコマンド」 : nmake CPP=mcpp32_std PREPROCESSED=1 「メイクファイルプロジェクト」では make install に相当するコマンドがない ので、「ビルドコマンドライン」「リビルドコマンド」で指定されるコマンドで は install も実行されるように makefile を書いておく必要がある。 MCPP をコンパイルするのでなければ、「ビルドコマンドライン」と「リビルド コマンド」とは通常は同一で良い。 これらを入力したら、「完了」をクリックする。 6、すると、「ソリューションエクスプローラ」にプロジェクトが現れるので、 その「ソースファイル」というフォルダをクリックして、そして、メニューの 「プロジェクト」から「既存項目の追加」を選び、ソースファイルをすべて選択 して「OK」する。すると、「ソリューションエクスプローラ」にソースファイル 名が現れる。 これで、[編集」「ビルド」「リビルド」「デバッグ」等の機能がすべて使え るようになります。 *1 しかし、私のところでは Windows2000 ではこれでできたが、WindowsXP HE ではなぜかできなかった。Windows2000 で生成されたプロジェクトファ イルをコピーしたところ、WindowsXP でもプロジェクトは問題なく使うこと ができた。 *2 デバッグ機能を使うためには、Windows2000 では "Debugger users" とい うグループにユーザを所属させる必要がある。WindowsXP HE ではそういう グループはないので、管理者としてログインしなければならない。また、ソ ースレベル・デバッグ機能を使うためには cl.exe の呼び出しに /Zi オプ ションを付加して、デバッグ情報が生成されるように makefile を書いてお く必要がある。 *3 「スタート」->「プログラム」から起動すると、ビルドされた実行プログ ラムをデバッグのために動かした時に、インクルードディレクトリ等の環境 変数が設定されない。これを設定するには先に「Visual Studio .NET コマ ンドプロンプト」を開いて、そこから devenv <プロジェクトファイル> /useenv として起動しなければならない。 ☆ 3.拡張機能と互換性 ☆ MCPP にはいくつかの固有の拡張機能があります。また、各処理系付属のプリ プロセッサにはそれぞれの拡張機能がありますが、それらの一部は MCPP では使 えません。ここではこうした拡張機能と互換性の問題を説明します。 なお、HAVE_PRAGMA == TRUE でコンパイルされた mcpp_std では #pragma 行 は原則としてそのまま出力します。mcpp_std 自身が処理するものについても、 同様です。同じ #pragma がコンパイラ本体にとっても意味を持つ可能性がある ためです。 しかし、#pragma MCPP で始まる行は mcpp_std 専用のものなので出力しませ ん。GNU C 版では、#pragma GCC に poison, dependency, system_header のど れかが続く行も出力しません。また、#pragma once も出力しません。ヘッダの "pre-preprocess" の機能を使うと、MCPP の出力を再度 MCPP で処理することに なりますが、その時に処理が重複するのを避けるためです(pre-preprocess に ついては [3.1] を参照)。#pragma push_macro, #pragma pop_macro もコンパ イラ本体にとっては無用なので出力しません。 HAVE_PRAGMA == FALSE でコンパイルされた mcpp_std では #pragma 行は出力 せず、mcpp_std 自身の処理しないものについてはウォーニングを出します。 また、EXPAND_PRAGMA == TRUE でコンパイルされた mcpp_std では #pragma 行の引数はマクロ展開の対象となります(実際には EXPAND_PRAGMA == TRUE は Visual 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 や GNU C 3 の #pragma GCC の方法を採り入れたものです。 ただし、#pragma once は多くの処理系に実装されて名前が衝突する恐れはな くなっているので、この名前のまま実装しています。また、#pragma __setlocale はコンパイラ本体に対しても必要なので MCPP という名前は付けず、 "__" を先頭に付けてユーザの名前空間と重ならないようにしています。 [3.1] #pragma MCPP put_defines, #pragma MCPP preprocess, #pragma MCPP preprocessed, #put_defines, #preprocess, #preprocessed #pragma MCPP put_defines, #pragma MCPP preprocess, #pragma MCPP preprocessed は mcpp_std のもので、#put_defines, #preprocess, # preprocessed は mcpp_prestd のものです。以下では #pragma を例にとって説 明します。 #pragma MCPP put_defines ディレクティブに出会うと MCPP は、その時点で 定義されているすべてのマクロを #define 行の形で出力します。もちろん、# undef されたものは出てきません。__STDC__ 等の #define, #undef の対象にで きないものは、一応 #define 行の形をとって、しかしコメントマークで囲んで 出力されます(__FILE__, __LINE__ はマクロ呼び出し時に動的に定義される特 殊なマクロなので、ここで出力される置換リストは無意味なものである)。 mcpp_prestd および mcpp_std の poststd モードでは、function-like マク ロ定義のパラメータ名は記憶しません。そこでこのディレクティブでは、パラメ ータ名は第1パラメータから順に機械的に a, b, c, ... という名前で表示しま す。27 個目以降のパラメータには a1, b1, c1, ..., a2, b2, c2, ... という 名前を使います。 MCPP を入力ファイルも出力ファイルも指定せずに起動して、キーボードから いきなり #pragma MCPP put_defines と打ち込むと、事前定義マクロをすべて知ることができます。-S1, -N 等のオプ ションを付けて起動すると、それぞれ事前定義マクロが違ってくることがわかり ます。 DEBUG == TRUE でコンパイルされた MCPP では、 #pragma MCPP put_defines とすると、それぞれのマクロ定義のあるソースファイル名と行番号を表示するコ メントも出力されます。 #pragma MCPP preprocess というディレクティブに出会うと MCPP は、 #pragma MCPP preprocessed という行を出力します。これは、このソースファイルはプリプロセス済みである ことを示すものです。 #pragma MCPP preprocessed というディレクティブに出会うと MCPP は、その ソースファイルは MCPP によってプリプロセス済みであると判断して、#define 行が出てくるまでは入力をそのまま出力にコピーします。そして、#define 行が 出てくると、あとはすべて #define 行であると判断して、マクロを定義します。 *1 その際に、DEBUG == TRUE でコンパイルされた MCPP では、コメント中にある ソースファイル名と行番号の情報も記憶します。*2 #pragma MCPP preprocessed の有効範囲はそのディレクティブのあるソースフ ァイルのその行以降だけです。そのソースファイルが #include されたものであ る場合は、include 元に戻ると通常のプリプロセスに戻ります。 *1 実際の処理はもう少し複雑である。#pragma MCPP preprocessed があると 入力行の大半をそのまま出力にコピーするが、ただし #line 行は処理系の コンパイラ本体が受け取れる形式に変換して出力する。また、標準事前定義 マクロは #define 行がコメントマークに囲まれているので、その行は捨て る。 *2 したがって、pre-preprocess してもマクロ定義の場所の情報は失われな い。逆に言うと、DEBUG == FALSE でコンパイルされた MCPP では、この情 報は使えない。 [3.1.1] ヘッダファイルの pre-preprocess 上記のディレクティブを利用すると、ヘッダファイルの「プリプリプロセス」 をすることができます。「プリプリプロセス」をしておくと、本番のプリプロセ ス時間がかなり短縮されます。その方法は、上記の仕様ですでにわかったかと思 いますが、念のために MCPP 自身のソースを例にとって説明します。 MCPP のソースには7本の *.c ファイルがあり、そのうちの6本はどれも "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_std > mcpp.H として MCPP を起動します(GNU C 等では、-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 したのとまったく同じ効果 を得ることができます。そして、これは標準ヘッダを含む元のヘッダファイルの 総計の数分の1のサイズになっています。#if とコメントが消えているからです。 これを6本あるいは7本の *.c ファイルで include するのは、system.H, internal.H を6回あるいは7回 include するのに比べて、はるかに短い時間で すみます。#pragma MCPP preprocess を使うことでさらに時間が短縮されます。 本番のコンパイルでは -DPREPROCESSED=1 というオプションを付けます。 この手順は何かのファイルに書いておいて、makefile でそれを参照するのが 良いでしょう。MCPP のソースに付けた makefile と preproc.c には、それが書 いてあるので、そちらを見てください。 Visual C, Borland C, LCC-Win32 のような1パス・コンパイラでは独立した cpp の使い道は少ないのですが、その場合でもこの機能は有用です。 このヘッダファイルの pre-preprocess の機能は、GNU C / cpp の -dD オプ ションの機能を真似たものです。ただし、次の点が違っています。 1. GNU C / cpp は行番号情報を #line 123 "filename" ではなく # 123 "filename" の形で出力する。このため、それを GNU C / cpp で再処理すること はできるが、Standard C のプリプロセッサではできない。 2. GNU C の古い cpp では #define 行は出現したところで出力されるが、# undef 行は出力されない。したがって、これを再処理すると元ソースの意図と異 なる結果になることがあった。 3. GNU C にはない #pragma MCPP preprocess を使うことで、さらに速度が 速くなる。 Pre-preprocess の機能としては、MCPP のほうが間違いがなく実用的です。 [3.2] #pragma once #pragma once は mcpp_std の(すなわち、__STDC__ が 0 以上の値に事前定 義されている)時だけ実装されます。*1 GNU C, Visual C, LCC-Win32 および Wave という単体プリプロセッサでは # pragma once も使えます。 ヘッダファイルを1回しかインクルードしたくない時に使います。ヘッダファ イルの中に #pragma once と書いておくと、そのファイルをインクルードする #include 行が何回出てきて も、最初の1回しかインクルードしません。 通常は、処理系付属の標準ヘッダでは #ifndef __STDIO_H #define __STDIO_H /* stdio.h の中身 */ #endif 等の皮でくるんで多重定義を防いでいますが、それと似た機能です。しかし、マ クロを使う方法ではヘッダファイルを読まないですますことはできません(スキ ップする部分でも、#if, #endif 等が出てくるのを監視するために、全部読まな ければならない。行頭の # がディレクティブ行(# に preprocessing directive が続く行)の指示であるかどうかを確かめるためにはコメントも処理 しなければならないし、そのためには文字列リテラルも判断しなければならない 等で、結局、全部読んで tokenization の大半までやらなければならないのであ る)。#pragma once は、ファイルへのアクセスさえもしないですますものです。 その結果、多重 include がある場合の処理速度がやや速くなります。 Header name が同じであるかどうかは、サーチしたパスのディレクトリ部分も 含めて単純に文字の比較で判断します。ただし、DOS/Windows 系では大文字・小 文字は区別しません。したがって、"/DIR1/header.h" と "/DIR2/header.h" は 別のものとして扱い、"header.h" と "HEADER.H" とは DOS/Windows 系では同じ もの、UNIX 系では別のものとして扱います。 この #pragma once は GNU C V.1.* / cpp の #pragma once のアイデアを借 用したものです。GNU C V.2.*, V.3.* / cpp でもこの機能は残っていますが、 obsolete なものとされています。#pragma once がなくても、ヘッダファイルの 全体が #ifndef _MACRO, #define _MACRO, #endif で囲まれていれば、cpp がこ れを記憶し、1回しか include しないという仕様に変更されています。 しかし、GNU C V.2, V.3 / cpp の仕様は、GNU C の使用を前提としない市販 の処理系などでは使えないことがあります。標準ヘッダの書き方が違っているか らです。また、実装も GNU C V.2, V.3 / cpp の仕様のほうが面倒です。そこで、 MCPP では #pragma once だけを実装しています。 他のプリプロセッサでも同じヘッダファイルを使う場合はこれだけに頼るわけ にはゆきません。マクロを使う方法と併用して、ヘッダファイルを次のような皮 でくるんでおくのが良いでしょう。 #ifndef __STDIO_H #define __STDIO_H #if __MCPP >= 2 #ifdef __STDC__ #pragma once #else #ifdef __cplusplus #pragma once #endif #endif #endif /* stdio.h の中身 */ #endif これは pre-Standard のプリプロセッサを使う場合も想定して、#if, #else, #ifdef だけ使い defined 演算子も使わないで書いていますが、pre-Standard のものを使うことがまったくないのであれば、次のように1行ですますことがで きます。 #pragma once ただし、 には #pragma once は書いてはいけません(その理由は cpp-test.txt [4.1.2] 参照)。C++ の , 等も同様です。 もう一つの問題は、GNU C / glibc の最近のシステムでは のよう に、他の system header から繰り返し #include されるヘッダファイルがある ことです。多くの system header が __need_NULL, __need_size_t, __need_ptrdiff_t, etc. のマクロを定義しては を #include しま す。そのたびに、 では NULL, size_t, ptrdiff_t, etc. が定義され てゆきます。, 等も同様です。 でさえも、他の system header が __need_FILE 等のマクロを定義しては #include し、そのたびに FILE 等が定義されてゆく場合があります。これらのファイルに は #pragma once は書き込むわけにはいきません。*2 *1 MCPP V.2.4.1 までは #pragma __once もあったが、必要はないと思われる ので削除した。 *2 少なくとも Linux / GNU C 2.9x, 3.2 / glibc 2.1, 2.2 ではそうなって いる。 FreeBSD 4.* では glibc は使われていないので、こういう複雑なシステム ヘッダにはなっていない。 [3.2.1] ヘッダファイルに #pragma once を書き込むツール これを書き込むのはヘッダファイルの数が少なければ大したことではありませ んが、数が多いと手ではうっとおしい作業になります。そこで、これを自動的に 書き込む簡単なツールを用意しました。 tool/ins_once.c は比較的古い GNU C のシステム用です。Borland C 4.0 で も標準ヘッダの書き方は同じルールに従っているので、これを使うことができま す。Glibc 2 のようなシステムでは上記のように例外が多いので、使わないほう が無難です。 ただし、これが使えるシステムでも、ヘッダファイルの中には GNU C の慣習 に従っていないものも散見されます。そうしたヘッダでは GNU C V.2.* / cpp の、1回しか読み込まないという機能も動作しません。 そこで、ins_once.c をコンパイルして、UNIX なら /usr/include, /usr/ local/include 等のディレクトリで、まず chmod u+w *.h */*.h */*/*.h とした上で、 ins_once -t *.h */*.h */*/*.h とします。そうすると、#ifndef または #if !defined で始まらないヘッダファ イルが報告されます。それらのヘッダを手で修正してください。それから、 ins_once *.h */*.h */*/*.h とすると、各ヘッダファイルの最初に出現する #directive が #ifndef または #if !defined であった場合は、その直後に #pragma once 行が書き込まれます。 (これができるのは root と特定のユーザだけのはず。さらに必要なら chmod u-w *.h */*.h */*/*.h として、access permission を元に戻しておく)。 ins_once には次のようなオプションがあります。システムに合わせて適当な オプションを選択してください。 -t: ファイルが(コメントを除くと)#ifndef か #if !defined で始まっ ているかどうかをテストする。ファイルは書き換えない。 -p: ファイルの冒頭に #pragma once 行を書き込む(デフォルトでは # ifndef / #if !defined 行の次に書き込む)。 -o: #pragma を受け付けない古いプリプロセッサも使う場合に備えて、上 記の9行を書き込む(デフォルトでは #pragma once の1行だけ)。 -g: GNU C の新しいシステムのために、, , , も書き換えない(デフォルトでは書き換えないのは , , だけ)。 ins_once は複数回実行しても同じファイルにダブって書き込むことはないよ うに、簡単なチェックはしています。しかし、厳密なものではありません。 この ins_once は間に合わせなので、tokenization はほとんどやっていませ ん。FreeBSD 2.0, 2.2.2, 2.2.7, Borland C 4.0 の各ヘッダファイルでは期待 通りの動作をしましたが、特殊なヘッダファイルがあると誤動作するかもしれま せん。ins_once は必ずバックアップをとってから実行してください。 ワイルドカードは shell に展開させてください(バッファがオーバーフロー する場合は、何回かに分けて実行する)。 [3.3] #pragma MCPP include_next, #pragma MCPP warning, #include_next, #warning これらのディレクティブは GNU C / cpp との互換性のために用意されている ものです。GNU C / cpp には #include_next, #warning という規格違反のディ レクティブがあります。規格違反ですが、これを使っているソースも稀にありま す。Glibc 2 のシステムでは、システムヘッダファイルにこれを使っているもの さえあります。そこで MCPP では、これらのソースをコンパイルできるようにす るため、GNU C 用に限って#include_next, #warning を実装しました。ただし、 mcpp_std ではウォーニングの対象となります。mcpp_std では #pragma MCPP include_next, #pragma MCPP warning も実装しました。これは GNU C 用に限り ません。 #pragma MCPP include_next あるいは #include_next は include directory をサーチする際に、header.h というファイルが最初に見 つかったディレクトリをスキップして、その次に見つかった header.h を include します。 DOS/Windows では、ヘッダ名の大文字・小文字の区別は無視します。 #pragma MCPP warning any message #warning any message では、any message をそのまま warning として標準エラー出力に出力します。 しかし、これは #error と違ってエラーにはなりません。 [3.4] #pragma MCPP push_macro, #pragma MCPP pop_macro, #pragma push_macro, #pragma pop_macro #pragma __setlocale, #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 は何重にもできます。 #pragma __setlocale( "") は multi-byte character encoding を に変更します。__setlocale の引数は文字列リテラルでなければな りません。 については [2.8] を見てください。これを使うと、1つ の translation unit の中でも複数の encoding を使うことができます。この # pragma は 32 ビット以上のシステムでしか使えません。 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 は mcpp_std のものです。 mcpp_prestd では #debug, #end_debug となります。DEBUG == TRUE and/or DEBUG_EVAL == TRUE でコンパイルしてあれば、それぞれに対応したデバッグ情 報の出力が実装されます。 #pragma MCPP debug ディレクティブはソース中の任意の行に書くこと ができます。 でデバッグ情報の種類を指定します。1つの #pragma MCPP debug ディレクティブで複数の を指定することができます。必ず1つ以 上の 指定が必要です。このディレクティブがあると、そこからデバッグ 情報の出力が始まります。そして、#pragma MCPP end_debug で、 のデバッグ情報出力が解除されます。#pragma MCPP end_debug では、 を 省略することができます。その場合は、設定されていたすべてのデバッグ情報出 力が解除されます。MCPP でサポートしない引数が にあった時は、ウォ ーニングを出しますが、その前にあった正しい引数は有効です。 デバッグ情報はすべて、プリプロセスの本来の出力と同じパスに出力されます。 これは本来の出力と同期させるためです。したがって、このディレクティブがあ るとコンパイルできません。MCPP の出力先を指定せずに、画面に出力して目で 追うのが、通常の使い方です。 プリプロセスの結果に疑問がある場合、その部分を #pragma MCPP debug token expand /* デバッグしたい部分 */ #pragma MCPP end_debug というふうにはさんで使います。 元来は MCPP 自身のデバッグ用のものですが、プリプロセスの過程をトレース したい時に使えます。元来の目的が目的なので、ソースを見ないと理解できない ところもあり、少々うるさくもありますが、がまんしてください。 この #pragma MCPP debug, #debug は system.H で DEBUG, DEBUG_EVAL の少 なくともどちらかが TRUE に定義されていないと実装されません。DEBUG == TRUE の場合は tokenization やマクロ展開のトレースができ、DEBUG_EVAL == TRUE の場合は #if 行の評価がトレースできます。 の種類は次の通りです。 DEBUG == TRUE の場合 path include ファイルのサーチパスを表示する token token を1つずつ切り分けて、その種類を表示する expand マクロ呼び出しの展開過程をトレースする if #if (#elif, #ifdef, #ifndef) の真偽を表示する getc プリプロセスを 1 byte ずつトレースする memory MCPP の使っているヒープメモリの状況を表示する DEBUG_EVAL == TRUE の場合 expression #if 式の評価をトレースする [3.5.1] #pragma MCPP debug path, #debug path このディレクティブに出会うと MCPP は、まず設定されている include ディ レクトリのサーチパスを優先順位の高いものから順に表示します(ただし、最初 にサーチされるカレントディレクトリおよびソースのディレクトリは省略)。 さらに、#include 行があると、そのヘッダファイルを include するために MCPP が実際にサーチしたディレクトリが(カレントディレクトリ等も含めて) すべて表示されます。#pragma once のあるヘッダファイルを再度 #include し た場合は、その旨を表示します。 [3.5.2] #pragma MCPP debug token, #debug token まず、読み込んだソース行を表示した上で、MCPP が token を1つ読むたびに、 その 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 (STR) string literal (NUM) preprocessing-number (WSTR) wide string literal (OPE) operator or punctuator (CHR) character constant (SPE) $, @ 等の特殊な pp-token (WCHR) wide character constant (SEP) token separator (white space) これらのうち (SEP) は改行コード以外は通常は表示されません。改行コード 等のコントロールコードは <^J>, <^M> 等と表示されます。 [3.5.3] #pragma MCPP debug expand, #debug expand マクロ呼び出しの展開過程をトレースします。 mcpp_std の #pragma MCPP debug では次の通りです。 マクロ呼び出しがあると、まずそのマクロの定義が表示されます。さらに、引 数が読み込まれ、置換リスト中のパラメータと置き換えられ、再走査されるよう すが、逐一表示されます。マクロ定義がネストされていれば、それが順次再走査 されて展開されていきます。引数中にマクロがあれば、この過程が再帰的に(パ ラメータとの置換の前に)トレースされます。 表示は mcpp_std 自身のいくつかの関数への出入りのたびに、その関数名とと もに行われます。これらの関数は次のような役割をするルーチンです。mcpp_std のソースを参照すると、さらによく理解できます。 expand マクロ展開の入り口ルーチン replace マクロを1レベル展開する collect_args 引数を集める prescan 置換リストを走査して #, ## 演算子の処理をする substitute パラメータを引数で置換する rescan 置換リストを再走査する これらのうち、expand 以外は互いに間接再帰の関係にあります。 replace, collect_args では、mcpp_std が内部的にスタックに積んでいる展 開途中のデータも表示されます。これらのデータでは、mcpp_std の内部的なコ ードが次のような記号で表示されます。 n 番目のパラメータ pp-token を区切るために MCPP が挿入した token separator 同名マクロの再置換を禁止するコード 置換リストの終わりを示すコード Identifier が再走査でソースファイルから取り込まれたもの であることを示すコード このうち は mcpp_std で poststd モードでも compat モードでもない 場合だけ使われます。 #pragma MCPP debug token も指定したほうが、わかりやすいでしょう。 mcpp_prestd の #debug expand では mcpp_std とは内部ルーチンが大幅に違 っています。説明は略します。 [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() という1バイト読み込み関数が呼び出されるたびに、詳細な データを出力します。ただし、mcpp_std では pp-token をスキャンする時は、 その1バイト目しか get() は呼び出されません。 #debug getc では token をスキャンする最中も get() が呼び出されるので、 とんでもない量のデータが吐き出されます。 いずれにしても、膨大なデータが出力されます。使う必要はまずありません。 [3.5.7] #pragma MCPP debug memory, #debug memory このディレクティブがあると、その時点で MCPP が内部的に使っている malloc(), realloc(), free() によるヒープメモリの状況を1回だけ報告します。 これは私の作った malloc() や他の何種類かの malloc() を使っている場合だけ の機能です(mcpp-porting.txt [4.extra] 参照)。他の malloc() の場合はエ ラーにはしませんが、何も報告しません。 このディレクティブが解除されないまま MCPP が終了すると、その時に再度ヒ ープメモリの状況が報告されます。MCPP が out of memory で終了した場合も同 様です。 [3.6] #assert, #asm, #endasm #assert は mcpp_prestd でだけ実装されます。GNU C 版では実装されません。 STANDARD の #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 や GNU C / cpp 等の #assert とは、まったく別 のものです。 #asm, #endasm の2つのディレクティブ行ではさまれたブロックはアセンブラ ソースとして扱われます。mcpp_prestd でだけ実装されます。ただし、これは Microware C / 09 用に書かれたものなので、他の処理系にも移植するには、 system.c の do_old(), do_asm(), put_asm() に書き足す必要があります。 #asm ブロックについては、trigraphs の変換と の削 除はしますが、コメントの処理や token チェックや文字チェックはせず、行頭 の space も削除せず、たまたまマクロと同じ名前があってもマクロ展開せず、 ソースの行をそのまま出力します。その他のディレクティブ行は #asm ブロック 内では意味を持ちません。 この #asm, #endasm は Standard C では認められないものです。まず、# pragma sub-directive 以外の拡張ディレクティブが規格外ですが、そればかり か、#pragma asm, #pragma endasm と名前を変えても解決しません。と言うのは、 Standard C ではソースはすべてCの token sequence で成り立っている必要が ありますが(厳密に言えば preprocessing token sequence)、アセンブラプロ グラムはCの token sequence ではないからです。Standard C でアセンブリコ ードを使うには、それを文字列リテラルという token に埋め込む方法しかあり ません。そして、それを処理する組み込み関数をコンパイラ本体に実装して、 asm( " leax _iob+13,y\n" " pshs x\n" ); といった形で呼び出すのです。 やや長いコードだとこんなことはやっていられないので、その場合はその部分 を別の関数にして、ライブラリ関数を書く時のように、別のファイルでアセンブ ラプログラムそのものを書いて、アセンブルパスで処理し、それをリンクして使 うことになります。これは窮屈な制限のように思えるかもしれませんが、port- able なCプログラムを書くにはアセンブラの部分は完全に分離する必要があり ますから、むしろ #asm を使わずに別ファイルで書くようにしたほうが良いでし ょう。 [3.7] C99 の新機能(_Pragma() 演算子、可変引数マクロ等) これは mcpp_std にだけ実装されます。 -V199901L オプションで __STDC_VERSION__ を 199901L 以上にすると、C99 の次の機能が有効になります。 C++ でも -V199901L オプションで __cplusplus を 199901L 以上にした場合 は同様です。1, 7 以外の仕様は C++ Standard にはありませんが、mcpp_std で は C99 との互換性を高めるために、このオプションを用意しています。 ただし、可変引数マクロは mcpp_std では C90 および C++ でも使えるように してあります。*1 1. // から行末までをコメントとして扱う。 2. 可変引数マクロが使える。 3. Preprocessing-number の中に e+, E+, e-, E- と同様に p+, P+, p-, P- という sequence も認める。これは浮動小数点数のビットパターンを 0x1. FFFFFEp+128 というふうに、16進で表記するためのものである。 4. _Pragma() operator が有効になる。 5. EXPAND_PRAGMA というマクロを TRUE に定義してコンパイルされた mcpp_std では、#pragma 行の引数は、STDC, MCPP, GCC のどれかで始まるので ない限りマクロ展開の対象となる(デフォルトでは EXPAND_PRAGMA == FALSE で あり、マクロ展開しない)。 6. long long を持つ処理系では #if 式の評価は long long, unsigned long long で行う。 7. 識別子・文字定数・文字列リテラル・pp-number の中にある \unnnn, \Unnnnnnnn の形の UCN という escape sequence を通す。これは Unicode の文 字の値を意味する。#if 式では UCN の値は16進表記として評価する。(ただ し、poststd モードでは UCN は使えない)。 可変引数マクロというのは、次のようなものです。 #define debug(...) fprintf(stderr, __VA_ARGS__) というマクロ定義があると、 debug( "X = %d\n", x); というマクロ呼び出しは次のように展開されます。 fprintf(stderr, "X = %d\n", x); すなわち、パラメータ・リスト中の ... が1個以上のパラメータを意味し、 置換リスト中の __VA_ARGS__ がそれに対応します。そして、マクロ呼び出し時 には ... に対応する引数が複数あっても、それらを , を含めて連結したものが 一つの引数のように扱われます。 _Pragma 演算子は _Pragma( "foo bar") と書くと #pragma foo bar と書いた のと同じ効果を持つ演算子です。引数は文字列リテラルまたはワイド文字列リテ ラル1個でなければなりません。ワイド文字列であれば接頭子 L を削除し、文 字列リテラルを囲む " を削除し、文字列リテラルの中の \", \\ をそれぞれ ", \ に置き換えたものが #pragma の引数として扱われます。 #pragma はソースの論理行1行の初めから終わりまでに書かなければならず、 引数が(少なくとも C90 では)マクロ展開されないのに対して、_Pragma() 演 算子はソースのどこに書いても独立した論理行に #pragma を書いたのと同じ効 果を持ち、マクロの置換リスト中に書くこともでき、マクロ展開の結果として生 成された _Pragma() operator も有効です。この柔軟性を利用することで、広い portability を持った pragma directive を書くことができ、処理系による # pragma の違いを1つのヘッダファイルで吸収することもできます。(サンプル としては "Validation Suite" の pragmas.h, pragmas.t を参照のこと)。*2 なお、C99 では #if 式の型はその処理系の最大の整数型となっています。 long long / unsigned long long は必須とされているので、#if 式の型は long long / unsigned long long またはそれ以上ということになります。 *1 これは GNU C との互換性のためである。他の処理系でも、C99 の仕様を一 挙に実装するのは難しいので、__STDC_VERSION__ を 199409L 等としたまま こうした一部の仕様から実装してゆくことが予想される。 *2 C99 では #pragma の引数が STDC で始まる場合はマクロ展開されないが、 そうでない場合は implementation-defined である。MCPP では system.H 中の EXPAND_PRAGMA というマクロを TRUE に定義してコンパイルされたも のでは、#pragma の引数をマクロ展開の対象とする。 [3.8] Borland C の asm 文その他の特殊な構文 Borland C には asm というキーワードがあって、 asm { mov x,4; ...; } といった形でアセンブリコードを記述するようになっていますが、これは #asm 以上にC言語の文法からはずれた極めて変則的なものです。この中にたまたまマ クロと同じ名前があると、それはマクロ展開されてしまいます。Borland C その ものでも MCPP でも、その点は同じです。アセンブラプログラムは別の .ASM フ ァイルで書くのが本当でしょう。 MCPP ではさらに、TOP_SPACE というマクロを FALSE に定義してコンパイルさ れた版では行頭の white spaces を削除するので、行中のカラムがずれます。 Visual C++ にも __asm という同様のキーワードがあります。 GNU C には asm( " mov x,4\n") というまっとうな形の組み込み関数が用意さ れています。 [3.9] GNU C / cpp との互換性 GNU C に移植された mcpp_std では、GNU C / cpp との互換性を実用上あまり 不便がない程度に確保していますが、非互換な面も多々あります。 まず実行時オプションについては、[2] に見るようにいろいろ違いがあります。 -A オプション等は実装していません。また、ディレクティブでは規格違反の # assert, #ident 等は実装していません。* しかし、幸いなことに、これらのことが原因でコンパイルできないというソー スはごく少ないようです。 むしろ実際に問題となるのは、古いプリプロセッサの特殊な仕様をあてにした ソースです。これらの多くは GNU C で -pedantic を指定するとウォーニングが 出ます。MCPP は mcpp_std のものではエラーチェックを規格通りに実装してい るので、ほぼ GNU C / cpp の -pedantic ないし -pedantic-errors がデフォル トとなっています。GNU C / cpp はデフォルトではそうした規格違反ソースを黙 って通すため、それをあてにしたソースが一部に見られます。そうしたソースを 規格に合致するように書くのはきわめて簡単なことで、わざわざ規格違反の書き 方をする必然性は何もありません。単に移植性を損なうだけで、悪くするとバグ の温床となるので、見つけしだい直しておきましょう。 * これらはいずれも必要なら #pragma で実装すべきものである。 #include_next, #warning も同様であるが、GNU C のシステムでは実際に時 々使われているので、MCPP でもやむなく実装した。ただし、ウォーニング の対象になる。 [3.9.1] FreeBSD 2 / kernel ソースのプリプロセス 以下に、FreeBSD 2.2.2-R (1997/05) の kernel ソースを例に問題点を挙げて おきます。ディレクトリ名はいずれも /sys (/usr/src/sys) 中のものです。こ れらのうち 7, 8 は必ずしも規格違反ではなく、MCPP でも期待通りの処理をし ます。しかし、あぶなっかしい書き方なのでウォーニングが出ます。6 は拡張機 能で、C99 でも同じ機能が用意されていますが、GNU C / 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 ... "); 文字列リテラルを閉じる " が行末までになかった場合は行末で閉じられてい ると解釈するのが GNU C / 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 では二重定義の結果は undefined で、実際には処理系によって、 エラーにした上で初めの定義を有効とするものと、GNU C 2 / cpp のように黙っ てあとの定義を有効とするものとあります。確実にあとの定義を有効にするには、 直前に #undef DEBUG を入れるべきです。 [3.9.1.5] #warning i386/isa/if_ze.c, i386/isa/if_zp.c には #warning があります。Kernel ソ ース中で唯一の規格違反ディレクティブです。Standard C に合わせるためには、 この行をコメントアウトするしかありません。 MCPP V.2.3 以降では #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); \ } これは GNU C / 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 では ... に対応する呼び出し時の引数は1個以上必要なのに対して、GNU C / cpp では a... に対応する引数は0個でもかまわないというのが、やっかい な相違点です。MCPP V.2.3 以降ではこれに対処するため、 ... に対応する引数 が1個もない場合は、warning は出すもののエラーにはしないようにしています。 したがって、次のような書き方もできます。このほうが書き換えは一対一対応で できるので、簡単です。しかし、この書き方では、カラ引数の直前のコンマが残 るので、たとえば printf( fmt, ) 等という展開結果になってしまうことがあり ます。その場合は、マクロ定義を上記の C99 仕様の書き方にするか、またはマ クロ呼び出しでカラ引数を使わないようにするしかありません。カラ引数の代わ りには NULL や 0 のような無害なトークンを使って、MCD_TRACE( fmt, NULL) 等と書くことになります。* #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__); \ } * MCPP を使うにはこの形にソースを書き換える必要がある。さらに -Q オプ ションも付ければ、大量のウォーニングが画面ではなく mcpp.err というフ ァイルに出力される。 GNU C 2.95.3 以降では C99 の構文の可変引数マクロも実装されている。今 後はこちらを使うほうが良い。GNU の可変引数マクロは引数がゼロ個でも良 いという柔軟性があるが、その記法は良くない。args... というパラメータ では args と ... とはくっついていなければならないが、こういう pp- token は存在しない。置換リストで可変引数を示すのにトークン連結演算子 と同じ記法を使っているのも、感心しない。C99 の記法で、ゼロ個の可変引 数も許容するという仕様が妥当であろう。 なお、GNU C 3 では可変引数マクロについて、GNU C 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 です。ネストされたマクロ呼び出しでたまたま引数がカラになった場 合のこと等を考えると、カラ引数が規定されているほうが良いと言えますが、ソ ース中にカラ引数を書くことにはこれらの場合は必然性がなく、感心しません。 引数が1つのマクロではカラ引数と引数の欠落との区別がつかないという 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_OLD というマクロでも、やはり第一引数等はマクロ展開されない)。こ れはカラ引数のままにしておくしかありません。* * 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 でも同様です(同種の定義が4つある)。 #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 ではなぜか合法となったものです。 しかし、私が別のところで論じているように、この仕様こそがマクロ展開を無用 に複雑にし、規格書にまで混乱をもたらしている元凶であり、「バグのような仕 様」なのです。* この例は実態は 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) * ISO 9899:1990 の Corrigendum 1:1994 ではこうした例は undefined とさ れた。そして、C99 では Corrigendum のこの項は別のものと置き換えられ た。しかし、規格書はこれに関しては混乱している。詳細は cpp-test.txt の [1.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 も削除せずに保存されなければなりません。行の最初の「トークン」がア センブラのコメントである # である場合はプリプロセッサの側で特殊な処理が 必要です。使えるプリプロセッサがかなり制限され、バグの混入にも余計な神経 を使わなければならないので、決して良いことだとは思えません。* 複数のマシンに対応させる等のためにプリプロセスする必要があるのであれば、 .S ファイルではなく .c ファイルとして、次の形で書くのが良いでしょう。4. 4BSD-Lite では実際にこの書き方になっています。 asm( " asm code0\n" #ifdef Machine_A " asm code1\n" #else " asm code2\n" #endif " ...\n" ); * 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 からそのまま来てい るからでしょうか。これだけの規模できれいなソースがそろっているのは珍しい ことで、特筆に値します。 ただ1個所、gen/getgrent.c に次のような行が見つかりました。もちろん、 行末の ; は余計です。 #endif; [3.9.3] GNU C 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 の 形に書き替えられているものさえあります。いったん一掃されたものが、なぜ復 活するのでしょうか? 私はここには、GNU C / cpp がこれらを黙って通してしまうことの悪影響が現 れていると思います。-pedantic の動作がデフォルトであれば、こうしたスタイ ルのソースがわざわざ「新しく」書かれることはなかったでしょう。かつては -pedantic-errors をデフォルトにしたのではコンパイルできないソースが多く て、実用的ではなかったのかもしれません。gcc の man page には -pedantic オプションについて、「これを使う理由は何もない。このオプションの存在理由 は pedants を満足させることだけである」とあります(*)。しかし Standard C が決まって8年もたついまとなっては、-pedantic-errors とまではいかなくて も -pedantic をデフォルトにすべき時に来ていると思われます。 FreeBSD 2.0-R ではコメントのネストが時々見られましたが、2.2.2-R では一 掃されています。一掃されたのは、GNU C / cpp が認めなくなったからです。こ れは -pedantic とは関係ありませんが、プリプロセッサのソースチェックの威 力はそれほど大きいのです。 * この [3.9.3] が書かれたのは 1998 のことである。その後、gcc の man や info では、さすがにこの表現は削除された。しかし、仕様が特に変わった わけではない。 [3.9.4] Linux / glibc 2.1 ソースのプリプロセス Vine Linux 2.1 (i386) で使われている glibc 2.1.3 (2000/09) のソースを リコンパイルしてみました。これには FreeBSD の libc とは違ってかなり多く の問題があります。中には GNU C / 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 にも行をまた ぐ文字列リテラルが現れます。 これはもちろん規格違反のソースですが、GNU C ではこれを改行コードの入っ た文字列リテラルとして扱います。 MCPP V.2.3 以降では -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 V.2.3 以降でも GNU C 用では #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 以上のファイルには GNU C / cpp の仕様の可変個引数マクロの定義と呼び出 しがあります。 これは C99 の仕様と異なっているので、MCPP ではソースを書き直す必要があ ります。 [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 です。 ところが GNU C / cpp では、#if 行でなければこうなるのですが、#if 行に 限って (1) でマクロ展開をやめてしまい、これを #if 式として評価します。 Undefined であるのでそれも間違いとは言えませんが、マクロ展開が #if 行と そうでない場合とで異なるのは、一貫しない仕様です。少なくともその仕様には portability がありません。* 問題のマクロは次のように書けば、何も問題ないのです。 #if defined(__linux__) && !defined(__arm__) #define HAVE_MREMAP 1 #endif こういう危なっかしいソースは早く一掃されてほしいものです。 *1 GNU C 2 / cpp は #if 行では内部的に defined を特殊なマクロとして扱 っている。そのため、次のようなトークン列をマクロ展開のために再走査す るのであるが、その結果がこれをマクロ展開せずに、#if 式として評価する ことになるのである。すなわち、マクロ展開と #if 式の評価とが分離せず に混交しているのである。 defined(__linux__) && !defined(__arm__) これは GNU C / cpp のプログラム構造にもかかわる問題である。GNU C 2 / cpp では rescan() というマクロ再走査ルーチンが事実上のメインルーチン となっていて、これがソーステキストを初めから終わりまで読みながら処理 してゆく。そして、プリプロセスディレクティブの処理ルーチンもこの中か ら呼び出されるのである。何でもマクロで実装するのはマクロプロセッサの 伝統的なプログラム構造であるが、この構造が、マクロ展開と他の処理との 混交を引き起こす背景になっていると考えられる。 [3.9.4.7] .S ファイルの「プリプロセス」 *.S という名前のファイルはプリプロセスを要するアセンブラのソースですが、 その中には #include, #define, #if 等のプリプロセスディレクティブが出てき ます。さらに、Make によって生成される compile/csu/crti.S というファイル には、 #APP とか #NO_APP という行まで現れます。これらの行は無効なプリプロセスディレクティブと構文 上、区別がつきません。GNU ではこれらの行はそのままプリプロセス後に残って、 アセンブラのコメントとして扱われるようです。 また、## 演算子による pp-token の連結が invalid な pp-token を生成して しまうソースもあります。GNU C / cpp はこれを黙ってそのまま出力します。 MCPP V.2.3 では GNU C / cpp との互換性のためにやむなく、-lang-asm (-x assembler-with-cpp, -a) オプションを付けると、こうした illegal なディレ クティブや ## によって生成された invalid な pp-token をエラーにせず、ウ ォーニングを出すもののそのまま出力するようにしました。 こうしたソースは本来、アセンブラ用のマクロプロセッサで処理すべきものだ と思われます。GNU にも gasp というアセンブラ用マクロプロセッサがあるよう ですが、なぜかほとんど使われていないようです。 [3.9.4.8] rpcgen と -dM オプションの仕様の問題 GNU C / cpp は -dM というオプションで起動するとマクロ定義だけを出力し ますが、make check で使われる stdlib/isomac.c はこれを利用しています。 isomac.c の問題は、マクロ定義ファイルの形式として GNU C / cpp の出力形式 だけを想定していて、コメントも空行もエラーになってしまうことです。* glibc の make では rpcgen というプログラムが使われることがあります。こ のプログラムの問題は、プリプロセッサの行番号情報の出力形式としてやはり GNU C / cpp の #123 "filename" という形式だけを想定していて、 #line 123 も #line 123 "filename" もエラーになってしまうことです。 MCPP では V.2.3 から、GNU C 版では GNU C / cpp の形式をデフォルトにし ました。しかし、rpcgen がこういう特殊な形式を前提にして、標準的な形式に 対応していないというのは、お粗末な仕様です。 * MCPP V.2.5 では -d* オプションの出力は GNU C と同じフォーマットに変 更した。 [3.9.4.9] -include, -isystem, -I- オプション glibc 2 の makefile では、-include オプションがしばしば使われています。 時には -isystem オプションや -I- オプションも使われます。-include はソー スの冒頭で #include すればすむもので、-isystem, -I- はシステムヘッダを更 新する場合しか必要性の感じられないものです。 MCPP V.2.3 では GNU C 用の実装に限って、この3つのオプションも実装しま したが、あまり必要のないオプションは整理してもらいたいものです。* *2 GNU C / 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 な事前定義マクロ __VERSION__, __SIZE_TYPE__, __PTRDIFF_TYPE__, __WCHAR_TYPE__ 以上の名前はドキュメントには見当たりませんが、GNU C / cpp では事前定義 マクロとなっています。__VERSION__ の値は Vine Linux 2.1 (egcs-1.1.2) で は "egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)" となっています。他 の3つは Linux / i386 をはじめ多くのシステムではそれぞれ unsigned int, int, long int となっているようです。しかし、FreeBSD, CygWIN では少しずつ 違っています(なぜ違う必要があるのかわからないが)。 こういうことがどうしてドキュメントにないのでしょうか。 __VERSION__ の値は GNU C / cpp ではそれ自身のソース中で設定されていま すが、MCPP では GCC_VERSION という環境変数で指定するようにしました。* * GNU C V.3 については MCPP V.2.5 以降では configure で設定されるので、 環境変数は必要ない。 [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 がソース中のマクロ定義とソースファイルの依存関係行 をその指定されたファイルに出力するというものなのです。 この動作を理解するには、GNU C / cpp のソース (egcs-1.1.2/gcc/cccp.c) を読むしかありませんでした。 このほか、DEPENDENCIES_OUTPUT という環境変数もあり、同様の意味を持って います。SUNPRO_DEPENDENCIES のほうは system headers の依存関係行も出力す るのに対して、DEPENDENCIES_OUTPUT はそうではないという違いがあります。 MCPP V.2.3 以降では GNU C 対応版に限って、この2つの環境変数に対応させ ましたが、こういう「裏仕様」のようなものは早く廃止してほしいものです。 [3.9.4.12] その他の問題 Linux (i386) / GNU C ではこのほか、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 の ソースが FreeBSD 2 / kernel のソースより整理されている面もいくらかありま す。 しかし全体としては、glibc 2 には GNU C の特殊な仕様に依存しているソー スが稀ならずあり、他の処理系への移植は困難になっています(数千本のソース ファイルの中ではごく一部であるが)。プログラムの可読性やメンテナンス性の ためにも、こうした GNU C local な仕様への依存は好ましくありません。GNU C V.3 ではこれらの裏技的仕様を廃止し、それに依存するソースを一掃することを 期待したいと思います。 [3.9.5] GNU C 2 で MCPP を使うには MCPP V.2.3 以降を glibc 2 のコンパイルに使うには、まず一部のソースの修 正が必要です。 一つは可変個引数のマクロの定義と呼び出しです。上記 [3.9.4.3] にある 14 個のファイルについて、[3.9.1.6] にあるような形で修正します。もちろん、元 のファイルは *.orig といった名前で残しておいたほうが良いでしょう。 もう一つは、[3.9.4.6] にある3つのファイルの、置換リストに "defined" が出てくるマクロの修正です。また、/usr/include/_G_config.h は sysdeps/ unix/sysv/linux/_G_config.h が install されてできる同一のファイルですが、 こちらも修正しておいたほうが良いでしょう。 MCPP の起動には、Makefile や specs ファイルで付加されるオプションのほ かに、行をまたぐ文字列リテラルやアセンブラ用のコメントを含む *.S ファイ ルのために -lang-asm (-x assembler-with-cpp) が必要です。このオプション は他のファイルのプリプロセスにも付けておいて、通常はかまいません。 GNU C / cpp を使ったり MCPP を使ったり、デフォルトで付加するオプション を変更したりするためには、次のようにするのが良いでしょう。 Super-user になって、cpp の存在するディレクトリ(Vine Linux 2.1 では /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66)に行きます。ここに GNU C / cpp が cpp という名前で存在し、MCPP が cpp_std という名前で install されているとします。まず、次のような内容の mcpp.sh という名前のファイル を作ります。*1, *2, *3 #!/bin/sh /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/mcpp_std \ -v -Q -lang-asm "$@" -v, -Q オプションはなくてもかまいませんが、大量の診断メッセージを記録 するには -Q を付けたほうが良く、MCPP の起動を確認するためには -v を付け たほうが良いでしょう(同様に、gcc も shell-script から -v を付けて呼び出 すようにするとわかりやすい)。 さらに、次のようなコマンドを打ち込みます。 chmod a+x mcpp.sh mv cpp cpp_gnuc ln -sf mcpp.sh cpp こうしておくと、gcc が cpp を呼び出した時に、それにリンクされている mcpp.sh が実行され、mcpp_std に上記のオプションを(gcc の付加するオプシ ョンの前に)付加して呼び出します。 デフォルトのオプションを変更する時は、mcpp.sh を変更するなり、mcpp_std を直接呼び出すなりします。そして、GNU C / cpp を使う時は ln -sf cpp_gnuc cpp とします。 この設定で MCPP をリンクしっぱなしにしていると、問題の発生することがあ ります。各種のプログラムの中には -traditional オプションをつけて cpp を 呼び出すものがあるからです。MCPP は(STANDARD モードでは)-traditional は実装していません。そこで、mcpp.sh を修正して、次のように -traditional が指定された時は GNU C / cpp を、そうでなければ MCPP をと、 自動的に選択されるようにしておくのが良いでしょう。*4 #!/bin/sh for i in $@ do case $i in "-traditional*") /usr/lib/gcc-lib/i386-redhat-linux/2.91.66/cpp_gnuc "$@" exit ;; esac done /usr/lib/gcc-lib/i386-redhat-linux/2.91.66/mcpp_std -Q -lang-asm "$@" MCPP を使う場合のもう一つの問題は、大量のウォーニングが出力されること です。-Q オプションでリダイレクトしても、glibc のような大規模なソースを 処理すると、-W3 オプションの場合は mcpp.err が総計数百 MB にもなるので、 すべてに目を通すわけにはいきません。 しかし、この内容を見ると、同じウォーニングが繰り返し出ていることがわか ります。同じ *.h ファイルが多くのソースから #include されるために、同じ ウォーニングが繰り返し繰り返し出るのです。これを整理して見るには、次のよ うにします。 まず、エラーをチェックします。 grep fatal `find . -name mcpp.err` grep error `find . -name mcpp.err` 次に、ウォーニングを整理します。 grep warning `find . -name mcpp.err` | sort -k4 -u | less ウォーニングの出所をすべて見るためには、次のようにします。 grep warning `find . -name mcpp.err` | sort -k4 | uniq | less 特定の種類のウォーニングを見るには、たとえば次のようにします。 grep 'warning: Replacement' `find . -name mcpp.err` | sort -k4 \ | uniq | less こうして見当をつけたうえで、該当する mcpp.err を less で見て内容を確認 し、必要ならソースを見ます。 さらに必要なら、ソースの問題の個所を #pragma MCPP debug expand, # pragma MCPP end_debug 等で挟んで再度プリプロセスして、その出力を見ます。 この時には、プリプロセスの出力と診断メッセージとが同じファイルに出るよう に、次のようにします(make する場合は、上記の shell-script を一時書き換 える)。 mcpp_std <-opts> in-file.c > in-file.i 2>&1 *1 VineLinux 2.5 / GNU C 2.95.3 では、/usr/lib/gcc-lib/i386-redhat- linux/2.95.3 ディレクトリに cpp のほかに cpp0 というファイルがある。 そして、cpp は cpp0 へのリンクとなっている。すなわち、プリプロセッサ の実体は cpp0 である。Gcc は直接 cpp0 を呼び出す。Cpp1, cpp2 等が将 来は用意されるということであろうか? ともかく、この場合は上記の mv, ln 等のコマンドでは cpp を cpp0 とする必要がある。 FreeBSD 4.4 / GNU C 2.95.3 でも /usr/libexec に cpp と cpp0 がある。 *2 VineLinux 2.5 / GNU C 2.95.3 ではそのほかにも次のようないくつもの cpp があり、それぞれが他の cpp へのリンクになっている。そして、最後 の /usr/bin/cpp-2.95.3 はリンクではなく実行プログラムであるが、これ は何と /usr/lib/gcc-lib/i386-redhat-linux/2.95.3/cpp0 を呼び出すので ある。ややこしいことである。どうしてこんなことになってしまったのであ ろうか? /lib/cpp -> /etc/alternatives/libcpp /etc/alternatives/libcpp -> /usr/bin/cpp-2.95.3 /etc/alternatives/cpp -> /usr/bin/cpp-2.95.3 /usr/bin/cpp -> /etc/alternatives/cpp /usr/bin/cpp-2.95.3 *3 私の検証セットを GNU C / testsuite で MCPP に適用する場合は、オプシ ョンは -23j としておかなければならない。digraph, trigraph を有効にし、 診断メッセージにソース行等の付加情報を出力しない GNU C に近い形式に するためである。他のオプションはこの shell-script では指定してはいけ ない。検証セットを GNU C / testsuite で使うことについては、cpp-test. txt [2.2.3] を参照のこと。 *4 これは VineLinux 2.1 / GNU C 2.91.66 用のものである。FreeBSD, CygWIN および Linux の他のバージョンでは、cpp のあるディレクトリと cpp の名前(cpp か cpp0 か)を書き換えれば良い。 また、GNU C 3.2 では tradcpp0 という traditional モードのプリプロセ ッサが独立しており、gcc に -traditional オプションを付けるとこれが呼 び出される。したがって、GNU C 3.2 ではこの shell-script は必要ない。 [3.9.6] GNU C 3.2 ソースのプリプロセス Linux および FreeBSD で、GNU C 2.95.* で GNU C 3.2R (2002/08) のソース をコンパイルしてみました。そして、生成された gcc を使って MCPP をコンパ イルし、次にプリプロセスにそれを使って GNU C 3.2 で GNU C 3.2 のソースを リコンパイルしてみました。 GNU C の make はいくつかの段階を経て bootstrap されてゆくようになって います。すなわち、最初の段階で生成された gcc, cc1, etc. を使って自分自身 をリコンパイルし、そうやって再生成されたものを使ってまた自分自身をリコン パイルし、といった経過をたどります。gcc は bootstrap の途中では xgcc と いう名前で存在しています。 また、GNU C 2 では cc1, cc1plus からは独立していた cpp が、GNU C 3 で は cc1, cc1plus に吸収されてしまいました。しかし、独立した cpp (cpp0) も 存在しており、gcc や g++ に -no-integrated-cpp というオプションを付ける とこれにプリプロセスをさせることができるようになっています。したがって、 MCPP にプリプロセスをさせるためには、gcc (xgcc), g++ の呼び出しを shell- script に置き換えて、MCPP => cc1 または MCPP => cc1plus という順序で実行 されるようにしなければなりません。*1 GNU C のシステムでは、システムヘッダやそのサーチ順の設定が非常に複雑に なってきています。また、GNU C 3 では GNU C が内部的に使う C++ の shared library の仕様が GNU C 2 とは変わったようです。これらのためか、コンパイ ルするだけでもうまくゆかないことがあります。また、コンパイルとテストには 多くの他のソフトウェアも必要で、それらのバージョンが古いと、うまくゆかな いことがあります。私のところではハードウェアの問題でうまくコンパイルでき ないこともありました。 FreeBSD 4.4R では GCC 3.2 はコンパイルできませんでした。FreeBSD を 4. 7R に upgrade し、packages を 4.7 用のものに入れ替えて、ようやくコンパイ ルすることができました。*2 私のところでは2台のパソコンに同じ VineLinux 2.5 が入っていますが、そ の片方 (K6/200MHz) では GNU C 2.95.3 を使ってのコンパイルはできたものの、 生成された GNU C 3.2 / cc1 が segmentation fault を繰り返し起こしてしま い、自分自身のリコンパイルができませんでした。その後、K6 を AthlonXP に 取り替えてリコンパイルしたところ、今度は segmentation fault は発生しませ んでした。問題はハードウェアだったのかもしれません。 また、FreeBSD では K6 のパソコンで GNU C 2.95.4 を使って GNU C 3.2 を コンパイルした時は、make -k check ではほとんどすべて通ったのですが、さら に生成された GNU C 3.2 で GNU C 3.2 自身をリコンパイルしたところ、今度は、 g++, libstdc++-v3 が make -k check で testsuite の2割近くが通らないとい う現象も起こりました。しかし、AthlonXP に取り替えてからはうまくゆくよう になりました。これもハードウェアの問題だったのかもしれません。 また、VineLinux で GNU C 3.2 自身と MCPP を使ってリコンパイルした場合 は、生成された gcc は make -k check を通りましたが、g++, libstdc++-v3 は testsuite の2割近くが通りませんでした。*3, *4, *5 どちらにしても、生成された gcc, g++, cc1, cc1plus 等の問題ではなく、ヘ ッダファイルかライブラリか何かの設定の微妙な問題のようです。 MCPP は GNU C / cpp とは完全に互換ではありませんが、かなり高い互換性を もっているので、取り替えて使ってほぼ問題はないと思われます。 GNU C 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++ だけです。 なお、私の「Cプリプロセス検証セット」V.1.3 には GNU C の testsuite で 使える edition が追加されました。これを使うと、make -k check または runtest でプリプロセスの詳細でシステマティックなテストができます。これは ソースをチェックするものではなく、プリプロセッサの動作チェックをするもの です。これについては cpp-test.txt [2.2.3] を見てください。 *1 これを bootstrap の各段階ごとにやらなければならないのである。 Makefile は手を入れるにはあまりに大きく複雑なので、画面に張り付いて いて、stage が変わったところで ^C で中断して、script に置き換えると いう不細工な方法をとった。 *2 しかも、多くの packages の間の依存関係があるので、バージョンがまち まちだと混乱が生じる。私のところではこのために、一時は kterm が起動 しないという状態に陥ったこともある。 *3 make -k check する時は MCPP は使ってはいけない。診断メッセージが GNU C / cpp とは異なるからである。 *4 GNU C を configure する時に --enable-c-mbchar というオプションを付 けると、環境変数 LANG, LC_ALL 等で multi-byte character の encoding を指定できるようになる。しかし、このオプションを付けなかった場合は、 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] 行をまたぐ文字列リテラル このあまりにも古い書き方は GNU C 3.2 のソースにはありません。この仕様 は GNU C 3.2 でようやく obsolete とされました。ソース中にこれがあると、 期待通りに処理はされますが、ウォーニングが出ます。 [3.9.6.2] #include_next, #warning Make の途中で生成される build/gcc/include の limits.h, syslimits.h に は #include_next があり、GNU C 3.2 を install すると、lib/gcc-lib/i686- pc-linux-gnu/3.2/include の limits.h, syslimits.h にコピーされます。 #warning は見当たりませんでした。 [3.9.6.3] 可変引数マクロ 可変引数マクロはいくつかありますが、大半は testsuite のもので、テスト 用に書かれたサンプルにすぎません。そして、GNU C 2 以来の記法はまだサポー トされてはいるものの、__VA_ARGS__ を使った C99 のものが多くなっています。 また、GNU C 3 では GNU C 2 の仕様と C99 の仕様との折衷的な書き方が追加 されています。次の形です。 #define eprintf( fmt, ...) fprintf( stderr, fmt, ##__VA_ARGS__) これは、... に対応する引数がなかった時は、その直前のコンマを削除すると いう仕様です。たとえば、次のように展開されます。 eprintf( "success!\n") ==> fprintf( stderr, "success!\n") この例を見ると便利な仕様のようですが、マクロ定義の置換リスト中のコンマ はパラメータを区切るものとは限らないこと、トークン連結演算子である ## に 別の働きをさせていること、規則に例外を作って複雑にするものであること、等 の問題があります。MCPP ではこの機能は実装していません。この形のマクロ定 義でもエラーにはなりませんが、マクロ呼び出しに際して、カラ引数の直前のコ ンマは削除しません。 [3.9.6.4] マクロ呼び出しのカラ引数 マクロ呼び出しのカラ引数は、#include されるシステムヘッダのものを別と すると(/usr/include/bits/mathcalls.h, /usr/include/bits/sigset.h)、GNU C 3.2 のソース自体では、gcc/libgcc2.h にだけ見られます。* * この2つのヘッダファイルは 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' に展開されるマクロ これは GNU C 3.2 のソースには見当たりません。 ドキュメントによると、この種のマクロの処理は GNU C 2 / cpp と同じであ るものの、「portability がない」というウォーニングを出すことになっていま す。ただ、テストしてみると、[3.9.4.6] の例はウォーニングが出ないようです。 [3.9.6.7] .S ファイルの「プリプロセス」 GNU C 3 / cpp のドキュメントには、「CプリプロセッサをC以外の言語のテ キストプロセッサに流用するのは注意が必要であり、できるだけその言語用のプ ロセッサを使うべきである、アセンブラにもマクロ処理の機能がある」という意 味のことが書かれています。 しかし、GNU C 3.2 自身のソースには、*.S ファイルが gcc/config ディレク トリに数本あります。 [3.9.6.8] rpcgen と -dM オプションの仕様の問題 GNU C 3.2 の make では rpcgen も -dM オプションも使われませんでした。 しかし、rpcgen や -dM オプションの仕様は特に変わってはいないようです。 [3.9.6.9] -include, -isystem, -I- オプション これらのオプションはしばしば使われており、-isystem オプションで system include directory が同時に数個指定される場合さえあります。こういう、シス テムヘッダそのものを更新するソフトウェアのコンパイルでは、やむをえないこ となのでしょうか。環境変数で一括して指定したほうが、わかりやすいと思うの ですが。 一方で、GNU C 3 / cpp のドキュメントでは、-iwithprefix, -iwithprefixbefore オプションについては、「使わないほうが良い (discouraged)」と書かれています。GNU C には include directory を指定する オプションがやたらにありますが、整理する方向に入ってきたのでしょうか。* * しかし、GNU C 3.2 の Makefile は -iprefix オプションを付けている。と ころが -iwithprefix, -iwithprefixbefore は使われないのである。 -iprefix オプションは、その後にこの2つのオプションのどちらかがあっ て初めて意味を持つのであるが。 [3.9.6.10] Undocumented な事前定義マクロ GNU C 2 では __VERSION__, __SIZE_TYPE__, __PTRDIFF_TYPE__, __WCHAR_TYPE__ 等の事前定義マクロについては、ドキュメントに記載がなく、 -dM オプションでも知ることができませんでしたが、GNU C 3 ではドキュメント に意味が記載され、具体的な値も -dM で知ることができるようになりました。 [3.9.6.11] Undocumented な環境変数 GNU C 2 ではドキュメントに記載のなかった SUNPRO_DEPENDENCIES という環 境変数については、GNU C 3 ではドキュメントに記載されるようになりました (しかし、なぜこんなものが必要なのかはわからない)。 [3.9.6.12] その他の問題 GNU C 3 / cpp では次のような #pragma が実装されています。 #pragma GCC poison #pragma GCC dependency #pragma GCC system_header GNU C 3.2 のソースでも、このうち poison と system_header が使われてい ます。しかし、MCPP ではこれらはサポートしていません。仕様の説明は省きま すが、必要性があまり感じられないからです。 GNU C 3 では #assert 等の assertion directives は「推奨しない (deprecated)」とされました(しかし、gcc はデフォルトで -A オプションを発 行するが)。 また、GNU C 2 では -traditional オプションは同一の cpp で実装されてお り、そのため非常に古い仕様と C90 の仕様が混在する奇怪な仕様となっていま したが、GNU C 3 ではプリプロセッサが通常の cpp0 と tradcpp0 とに分けられ ました。-traditional オプションは gcc に対してだけ有効で、cpp0 にはあり ません。gcc -traditional はプリプロセスに tradcpp0 を呼び出します。 tradcpp0 は C90 以前の真に traditional なプリプロセッサに近いものとなっ ています。そして、tradcpp0 のほうは今後、深刻なバグの修正以外はメンテナ ンスしないとされています。 GNU C 2 / cpp の奇妙な仕様はだいぶ修正されてきたようです。 [3.9.7] GNU C 3 で MCPP を使うには 以上に見てきたように、GNU C 3.2 のソースは少なくともプリプロセス上は、 glibc 2.1.3 などに比べるとかなりきれいなものになっています。Traditional な書き方はほぼ一掃され、意味のないオプションは使われなくなってきています。 また、GNU C 3.2 / cpp そのものも、traditional な仕様を obsolete なもの として扱い、token-based な原則を明確にするなど、GNU C 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)で、GNU C 2 の場合と同様に、 cpp, cpp0 の呼び出しで MCPP が実行されるようにしておきます([3.9.5] 参照)。 こうしておくと、gcc や g++ からまず MCPP が呼び出され、その後に cc1, cc1plus が -fpreprocessed というプリプロセス済みであることを示すオプショ ンを付けて呼び出されるようになります。 このほか、GNU C の他のバージョンの場合と同様に、環境変数をセットします ([3.9.5] 参照)。PATH の設定等も必要です。* なお、システムの標準と異なるバージョンの GNU C をインストールした場合 は付加的な include directory の設定が必要なことがありますが、MCPP V.2.4 ではこれらも MCPP のコンパイル時に組み込むようになったので、通常は環境変 数で設定する必要はありません。 私のところの Linux の例では、/etc/ld.so.conf に /usr/local/gcc-3.2/lib という行を追加し、~/.bash_profile に次のような設定を加えています。 export PATH=/usr/local/gcc-3.2/bin:$PATH できれば cc1, cc1plus のプリプロセス部分である cpplib のソースを MCPP のものに置き換えたいところですが、cpplib の cc1, cc1plus との内部的な interface および cpplib を使うユーザプログラムとの外部的な interface を 定義しているソースファイルが合わせて 46KB もあり、とても置き換えは不可能 です。どうしてこういう複雑な interface にする必要があるのでしょうか。残 念なことです。 * MCPP V.2.5 では GNU C V.3.* については configure で必要な設定をすべ て取得できるようになったので、GCC_VERSION 等の環境変数は必要なくなっ た。 [3.9.7.1] GNU C 3.3, 3.4 で MCPP を使うには 良い方向に向かっていた GNU C V.3 でしたが、V.3.3 からは妙な方向に転換 してしまいました。V.3.3 は V.3.2 と比べると、次のような点で大きく変わっ ています。 1. 単体の cpp0 はなくなった。gcc -no-integrated-cpp はまだ使えるが、 そこから呼び出されるのは cc1 (cc1plus) である。すなわち、プリプロセ スでもコンパイルでも cc1 が呼び出される。そして、プリプロセスフェー ズの cc1 にプリプロセッサ用でないオプションが渡されることがある。 2. 60 個から 70 個の大量のマクロが事前定義されている。これによってシ ステムヘッダと GNU C との関係がさらに複雑になった。 3. tradcpp もなくなり、GNU C V.3.2 では obsolete とか deprecated とさ れた古い仕様の一部が復活した。 全体として、1つの巨大なコンパイラにすべてを吸収する方向となっており、 C処理系の構成のしかたとしても、オープンソースの処理系の開発の方向として も、疑問のあるところです。 MCPP の移植では、gcc からどんなオプションが渡されてくるかわからないと いうのが困るところです。間違ったオプションもすべてチェックせずに無視する のでは危険があります。とりあえず、しばしば間違って渡されてくるオプション は無視するようにしましたが、それ以外のオプションが渡されるとエラーになる はずです。 これだけの変更でも、MCPP の従来のオプションの中には使えなくなったもの があります。-E オプションは廃止し、-m オプションは -e に、-c は -@compat に変更しました。 また、GNU C V.3.2 では cpp0 の呼び出しを MCPP に置き換えればすんだとこ ろが、今度は cc1 (cc1plus) の呼び出しを MCPP と cc1 (cc1plus) とに振り分 けることが必要になります。このための shell-script は src/set_mcpp.sh の 中に用意しました。 大量の事前定義マクロは1つ1つ対応しているわけにはいかないので、GNU C の -dM オプションの出力を一括して利用するようにしました。 GNU C V.3.4 ではさらに、ソースファイルはすべて UTF-8 に変換してから処 理するように変わりました。ドキュメントによると、具体的には次のようになっ ています。* 1. プリプロセスの最初のフェーズで、multi-byte/wide character を UTF-8 に変換する。 2. この変換には libiconv の関数を使う。したがって、iconv が対応してい る encoding はすべて使える。 3. ソースファイルの encoding を指定するには -finput-charset= オプションを使う。 4. コンパイル後の encoding はデフォルトでは UTF-8 であるが、-fexec- charset= オプションで他の encoding を指定することができる。 「国際化」と言えば Unicode に対応させることと考える風潮が、ことに実際 に multi-byte character を使わない西欧の人々の間にありますが、この風潮が GNU C にも及んでしまったようです。 実際に使ってみると、GNU C V.3.4 では EUC-JP や BIG5 は通るようですが、 shift-JIS では混乱します。また、-fexec-charset オプションは効きません。 MCPP は GNU C V.3.3 までの場合は、BIG5, shift-JIS, ISO2022-JP では 等と一致する値のバイトの直前に を挿入してコンパ イラの欠陥を補いますが、GNU C V.3.4 の場合は特別な処理はしません。また、 encoding は UTF-8 には変換せず、元の encoding のまま出力します。これは次 のような理由です。 1. cc1 で -fexec-charset オプションが効かないので、UTF-8 に変換してし まうと元の encoding には戻らない。変換しなかった場合は、shift-JIS, ISO2022-JP 以外の encoding (EUC-JP, GB2312, BIG-5, KSC-5601, UTF-8) はそのまま通るようである。Shift-JIS, ISO2022-JP は cc1 が混乱するの で、どちらにしても使えない。 2. GNU C の近い将来の仕様変更を期待したい。 * FreeBSD 5.3 に同梱されている GNU C 3.4.2 ではこの機能は有効になって いない。 [3.10] Visual C++ .net のシステムヘッダの問題 Visual C++ .net 2003 でいくつかのサンプルプログラムのプリプロセスに MCPP を使ってみました。このシステムのシステムヘッダには、プリプロセス上 の互換性が問題となるようなものはごく少ないようです。次のようなものはあり ますが、これらは他の処理系でもしばしば見られるもので、特に問題となるもの ではありません。 1. C99 の仕様はほとんど実装されていないにもかかわらず、C で // コメン トが多用されている。 2. Function-like マクロの名前に展開される object-like マクロの定義が 時々見られる。 3. limits.h に間違ったマクロ定義が1つある(cpp-test.txt [4.1.3.1] の 注2 を参照)。 Linux や glibc のシステムヘッダには GNU C local な仕様がしばしば使われ ていますが、Visual C++ のシステムヘッダには Visual C++ local な書き方は あまり見られません。 [3.10.1] コメントを生成するマクロ? しかし、Visual C++ には1つだけとんでもないマクロがあります。Vc7/ PlatformSDK/Include/WTypes.h には次のようなマクロ定義があります。 #define _VARIANT_BOOL /##/ そして、Vc7/PlatformSDK/Include/ の oaidl.h, propidl.h で次のように使 われています。 _VARIANT_BOOL bool; これはいったい何でしょうか? これは _VARIANT_BOOL が // に展開されて、その結果、この行がコメントア ウトされることを期待しているもののようです。そして、実際に Visual C の cl.exe ではそうなってしまいます! しかし、// はトークン (preprocessing-token) ではありません。また、マク ロの定義や展開は、ソースがトークンに分解されコメントが1個のスペースに変 換されたあとのフェーズで処理されるものです。したがって、マクロによってコ メントを生成することは決してできないのです。このマクロは // に展開された ところで、// は有効な 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] で見た GNU C 2 / cpp もその1つです。こうしたプリ プロセッサでは、部分的に手を加えれば加えるほどプログラム構造がゴチャゴチ ャしてくるので、いくら改良してもあるところで品質は頭打ちとなります。古い ソースを捨てて、初めから書き直さない限り、すっきりしたプリプロセッサには ならないと思われます。 GNU C 3 / cpp ではソースが新しく書き直されて、GNU C 2 とは別のプリプロ セッサとなりました。MCPP も、DECUS cpp という古いプリプロセッサのソース から出発しながら、出発してまもなく全面的に書き直されたものです。 ☆ 4.処理系定義の仕様 ☆ C言語のプリプロセス仕様を逐一ここに書くわけにはゆきません。cpp-test. txt に Standard C のプリプロセスについて詳しい解説を書いてあるので、そち らを読んでください。MCPP の pre-Standard モードの動作については、mcpp- porting.txt の [4.1.3] を見てください。ここでは 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", のどちらかの形でなければならない。そうでない場合はエラーとな る。 2. "file-name" の形でも の形でも file-name がフルパスリス トであれば、そのままオープンする。オープンできなければエラーとする。 3. フルパスリストでなくて "file-name" の形であれば、次のディレクトリ (からの相対パス)と解釈してサーチする。-I1 オプションでは 3.1、-I2 では 3.2、-I3 ではその双方(この順で)となる。デフォルトでは、UNIX 系の処理系, GNU C, Visual C に移植されたものでは 3.2、その他では原則として 3.1 であ る。ただし、Borland C では、BC 4 までは 3.1、BC 5 は 3.1+3.2である。それ で発見できなければ、 の形と同様のサーチをする。 3.1. カレントディレクトリ(もちろん MCPP 起動時の)。#include がネスト されていても、常にカレントディレクトリを基準とする。 3.2. ソースファイル(インクルード元)のあるディレクトリ。#include がネ ストされている場合、ヘッダファイルが別ディレクトリにあると、そのたびに基 準がズレてゆく。 4. フルパスリストでなくて の形であれば、次のディレクトリを サーチする。これらのディレクトリそのものが .. といった相対パスで指定され ている場合は、カレントディレクトリからの相対パスと解釈する。これらを順に すべてサーチしてもファイルをオープンできなければエラーとする。 4.1. MCPP 起動時に -I オプションで指定されたディレクトリ。 複数あれば指定された順に(左から)サーチする。 4.2. GNU C 版では -isystem オプションで指定されたディレクトリ。複数あ れば指定された順に(左から)サーチする。 4.3. 環境変数で指定されたディレクトリ。この環境変数の名前は、system.H の ENV_C_INCLUDE_DIR で定義されている。C++ では ENV_CPLUS_INCLUDE_DIR が 定義されていればその環境変数が先に使われる。GNU C 版では C_INCLUDE_PATH (C++ では CPLUS_INCLUDE_PATH も)、Plan 9 版では include、その他では INCLUDE(C++ では CPLUS_INCLUDE も)をデフォルトの環境変数名としている。 環境変数で複数のディレクトリが separator で区切って指定されていれば、そ れらを最初のものから順にサーチする(separator は DOS/Windows 系では ;、 Plan 9 では space、その他では :)。 4.4. system.H のマクロ CPLUS_INCLUDE_DIR? で定義された implementation- specific なディレクトリ。 4.5. system.c の set_sys_dirs() で指定された site-specific なディレク トリ(/usr/local/include)。 4.6. system.H のマクロ C_INCLUDE_DIR? で定義された implementation- specific なディレクトリ。 4.7. system-specific なディレクトリ(UNIX 系では /usr/include)。 ディレクトリの数は上記のすべてを合わせて system.H で定義されている NINCLUDE 以内でなければなりません。-I- (GNU C では -nostdinc、Visual C では -X) オプションを指定すると、上記の 4.4 以降のサーチは行われません。 パスの基準をカレントディレクトリとするのは、ANSI C Rationale では委員 会の「意図 (intent)」であるとされています。基準ディレクトリが動くことが なく仕様が明確なので、妥当だと思われます。しかし、UNIX 系の処理系等では、 少なくとも #include "header" の形式では include 元のソースファイルのある ディレクトリを基準とする習慣があるようです。 [4.3] Header name の構築法 Header-name という pp-token の構築法と、そこから実際の include file の ファイル名を取り出す方法は、次の通りです。 1. ソース中に文字列リテラルの形式で書かれていれば、それをそのまま header-name とする。ソース中にマクロで書かれていて、それを展開した結果が 文字列リテラルになった場合も、同様である。文字列リテラルの形式の header- name では、単にその両端の " をとったものをファイル名とする。 2. ソース中に の形で書かれていれば、それをそのまま header- name とする。単に両端の <, > をとったものをファイル名とする。 3. ソース中にマクロで書かれていて、それを展開した結果が の 形になった場合は、両端の <, > をとり、さらにすべての space を除去したも のをファイル名とする。 4. どの場合でも、DOS/Windows 系では path-delimiter として \ も / も使 えるが、\ は / に変換する。 5. DOW/Windows 系ではファイル名の中の大文字はすべて小文字に変換する。 [4.4] #if 式の評価 mcpp_std は C90 の動作モードでは #if 式の評価は、ホスト処理系とターゲ ット処理系がともに unsigned long 型を持っていれば(HAVE_UNSIGNED_LONG == TRUE であれば)、必要に応じて long および unsigned long で行います。 HAVE_UNSIGNED_LONG == FALSE の処理系では long だけで評価します。 mcpp_prestd も long だけです。 mcpp_std は long long を持つ処理系では、-V199901L オプションで __STDC_VERSION__ が 199901L 以上の値に定義された時は C99 の仕様となり、# if 式は long long / unsigned long long で評価しますC++ でも -V199901L オ プションで起動した場合は同様です。 Visual C, Borland C 5.5 では long long はありませんが、それと同サイズ の __int64 という型があるので、C99 の #if 式は __int64 / unsigned __int64 で評価します(ただし、Visual C++ .net 2002 までと Borland C 5.5 では LL, ULL という suffix が使えないので、これらの suffix は #if 行では 使えるが地の文で使ってはいけない)。 また、-+ オプションで C++ のプリプロセスをする時は、#if 式中の true, false という pp-token の値はそれぞれ 1L, 0L と評価します。 HAVE_UNSIGNED_LONG == TRUE の処理系での mcpp_std の C90 モードでの具体 的な評価のしかたを、以下に説明します。C99 モードの場合は、以下の [4.4], [4.5] の記載はすべて、long / unsigned long をそれぞれ long long / unsigned long long と読み替えてください。 1.個々の整数定数トークン(文字定数を含む)は、数値トークンに接尾子 U が付いていれば unsigned long で評価する(mcpp_prestd では接尾子 U は認知 しない)。 2.そうでなければ、long の非負の範囲におさまれば long で評価する。 3.そうでなくて unsigned long の範囲に入れば unsigned long で評価する。 4.それも越える値は out of range のエラーとする。 5.二項演算は被演算数のどちらかが符号なしであれば符号なしで、そうでな ければ符号つきで行う。 どちらにしても整数定数トークンは常に非負の値をとります。 ホスト処理系とターゲット処理系のどちらかが unsigned long 型を持ってい なければ、整数定数トークンの評価は非負の long の範囲で行い、それを越える 値は out of range とします。その演算もすべて long で行います。 また、双方が unsigned long 型を持っていても、ホストのそれのほうが範囲 が狭い場合は、それを超える値は out of range となります。 定数同士の演算結果が範囲外となった場合は、long では out of range のエ ラーとなり、unsigned long ではウォーニングが出ます。演算の中間結果につい ても同様です。 負数の右ビットシフトや負数を含む割り算には移植性がないので、ウォーニン グを出します。符号なし型と符号つき型の混合演算によって符号つき型の負の値 が符号なし型の正の値に変換された場合も、ウォーニングを出します。実際の処 理は、ホスト処理系のコンパイラ本体の仕様に従います。 C90 ではプリプロセスでの #if 式の評価はすべて long / unsigned long で (C99 ではその処理系の最大の整数型で)行うことになっています。コンパイラ 本体での if (expression) の評価の仕方よりは大ざっぱなものです。符号拡張 が関係する場合には、コンパイラ本体とは違う結果になることが往々にしてあり ます。 また、Standard C のプリプロセスでは keyword というものが存在しないので、 sizeof やキャストは使えません。もちろん、変数や列挙定数や浮動小数点数は 使えません。mcpp_std では #if 式に defined 演算子が使え、#elif ディレク ティブも使えます。あとはコンパイラ本体での if (expression) と同様に、各 演算子の優先順位とグルーピング規則(いわゆる結合規則)に従って評価が行わ れます。2項演算子の多くでは、両辺を同型にするための算術変換が行われ、片 方が unsigned long の場合は他方は long であっても unsigned long に変換さ れます。 なお、long, unsigned long 型の範囲は Standard C では でわか ることになっていますが、MCPP のソースでは は使っていません。 Pre-Standard の処理系でもコンパイルできるようにするためと、Standard C 準 拠と称する処理系の には時に 間違った書き方が見られるからです。 [4.5] #if 式での文字定数の評価 #if 式の定数トークンとしては識別子(マクロ、非マクロ)、整数の数値トー クン、文字定数がありますが、このうち文字定数の評価の仕方はほとんど implementation-defined であり、portability はあまりありません。#if 'const' と compiler-proper での if ('const') との間でさえも結果が違う場 合があります(Standard C でも、これが同じであることは保証されていない)。 poststd モードではこのほとんど意味のない #if 式中の文字定数の評価は行 いません(エラーとなる)。 文字定数の評価は他の整数定数トークンと同様に、long, unsigned long の範 囲で常に正の値に評価します(C99 モードでは long long, unsigned long long)。 Single character でない multi-byte character および wide character は、 encoding が UTF-8 の場合は4バイト、それ以外ではすべて2バイトの型で評価 します。UTF-8 はサイズが可変なので、4バイトの型で評価します。EUC-JP の 3バイト encoding には対応していません(3バイト文字は1バイト+2バイト の2文字として認識される。その結果、値は正しく評価されることになる)。2 バイトの encoding でありながら、wchar_t が4バイトの型である処理系もあり ますが、MCPP は wchar_t には関知しません。以下では2バイトの multi-byte character encoding の場合について説明します。 '字' というような multi-byte character constant は ((1バイト目の値 << CHARBIT) + 2バイト目の値) と評価します(CHARBIT は の CHAR_BIT と同じ値)。 'ab', '\x12\x3', '\x123\x45' というような multi-character character constant では、'a', 'b', '\x12', '\x3', '\x123', '\x45' 等をそれぞれ1バ イトとして [0, UCHARMAX] の範囲で評価し、その結果を上位バイトから順次 CHARBIT ずつ左シフトさせながら足してゆきます(UCHARMAX は の UCHAR_MAX と同じ値)。1つの escape sequence の値が UCHARMAX を超えた時 は、out of range のエラーとなります。したがって、CHARBIT == 8 で文字セッ トが ASCII であれば、この3つのトークンの値はそれぞれ 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' をそれぞれ1つの wide character として [0, (UCHARMAX << CHARBIT) | UCHARMAX] の範囲で評価し、その結果を上位の wide character から順次 CHARBIT * 2 ずつ左シフトさせながら足してゆきます。1 つの escape sequence の値が2バイト符号なし整数の最大値を超えた時は、out of range エラーとなります。したがって、CHARBIT * 2 == 16 で ASCII であれ ば、この3つのトークンの値はそれぞれ 0x00610062, 0x00120003, 0x01230045 となります。 Multi-character character constant, multi-character wide character constant の値が unsigned long の範囲を超えた時は out of range エラーとな ります。 __STDC_VERSION__ または __cplusplus の値が 199901L 以上の場合は、 \uxxxx, \Uxxxxxxxx の形の UCN (universal-character-name) を16進 escape sequence として評価します(こういう評価をしても何の意味もないが、しかし こう評価するしかないのである)。 ターゲット処理系のコンパイラ本体で char や wchar_t が符号ありの場合は、 #if 式での文字定数の評価とコンパイラ本体での if (expression) による文字 定数の評価とは、結果が違ってくることがあります。範囲エラーとなる範囲も違 う可能性があります。また、multi-character character constant, multi-byte character constant の評価は、プリプロセッサだけでなくコンパイラ本体でも 処理系によってまちまちです。CHAR_BIT が8であっても、'ab' を 'a' * 256 + 'b' と評価するか、それとも 'a' + 'b' * 256 と評価するかさえも、Standard C では決められていません。 一般に、#if 式では文字定数はそれに代わる手段がある限りは使うべきではあ りません。それに代わる手段がない場合というのは、私には思い付きませんが。 [4.6] #if sizeof (type) Standard C ではプリプロセスは実行時環境やコンパイラ本体の仕様から独立 した文字通りのプリプロセスとして規定が明確にされ、その結果、#if 行では sizeof とキャストは使えないことになりました。しかし、MCPP では mcpp_prestd の場合は system.H で OK_SIZE を TRUE に定義すると、#if 行で sizeof (type) が使えるようになっています。これはオリジナル版を継承して、 それに long long, long double の処理を追加する等の手を加えたものです(さ すがにキャストを実装するのは煩雑なので、やっていない。やる気もない)。 ただ、やはり sizeof の評価は実行時環境によって違ってくるので、気を付け るべき問題があります。MS-DOS のようにメモリモデルによって sizeof (type) が違ってくる場合もあります。また、MCPP では near, far 等の処理系依存の修 飾子は認識しません。他のOSで同様のメモリモデルのようなものがある場合は、 system.c の mem_model() という関数に対応版を書き加える必要があります。 eval.c の S_CHAR 等の S_* というマクロでは各型のサイズが定義されていま すが、クロス処理系で使う場合は、その値としてターゲット処理系のこれらの型 のサイズを整数値で直接書く必要があります。HAVE_LLONG, HAVE_LDBL というマ クロは処理系が sizeof (long long) や sizeof (long double) をコンパイルで きるかどうかだけを示すものです。ホストとターゲットとでこれらの型の扱いが 異なる場合は、HAVE_LLONG 等に関係なく、S_LLINT, S_PLLINT 等をターゲット 処理系に合わせて整数値で定義しなければなりません。 MCPP の #if sizeof には手抜きがあります。char, short, int, long, long long の頭に付く signed, unsigned は単に無視します。したがって、unsigned char, unsigned long 等の型のない処理系でもそれらの sizeof はエラーになら ず、それぞれ char, long のサイズを返します。また、sizeof (void *) はサポ ートしません。いささか中途半端ですが、こういう後ろ向きの機能のために system.H のフラグを増やして煩雑にしたくないのです。どうせキャストもサポ ートしないのだから sizeof は削除しようかとも思いましたが、せっかく旧版に あったものなので、若干の手を加えただけで残してあります。 [4.7] White-space sequence の扱い MCPP は translation phase 3 の tokenization に際して、token separator としての複数の white spaces の sequence は、改行コード以外は one space に圧縮します。また、行末の white space sequence は削除します。行頭の white spaces の処理は実装によって違い、原則として削除する場合と one space に圧縮して保存する場合とあります。 ただし、これはプリプロセスの中間段階の話です。その後に phase 4 があっ て、マクロ展開とプリプロセスディレクティブ行の処理が行われます。マクロ展 開の後ではその前後に複数の spaces ができることがあります。もちろん、 space がいくつあろうと、コンパイルの結果は何も変わりません。 Standard C では translation phase 3 でこれを one space に圧縮するかど うかは implementation-defined とされていますが、通常はユーザはまったく気 にする必要はありません。Portability が問題になるのは、preprocessing directive 行に または がある場合だけです。こ の場合は Standard C では undefined です。MCPP ではこれらは space に変換 します。 [4.8] MCPP 実行プログラムのデフォルトの仕様 noconfig ディレクトリにある各処理系用の差分ファイルと makefile を使っ てデフォルトの設定でコンパイルした場合の MCPP 実行プログラムの仕様をここ に書いておきます。Configure スクリプトで設定を生成してコンパイルした場合 は configure の結果によって違ってきますが、OS と処理系のバージョンが同一 であれば、少なくともインクルードディレクトリ以外は同じ結果になるはずです。 これらの差分ファイルと makefile は次の処理系用のものです。 FreeBSD 5.3 (GNU C V.3.4) VineLinux 3.1-ix86 (GNU C V.2.95, V.3.2, V.3.3) CygWIN 1.3.10 (GNU C V.2.95) LCC-Win32 V.3.2 Visual C++ .net 2003 Borland C++ V.5.5J / WIN32 Borland C++ V.4.02J / MS-DOS Plan 9 ed.4 / pcc いずれもそれらの処理系自身でコンパイルされます。 Borland C には、BCC 5.5 による 32 ビット版と BCC 4.0 による 16 ビット 版とあり、16 ビット版は large モデルでコンパイルされます。 MCPP V.2 は system.H というソース中のマクロ定義を変更することで各種の 動作仕様の MCPP 実行プログラムができるようになっていますが、中でも MODE というマクロが動作の根幹を決めるものとなっています。ここでは MODE == STANDARD でコンパイルされた MCPP について説明します。 MODE == STANDARD でコンパイルされた MCPP は、デフォルトでは Standard C の仕様で動きます。さらにこの実行プログラムは poststd モードという動作モ ードも持っています。poststd モードというのは、私が勝手に作ったプリプロセ ス仕様で、Standard C のプリプロセス規定の不規則な部分を整理したものです。 詳細な仕様は [2.4] を参照してください。尋常なソースであれば、これを Standard C のプリプロセッサのつもりで使ってさしつかえありません。 noconfig.H, system.H で定義されるマクロのうち、次のものはどの処理系用 もすべて同じ設定にしてあります。 これらの処理系のコンパイラ本体はいずれも #pragma 行を受け付けるので、 HAVE_PRAGMA は TRUE としています。 OK_SIZE は FALSE でコンパイルしています。これは Standard C の仕様とし て当然です。DOLLAR_IN_NAME も FALSE です。したがって、名前には $ は使え ません。 CONCAT_STRINGS は FALSE としているので、文字列リテラルの連結は行いませ ん。 OK_DIGRAPHS == TRUE, DIGRAPHS_INIT == FALSE でコンパイルされているので、 digraph は -2 (-digraphs) オプションで有効となります。OK_PRAGMA_OP は TRUE としているので、-V199901L オプションで _Pragma() 演算子が有効になり ます。OK_MBIDENT は FALSE としているので、識別子中に multi-byte- character は使えません。STDC は 1 としているので、__STDC__ の初期値は 1 となります。 OK_TRIGRAPHS == TRUE, TFLAG_INIT == FALSE としているので、trigraph は -3 (-trigraphs) オプションで有効となります。OK_UCN は TRUE にしているの で、C99, C++ で UCN (universal character name) が使えます。 DEBUG, DEBUG_EVAL は TRUE としているので、#pragma MCPP debug ディレク ティブで各種のデバッグ情報が出力されます。OK_MAKE は TRUE としているので、 makefile 用の依存関係行を出力するオプションが実装されています。 TOP_SPACE は TRUE にしているので、行頭の spaces は原則として a space に圧縮したうえで残します。 しかし、poststd モードでは trigraphs も UCN も使えません。行頭の spaces は原則として削除されます。 各種の translation limits は次のようにしています。 NMACPARS(マクロの引数の最大数) : 255 NEXP (#if 式中の副式の最大ネストレベル) : 256 BLK_NEST(#if section の最大ネストレベル) : 256 RESCAN_LIMIT(マクロ再走査の最大ネストレベル) : 64 次のマクロは処理系によって異なった設定にしています(BC4/16 というのは Borland C 4.0 の 16 ビット版を指す)。 STDC_VERSION (__STDC_VERSION__ の初期値) GNU C 2 : 199409L その他 : 0L HAVE_DIGRAPHS (digraphs をそのまま出力するか) GNU C, Visual C : TRUE その他 : FALSE EXPAND_PRAGMA (C99 で #pragma 行の引数をマクロ展開するか) Visual C: TRUE その他 : FALSE IDMAX (identifier の有効長) BC4/16 : 255 その他 : 1024 NINCLUDE(include directory の最大数) BC4/16 : 16 その他 : 64 NBUFF(ソースの最大行長で、かつコメントを a space に処理し による行接続をした後の最大行長)、NWORK(出力の最大行長)は次 のようにしています(NBUFF, NWORK は同じ値にしてある)。 BC4/16 : 4096 その他 : 65536 NMACWORK(マクロ展開等の内部バッファのサイズ)は NBUFF, NWORK の4倍に なっています。 #include のネストレベルには、特に制限はありません(同時にオープンでき るファイルの数に関するOSの制限を超えていてもかまわない)。 GNU C 2.7-2.95 では __STDC_VERSION__ は 199409L となっていましたが、3. x では __STDC_VERSION__ はデフォルトでは事前定義されず、実行時オプション に応じて定義されるようになりました。MCPP の GNU C 用の設定はこれに対応し たものです。 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 は MCPP で変換されてから出 力されます。 EXPAND_PRAGMA が TRUE であっても、STDC, MCPP, GCC のどれかで始まる # pragma 行はマクロ展開しません。 Include ディレクトリは次のように設定してあります。 まず、UNIX 等で言ういわゆる system-specific なものおよび site-specific なものは次の通りです。 FreeBSD, Linux, CygWIN : /usr/include, /usr/local/include Plan 9 : /sys/include/ape, /$objtype/include/ape 処理系やそのバージョンによって異なる implementation-specific なものは 次の通りです。これらのディレクトリを定義するマクロは noconfig.H または config.h にあります。 C_INCLUDE_DIR1 での設定 FreeBSD 5.3 / GNU C 3.4.2 : なし VineLinux 3.1 / GNU C 2.95.3 : /usr/lib/gcc-lib/i386-vine-linux/2.95.3/include VineLinux 3.1 / GNU C 3.2 : /usr/local/gcc-3.2/include VineLinux 3.1 / GNU C 3.3.2 : /usr/lib/gcc-lib/i386-vine-linux/3.3.2/include CygWIN 1.13 / GNU C 2.95.3-5 : /usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include BCC5.5 : /BCC55/INCLUDE BCC4 : /BC4/INCLUDE C_INCLUDE_DIR2 での設定 VineLinux 3.1 / GNU C 3.2 : /usr/local/gcc-3.2/lib/gcc-lib/i686-pc-linux-gnu/3.2/include CPLUS_INCLUDE_DIR1 での設定 FreeBSD 5.3 / GNU C 3.4.2 : /usr/include/c++ VineLinux 3.1 / GNU C 2.95.3 : なし VineLinux 3.1 / GNU C 3.2 : /usr/local/gcc-3.2/include/c++/3.2 VineLinux 3.1 / GNU C 3.3.2 : /usr/include/c++/3.3.2 CygWIN 1.13 : /usr/include/g++-3 CPLUS_INCLUDE_DIR2 での設定 FreeBSD 5.3 / GNU C 3.4.2 : /usr/include/c++/backward VineLinux 3.1 / GNU C 3.2 : /usr/local/gcc-3.2/include/c++/3.2/i686-pc-linux-gnu VineLinux 3.1 / GNU C 3.3.2 : /usr/include/c++/3.3.2/i386-vine-linux Linux / GNU C 3.2 : /usr/local/gcc-3.2/include/c++/3.2 CPLUS_INCLUDE_DIR3 での設定 VineLinux 3.1 / GNU C 3.2 : /usr/local/gcc-3.2/include/c++/3.2/backward VineLinux 3.1 / GNU C 3.3.2 : /usr/include/c++/3.3.2/backward Visual C 版、LCC-Win32 版ではこれらは特に設定せず、環境変数を参照す る。環境変数は Visual C, LCC-Win32 では INCLUDE, CPLUS_INCLUDE である。 これでつごうが悪ければ、設定を変えて MCPP をリコンパイルするか、環境変 数で指定するか、-I オプションで指定するかしてください。 MCPP はプリプロセスした結果が NWORK-1 を超える時は、これ以下の長さに行 を分割して出力します。文字列リテラルの長さは NWORK-2 以下でなければなり ません。 念のために繰り返しますが、以上はいずれも MCPP をコンパイルした時のマク ロであり、MCPP の実行プログラムが持っている組み込みマクロではありません。 MCPP は次のような組み込みマクロもいくつか持っています。(=value) と書い てあるものはその value に定義され、その他は 1 に定義されます。 __STDC__ が 1 以上の状態では、_ で始まらない組み込みマクロは削除されま す。-N (-undef) オプションでは __MCPP 以外がすべて削除されます。その上で -D オプションで設定し直してもかまいません。処理系のバージョンが違うだけ の場合は、MCPP をリコンパイルしなくても、この方法で別バージョンに対応さ せることができます。-N や -U を使わなくても、-D で特定のマクロだけ再定義 することができます。Borland C の別バージョンに対応させるには、これで良い でしょう。 入力ファイルを指定せずに MCPP を起動して、#pragma MCPP put_defines と 打ち込むと、組み込みマクロの一覧が表示されます。 FreeBSD 5 / GNU C 3.4: __i386__, unix, __unix, __unix__, __FreeBSD__ (=5), __GNUC__ (=3), __GNUC_MINOR__ (=4), __MCPP (=2), __SIZE_TYPE__ (=unsigned int), __PTRDIFF_TYPE__ (=int), __WCHAR_TYPE__ (=int) Linux / GNU C 2.95: __i386__, unix, __unix, __unix__, __linux__, __GNUC__ (=2), __GNUC_MINOR__ (=95), __MCPP (=2) __SIZE_TYPE__ (=unsigned int), __PTRDIFF_TYPE__ (=int), __WCHAR_TYPE__ (=long int) Linux / GNU C 3.2: __i386__, unix, __unix, __unix__, __linux__, __GNUC__ (=3), __GNUC_MINOR__ (=2), __MCPP (=2) __SIZE_TYPE__ (=unsigned int), __PTRDIFF_TYPE__ (=int), __WCHAR_TYPE__ (=long int) Linux / GNU C 3.3.2: __i386__, unix, __unix, __unix__, __linux__, __GNUC__ (=3), __GNUC_MINOR__ (=3), __MCPP (=2) __SIZE_TYPE__ (=unsigned int), __PTRDIFF_TYPE__ (=int), __WCHAR_TYPE__ (=long int) CygWIN 1.3.10: __i386__, __CYGWIN__, __CYGWIN32__, __GNUC__ (=2), __GNUC_MINOR__ (=95), __MCPP (=2), __SIZE_TYPE__ (=unsigned int), __PTRDIFF_TYPE__ (=int), __WCHAR_TYPE__ (=short unsigned int) LCC-Win32: __i386__, __WIN32__, WIN32, _WIN32, __FLAT__, __LCC__, __LCCDEBUGLEVEL (=0), __LCCOPTIMLEVEL (=0), __MCPP (=2) VC .net 2003: __i386__, __WIN32__, _WIN32, WIN32, __FLAT__, _MSC_VER (=1310), _MSC_EXTENSIONS, _M_IX86 (=600), __MCPP (=2) BC 5.5 / 32 bits: __i386__, __WIN32__, WIN32, __FLAT__, __BORLANDC__ (=0x0550), __TURBOC__ (=0x0550), __MCPP (=2) BC 4.0 / 16 bits: __8086__, MSDOS, __MSDOS__, __SMALL__, __BORLANDC__ (=0x0452), __TURBOC__ (=0x0452), __MCPP (=2) Plan 9 / pcc: unix, __unix, __unix__, _PLAN9, __MCPP (=2) -+ (-lang-c++) オプションで C++ のプリプロセスを指定した時は、 __cplusplus が事前定義されますが、その初期値は 1L です。そのほかにさらに 次のマクロが事前定義されます。 GNU C V.2.* 版 : __GNUG__ (=2) GNU C V.3.* 版 : __GNUG__ (=3) BC 4.0 : __BCPLUSPLUS__ (=0x0320) BC 5.5 : __BCPLUSPLUS__ (=0x0550) GNU C では V.3.2 までは事前定義マクロと言っても GNU C / cpp が事前定義 するものは少なく、多くは gcc から cpp に -D オプションで渡されるものでし た。それとの互換性のためには MCPP で定義する必要はないのですが、MCPP を ("pre-preprocess" 等で)単体で動かす時の便宜のために MCPP 内で定義して います。 GNU C V.3.3 以降は突然、60 個から 70 個のマクロが事前定義されるように なりました。MCPP V.2.5 でもやむなくこれを取り込んでいます。したがって、 GNU C V.3.3 以降に移植された MCPP では上記のほかに多くのマクロが事前定義 されます。その内容は MCPP のコンパイル時に生成される mcpp_g*.h というヘ ッダファイルでわかります。 FreeBSD, Linux, CygWIN / GNU C, LCC-Win32, Plan 9 / pcc は long long を持っているので、-V199901L オプションを指定した時は #if 式は long long, unsigned long long で評価します。Visual C, Borland C 5.5 では long long はありませんが、__int64, unsigned __int64 という型があるのでこれを使いま す。他の処理系では C90 と同じ long, unsigned long のままです。 これらの処理系では 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]) です。 これらの処理系本体ではいずれも符号つき整数型の内部表現は2の補数であり、 ビット演算もそれに対応しているので、MCPP の #if 式でも同様です。 負の整数の右シフトはいずれも算術シフトであり、MCPP の #if 式でも同様で す(1ビットの右シフトで値が符号つきのまま 1/2 になる)。 整数の除算・剰余算で operand の片方または双方が負である場合はいずれも Standard C の ldiv() 関数と同じ代数的演算が行われるので、MCPP の #if 式 でも同様です。 これらのシステム(OS)ではいずれも基本文字セットが ASCII なので、 MCPP でも同様です。Multi-byte character のデフォルトの encoding は FreeBSD, Linux 版は EUC-JP、Plan 9 版は UTF-8、その他は shift-JIS です。 私が書いた kmmalloc というメモリー管理ルーチンがあり、malloc(), free(), realloc() 等を含んでいますが、CygWIN 以外ではこれがインストールされてい る場合は、make する時に MALLOC=KMMALLOC(または -DKMMALLOC=1)というオプ ションを指定するとこれが static link されます。_MEM_DEBUG というモードで コンパイルされていて、ヒープメモリのデバッグ用ルーチンがリンクされるよう になっています(XMALLOC というマクロも 1 に定義されている)。errno の番 号 EFREEP, EFREEBLK, EALLOCBLK, EFREEWRT, ETRAILWRT には Linux, LCC- Win32 ではそれぞれ 2120, 2121, 2122, 2123, 2124 を、それ以外ではそれぞれ 120, 121, 122, 123, 124 を割り当てています(mcpp-porting.txt [4.extra] 参照)。* GNU のシステムおよび Visual C 以外では、環境変数 TZ はあらかじめ JST-9 にセットしておく必要があります。そうしないと、__DATE__, __TIME__ マクロ の値がズレてしまいます。 * CygWIN 1.3.10 では malloc() に _malloc_r() という内部ルーチンがあり、 他のライブラリ関数にはこれを呼び出すものがいくつかある。そのため、他 の malloc() を使うことができない。 ☆ 5.診断メッセージ ☆ [5.1] 診断メッセージの形式 MCPP が出す診断メッセージとその意味は、以下の通りです。診断メッセージ はいずれも標準エラー出力に出力され、-Q オプションでカレントディレクトリ 中の mcpp.err というファイルにリダイレクトされます。 診断メッセージは次の形をとっています。 1."filename:line: " に "fatal error: ", "error: ", "warning: " のど れかが続き、さらに [5.3] - [5.9] のメッセージのうちのどれかが続く。 "filename:line: " に始まる1行の診断メッセージというのはいささか窮屈な仕 様であるが、UNIX 上のC処理系での伝統的な診断形式で、各種のツールがこれ を前提としているので採用している。画面上の1行におさまらないことがしばし ばある。 2.マクロ展開中であれば、そのマクロ呼び出しが表示される。ネストされた マクロ呼び出しであれば、ネストをさかのぼってそれぞれのマクロ名が表示され る。DEBUG == TRUE でコンパイルされた MCPP では、さらにそのマクロ定義も表 示され、そのマクロ定義のあるソースファイル名と行番号も表示される。 3.ソースファイル名と行番号とその行が表示される。ファイルが include されたものであれば、include 元のファイルの名前と行番号と行が順次表示され る。 表示される行は通常は、ソースの「物理行」が行末の \ によって接続された あとの「論理行」からさらにコメントを a space に変換した後のものであり、 コメントが行をまたいでいる場合は複数の論理行が連結されたものとなる。行番 号は連結された最後の物理行の番号である。ただし、コメント処理等の前の translation phase でのエラー等では、その phase の行が表示される。 ただし、-j オプションを指定した時は、上記の2と3は出力しません。 診断メッセージには次の3つのレベルがあります。 fatal error : プリプロセスをこれ以上続けても意味がない場合 error : 構文や使用法が間違っている場合 warning : Portability のない場合や間違いの可能性のある場合 Warning にはさらに次の5つのクラスがあります。Class 1, 2 以外はやや特 殊なものです。 class 1 : 間違いの可能性のある、または portability を欠いたソース class 2 : 規格上は問題があるが実際にはたぶん問題のないソース class 4 : 実際にはたぶん問題がない portability に関する warning class 8 : スキップされる #if group や、#if 式中の評価をスキップさ れる副式や、文字列リテラルの連結等についてのお節介な warning class 16: trigraph, digraph についての warning MCPP はきわめて多種の診断メッセージを用意しています。mcpp_std では次の ような種類にのぼっています(こまかく分ければもっと多くなる)。 fatal error : 19 種 error : 75 種 warning class 1 : 36 種 warning class 2 : 8 種 warning class 4 : 15 種 warning class 8 : 23 種 warning class 16: 2 種 これらについて、原則としてその行の中の問題の部分を具体的に指摘します。 なお、以下の説明では、診断メッセージで引用されるソース中のトークンや数 値の部分には例として何かのトークンをはめこんでいます。そのうち、数値のか わりにマクロ名を書いているところは、実際にはそのマクロを展開した値が表示 されます。 また、場合によってエラーとして出たりウォーニングとして出たりするメッセ ージもあります。以下の説明では同一のメッセージについては最初に記載すると ころでだけ解説を加え、あとは単にメッセージを並べるだけにします。 なお、診断メッセージに出てくる path-list やファイル名は DOS/Windows 系 では正規化のためにすべて小文字に変換しています。 [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 自身のバグを意味します。 このチェックは DEBUG, DEBUG_EVAL を TRUE に定義してコンパイルした MCPP でだけ行われます。これが出ることはないと思いますが、もし出たら ぜひその状況をご連絡ください。 [5.3.2] 物理的エラー File read error ソースファイル読み込みのエラーです。ディスクが傷んでいるのでしょう。 File write error 出力ファイルの書き出しエラーです。ディスクが傷んでいるかいっぱいにな っているのでしょう。 Out of memory (required size is 0x123 bytes) メモリが足りなくなりました(MCPP がヒープから 0x123 bytes を取得しよ うとしたが、できなかった)。メモリの少ないシステムで長大なマクロ定義 があまりにも多くある場合に発生します。ソースファイルを分割して、1 translation unit のマクロ定義を減らしてください。 [5.3.3] Translation limits と内部バッファのエラー Too long header name "long-file-name" Include すべきファイルのパスリスト(指定のディレクトリのパスリストと 連結したもの)が FILENAMEMAX または NWORK のサイズを超えています。 Too long source line ソースの物理行の長さが NBUFF-2 を超えています。Cのソースではないの でしょう。 Too long logical line ソースの物理行を行末の \ によって接続した論理行の長さが NBUFF-2 を超 えています。あまりにも長大なマクロを定義した時に発生することがありま す。そのようなコードはマクロではなく関数として書くべきでしょう。 Too long line spliced by comments コメントを a space に変換したあとの行の長さが NBUFF-2 を超えています。 行をまたぐコメントによって多くの行を連結した場合に発生します。コメン トを分離してください。 Too long output line プリプロセス後の行長が NWORK - 2 を超えました。長大な定義を持つマク ロ呼び出しが同一行に含まれているのかもしれません。行を分割してくださ い。 Too long token プリプロセス後の行に NWORK - 2 を超える長さのトークンが含まれていま す。NWORK < NMACWORK でコンパイルされた MCPP ではコンパイラ本体の受 け取れる行長に出力行を分割しようとしますが、あまり長いトークンがある と分割しても出力できないことがあります。 次の4つのエラーはトークンがさほど長くなくても、マクロ展開中にそのトー クンのところでバッファオーバーフローになった場合にも起こります。その場合 はマクロ呼び出しを分割してください。また、文字列リテラルの連結中に発生す ることもあります。その場合は行を分割してください。 Too long quotation "long-string" 文字列リテラル、文字定数または header-name が長すぎます。文字列リテ ラルであれば分割してください。隣接する文字列リテラルは Standard C の 処理系であればコンパイラ本体が連結してくれます (ただし、CONCAT_ STRINGS == TRUE でコンパイルした MCPP は自分でやってしまうが)。 Too long pp-number token "1234567890toolong" Preprocessing-number token が長すぎます。mcpp_std の場合です。 Too long number token "12345678901234......" Number token が長すぎます。mcpp_prestd の場合です。 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 を分割してください。 Too many include directories "dir" Include directory の指定が NINCLUDE を超えています。 Too many include files 1本のソースファイルのプリプロセス中に #include されるヘッダファイル の数が NINCLUDE*4 を超えました。重複して include されるヘッダファイ ルは1つと数えます。 [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 があった場合は 処理系は何らかの診断メッセージを出さなければならないことになっていますが、 mcpp_std ではこの violation に対しては原則としてエラーメッセージが出ます。 一部はウォーニングです。 また、Standard C で undefined とされているものの多くについても error メッセージまたは warning が出ます。Undefined でありながら error も warning も出ないのは、次のものだけです。 1.文字列リテラルの形の header name 中の ' と /*。これは単なる文字と して扱われる。実際にはこれは、ファイルをオープンしようとしてエラーになる はずである(<, > で囲まれた header name の中ではこれらは文字定数およびコ メントの開始と解釈されるので、何らかのエラーとなる)。header name の中で は \ も undefined であるが、これもチェックしない。実際にはやはりファイル のオープンでエラーになるであろう(DOS/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 で を削除した結果 UCN に相当する sequence ができた場合、および文字列リテラ ルの連結によって UCN sequence が生成された場合は undefined であるが、こ れは診断しない(いずれも UCN として扱う)。 Standard C のプリプロセスで具体的に何が violation of syntax rule or constraint で、何が undefined で何が unspecified で何が implementation- defined であるかについては、cpp-test.txt を参照してください。 Fatal でない error メッセージが出てもプリプロセスは続けます。終了する と、エラーの数を表示し、親プロセスに「失敗」の状態値を返します。 [5.4.1] 文字とトークンに関するエラー Illegal control character 0x1b, skipped the character 文字列リテラル、文字定数、header name、コメント以外のところに white space 以外のコントロールコードがあります。その文字をスキップして処理 を続けます。 次の5つはトークンのエラーです。初めの4つはいずれもその行をスキップし て処理を続けます。初めの3つは文字列リテラル等のトークンで、論理行の行末 までに引用符が閉じられていないことを示します。 #error I can't understand. などと、preprocessing-token sequence の形を成さないテキストを文字列リテ ラルでもコメントでもないところに書くと、この種のエラーとなります。Pre- processing-token は本来の(コンパイラ本体での)Cの token よりおおまかな もので、文字が source character set に含まれてさえいればほとんどの character sequence が何らかの token sequence として通るので、pre- processing-token エラーとなるのはこれだけです。 なお、スキップされる #if group の中でも pp-token エラーはエラーとなり ます。 Unterminated string litera 文字列リテラルが完結していません。文字列リテラルは論理行をまたぐこと はできません。必要なら、複数行に文字列リテラルを書いて、処理系にそれ を連結させてください。このエラーは # 演算子による文字列化で発生する こともありますが、その場合は行はスキップしません。 mcpp_prestd の oldprep モードではこれはエラーになりません(行末がリ テラルの終わりとみなす)。 また、-a (-lang-asm, -x assembler-with-cpp) オプションで起動された場 合は、行をまたぐ文字列リテラルと判断して、\n を挿入して次の行と連結 するので、エラーにはなりません(ウォーニングは出る)。 Unterminated character constant 't understand. 文字定数が完結していません。mcpp_prestd の oldprep モードではこれは エラーになりません(行末がリテラルの終わりとみなす)。 Unterminated header name で囲まれる header-name 中に " または ' があった場合はこのエラーではなく上記2つのエラーとなります。 また、<, > で囲まれる header-name 中に /* があった場合はそこから後が コメントと解釈されます。 Empty character constant '' 文字定数の中身がカラです。 Illegal UCN sequence mcpp_std で __STDC_VERSION__ の値が 199901L 以上の場合、または C++ の場合は、UCN が認識されますが、識別子中の \u, \U で始まる16進 sequence のケタ数が、それぞれ4ケタ、8ケタに満ちていません。(#if 式中の文字定数でこれが起こったときは、Undefined escape sequence のウ ォーニングとなる。それ以外の場合は診断されない)。 UCN cannot specify the value "0000007f" UCN は16進で [0, 9f], [d800, dfff] の範囲の値を指定することはでき ません。ただし、前者のうち 0x24 ($), 0x40 (@), 0x60 (`) は可です。前 者はこの3文字を除くとすべて basic source character set の値と一致す るので、使えません。また、後者は special characters のための reserved area となっています。ただし、C++ では後者の制限はありません (なぜか規格が微妙に違っている)。しかし、C++ でも -V199901L として マクロ __cplusplus を 199901L 以上に事前定義した場合は、これに関して は C99 と同じ仕様になります。 Illegal multi-byte character sequence "XY" OK_MBIDENT == TRUE でコンパイルした mcpp_std の場合、C99 では identifier 中に multi-byte character が使えますが、multi-byte character として認められない character sequence があるとエラーになり ます。 [5.4.2] 完結しないソースファイルのエラー 次のメッセージはソースファイルが完結しない行、コメント、#if section、 マクロ呼び出し等で終わっている場合に出ます。そのファイルで入力が終わりの 場合(include されたファイルでない場合)は、"End of file" ではなく "End of input" と表示されます。 これらの診断メッセージは MCPP のモードによって、エラーとなる場合とウォ ーニングとなる場合とあります。 mcpp_std ではこれらはすべてエラーです。その行やマクロ呼び出しはスキッ プし、#if section の対応関係はそのファイルが include された時の初期状態 に戻します。 mcpp_prestd では、\ で終わるソースはチェックしません。それ以外のものは ウォーニングです。mcpp_prestd でも、oldprep モードでは unterminated macro call 以外はウォーニングさえも出ません。 End of file with no newline, skipped the line ファイルが改行コードのない行で終わっていてはなりません。 End of file with \, skipped the line ファイルが で終わっていてはなりません。 End of file with unterminated comment, skipped the line コメントの閉じ忘れです。 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 となるだろうが)。また、マクロ展 開の仕様が MODE によって異なるので、ある MODE では意図通りに展開され るマクロが他の MODE ではこのエラーになることがあります。 [5.4.3] Preprocessing group 等の対応関係のエラー 次は #if, #else 等の group の対応関係のエラーです。これらの行は無視し て(それまでの group が続いているものとして)処理を続けます。これらのチ ェックはたとえスキップされる #if group の中にあっても行われます。 なお、#if (#ifdef) section とは #if, #ifdef, #ifndef から #endif まで、 #if (#elif, #else) group とは1つの #if (#ifdef) section のうちの #if (# ifdef, #ifndef), #elif, #else, #endif 等ではさまれた1つの行ブロックを指 します。 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 されたファイルが元ファ イルの中にあればエラーにならないが、単一のファイルの中ではバランスが とれていないという場合です。mcpp_std の時だけです(mcpp_prestd では ウォーニング)。 次の2つは #asm, #endasm の対応関係のエラーです。もちろん、mcpp_prestd の特定の処理系の場合だけです。 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 行の引数がマクロであれば、そ れを展開したうえでチェックが行われます(mcpp_prestd では展開しない)。 下記のメッセージそのものにはディレクティブ名が出てきませんが、これに続 いて表示されるソース行でディレクティブがわかります(ディレクティブ行はコ メントが space に変換されると、必ず1行になる)。 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 の第一引数は十進整数トークンでなければなりません。mcpp_std の 場合です(mcpp_prestd はウォーニングは出すが、16進、8進等の整数ト ークンも認める)。 Line number "2147483648" is out of range of [1,2147483647] #line の第一引数は [1,2147483647] の範囲になければなりません。0 もエ ラーです。mcpp_std の場合です。__STDC_VERSION__ < 199901L または __cplusplus < 199901L の場合はこの引数の有効範囲は [1,32767] ですが、 [32768,2147483647] の範囲はエラーではなくウォーニングにとどめていま す。 Not a file name "name" #line の第二引数がある場合は通常の文字列リテラルでなければなりません。 Identifier やワイド文字列リテラル等ではいけません。 次のエラーは mcpp_std の場合だけで、これらのディレクティブは無視されま す。mcpp_prestd では、oldprep モードではエラーもウォーニングも出ず、デフ ォルトではウォーニングとなり、この "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 については、 それがCの legal な preprocessing token で成り立っているかどうかと、#if 等の group の対応関係はチェックしますが、その他の文法エラーはエラーには なりません。 #if 行そのものの中では、評価をスキップされる部分式があります。例えば #if a || b のような式で a が真である場合は、b の評価は行われません。しか し、次の13種の文法エラーないし 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 以上になりました(カッコは一対を2つと数える)。#if 式のネストが深すぎます。 Misplaced constant "12" #if 式の定数のあるべきでない位置に定数 12 があります。#if ではキャス トは使えませんが、(int)0x8000 などとキャストを使ったりすると、int が マクロとして定義されていない identifier とみなされ 0 と評価されるの で、(0)0x8000 となり、このエラーが発生します。 Operator ">" in incorrect context #if 式の妙な位置に > という演算子があります。マクロ MACRO が0個のト ークンに定義されている時に、#if MACRO > 0 などとすると、マクロ展開の 結果 #if > 0 となるので、このエラーとなります(この場合はその直前に Macro "MACRO" is expanded to 0 token というウォーニングが出るので、 それとわかる)。 Unterminated expression #if 式が完結していません。MACRO が0個のトークンに定義されている時の #if a || MACRO などの場合です。 Excessive ")" #if 式に ( に対応しない余計な ) があります。 Missing ")" #if 式で ( に対応する ) がありません。 Misplaced ":", previous operator is "+" ? に対応しない : があります。 Bad defined syntax #if defined の文法が間違っています。かっこの対応がとれていなかったり、 identifier が引数になっていなかったりの場合です。DEBUG == TRUE でコ ンパイルされた MCPP では、マクロ展開の結果、このエラーが起こった場合 は、このメッセージに続いてその展開結果を表示します。 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 にも合致しない、その文字1字のトークンで す(コントロールコードはその前にチェックされているので、それ以外の文 字)。$ を identifier 中に認める処理系では、MCPP を OK_DOLLAR == TRUE としてコンパイルしておかないと、このエラーが発生します。もちろ ん、スキップされる group の中にあるものはチェックされません。 次は #if sizeof に関するエラーです。もちろん、OK_SIZE == TRUE でコンパ イルされている場合だけです。 sizeof: Syntax error #if sizeof の文法が間違っています。かっこがなかったり、かっこが多す ぎたり、かっこの対応がとれていなかったり、引数がなかったりする場合で す。 sizeof: No type specified #if sizeof (type) の type が指定されていません。sizeof (*) などです。 sizeof ((*)()) は関数へのポインタのサイズを求める legal な構文です。 [5.4.6] #if 式の評価に関するエラー 次のエラーは評価をスキップされる部分式にある場合は起こりません(-W8 オ プションではこれらについてもウォーニングが出る)。 Constant "123456789012" is out of range 整数定数の値が unsigned long で表現できる範囲を超えています (unsigned long のない処理系では long。以下同様)。__STDC_VERSION__ または __cplusplus の値が 199901L 以上の場合は、long long を持つ処理 系では、この範囲は unsigned long long の範囲となります(以下同様)。 Integer character constant 'abcde' is out of range 文字定数 'abcde' の値が unsigned long (unsigned long long) で表現で きる範囲を超えています。 Wide character constant L'abc' is out of range ワイド文字定数 L'abc' の値が unsigned long で表現できる範囲を超えて います。文字が UTF-8 で encode されている場合は、1文字が4バイトの 値で評価されるので、2文字では long の範囲を超えます。-V199901L オプ ションで C99 の仕様にすると、2文字までは long long の範囲におさまり ます。mcpp_std の時だけです。 CHARBIT bits can't represent escape sequence '\x123' 文字定数中の1つの escape sequence の値が CHARBIT bits で表現できる 範囲([0, UCHARMAX])を超えています。 CHARBIT*2 bits can't represent escape sequence L'\x12345' ワイド文字定数中の1つの escape sequence の値が CHARBIT*2 bits (UTF- 8 の場合は CHARBIT*4 bits)で表現できる範囲を超えています。mcpp_std の時だけです。 Division by zero #if 式に 0 による割り算があります。割り算は / によるものと % による ものがあります。#if dividend / divisor .. で divisor がマクロとして 定義されていない場合などに起こります。#if defined divisor && (dividend / divisor ..) と書くことで、このエラーを避けることができま す。 Result of "op" is out of range 演算子 op による演算の結果が long の範囲外となりました。op は2項演 算子 *, /, %, +, - のどれかです。整数の内部表現が2の補数の場合は、 単項 - 演算子も -LONG_MIN で(C99 では -LLONG_MIN で)オーバーフロー となります。Unsigned long (unsigned long long) はオーバーフローする ことがないのでエラーにはなりませんが、代数的な演算結果が範囲外となる 場合はウォーニングが出ます。 次は sizeof に関するエラーです。スキップされる部分式では出ません(-W8 オプションではウォーニングが出る)。OK_SIZE == TRUE の場合です。 sizeof: Unknown type "type" #if sizeof (type) の type が変です。 sizeof: Illegal type combination with "type" #if sizeof (long float) 等と、型の組み合わせが変です。MCPP の設定に よっては同じ組み合わせが変ではなくなることもあります(long long 等)。 [5.4.7] #define のエラー 次は #define に関するエラーです。マクロは定義されません。 #, ## 演算子に関するエラーは mcpp_std のものです。 __VA_ARGS__ に関するエラーも mcpp_std の場合です。可変引数マクロは C99 の仕様ですが、GNU C との互換性のために C90 でも C++ でも有効としています (ただし、ウォーニングが出る)。 "defined" shouldn't be defined defined という名前のマクロは定義できません。これは mcpp_std でチェッ クされます。 "__STDC__" shouldn't be redefined マクロ __STDC__ は #define できません。__STDC_VERSION__, __FILE__, __LINE__, __DATE__, __TIME__ も(C99 モードでの __STDC_HOSTED__ と -+ オプションでの __cplusplus も)同様です。これは mcpp_std でチェッ クされます。 "__VA_ARGS__" shouldn't be defined C99 では可変引数マクロの定義の置換リスト中に __VA_ARGS__ というパラ メータが使われますが、この identifier はマクロとして定義することはで きません。 More than NMACPARS parameters マクロ定義でパラメータの数が NMACPARS を超えています。 Empty parameter マクロ定義にカラのパラメータがあります。 Illegal parameter "123" マクロ定義で identifier 以外のトークンがパラメータに使われています。 mcpp_std では、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 "..." というパラメータはマクロ定義の最後のパラメータでなければなりま せん。(mcpp_prestd では ... は Illegal parameter エラーになる)。 "__VA_ARGS__" without corresponding "..." 置換リスト中の __VA_ARGS__ という identifier は ... というパラメータ がある場合しか使えません。 [5.4.8] #undef のエラー 次は #undef に関するエラーです。 "__STDC__" shouldn't be undefined マクロ __STDC__ は #undef できません。__STDC_VERSION__, __FILE__, __LINE__, __DATE__, __TIME__ も(C99 モードでの __STDC_HOSTED__ と -+ オプションでの __cplusplus も)同様です。これは mcpp_std の場合に チェックされます。 [5.4.9] マクロ展開のエラー 次はマクロ展開に関するエラーです。 DEBUG == TRUE でコンパイルされた MCPP では、それらのマクロ定義も表示さ れ、そのマクロ定義のあるソースファイル名と行番号も表示されます。#, ## 演 算子に関するエラーは mcpp_std だけです。 Less than necessary N argument(s) in macro call "macro( a)" マクロ呼び出しの引数が足りません。macro には引数は N 個必要です。足 りない引数には 0 個のトークンを割り当てて、処理を続けます。パラメー タが1個で引数がゼロの場合はカラ引数と引数の欠落との区別がつきません が、これはエラーにしません。 oldprep モードではこれはウォーニングです。 More than necessary N argument(s) in macro call "macro( a, b, c)" マクロ呼び出しの引数が多すぎます。macro の引数は N 個のはずです。余 計な引数を捨てて、処理を続けます。 oldprep モードではこれはウォーニングです。 Not a valid preprocessing token "+12" ## 演算子によって2つの pp-token を連結したところ、"+12" という invalid なものになりました。これは後で切り離されるかもしれませんが、 このまま処理を続けます。-lang-asm (-x assembler-with-cpp, -a) オプシ ョンおよび compat モードでは、これはウォーニングです。 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)" マクロ呼び出しが完結していません。このエラーが起こるのはたいていは、 ディレクティブ行でのマクロ呼び出しがその行で完結していない場合です。 また、mcpp_std では引数中のマクロは引数の置換に先立って展開されます が、その時にそのマクロ呼び出しはその引数の中で完結していなければなり ません。poststd モードではマクロの置換リスト中にあるマクロ呼び出しが 置換リストの中で完結していない場合も、このエラーとなります。 Rescanning macro "macro" more than RESCAN_LIMIT times at "something" マクロのネストが深すぎて、展開中に "something" のところで再走査の回 数が RESCAN_LIMIT を超えました。mcpp_std の場合だけですが、まず起こ りえません。 Recursive macro definition of "macro" to "macro" マクロの定義が再帰的です。これは mcpp_prestd の時だけ起こりうるエラ ーです。再走査の回数が RESCAN_LIMIT に達すると再帰的マクロ定義とみな します。 [5.4.10] #error, #assert #error #error ディレクティブが実行されました。その #error 行が表示されます (引数そのものにトークンエラー(Unterminated string 等)があった場合 は #error は実行されない)。#error は mcpp_std の場合だけです。 Preprocessing assertion failed: #assert ディレクティブが実行されました。この言葉に続いて #assert 行 の引数が出力されます。引数の式そのものにエラーがあった場合は、asser- tion が失敗したものとみなします。#assert は mcpp_prestd で COMPILER ! = GNUC の場合だけです。 [5.4.11] #include の失敗 Can't open include file "file-name" Include すべきファイルが存在しない場合にこのエラーが発生します。たぶ んファイル名のスペルミスか include directory の指定の間違いでしょう。 [5.4.12] その他のエラー Operand of _Pragma() is not a string literal _Pragma() operator の引数は1個の文字列リテラルまたはワイド文字列リ テラルでなければなりません。OK_PRAGMA_OP == TRUE でコンパイルした mcpp_std で -V199901L オプションを指定した時だけのものです。C++ でも -V199901L オプションで起動した場合は同様です。 [5.5] Warning (class 1) 文法的には間違いではないが何かの書き間違いの可能性がある場合や port- ability の問題のある場合に、warning が出ます。Warning には 1, 2, 4, 8, 16 の5つの class があります。MCPP の起動時に -W というオプションを 指定することで、これらが有効になります。 は 1, 2, 4, 8, 16 のうちの任 意のものの OR をとったものです。なお、以下の説明で -W4 等と言っているの は、-W & 4 が真の場合のことで、4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31 を含みます。 mcpp_std では Standard C で undefined とされている動作を引き起こすソー スの多くは error にしますが、一部については warning を出します。 同様に mcpp_std では Standard C で unspecified とされている仕様を使う ソースに対しては、次の点以外は必ず warning を出します。 1.#if 式中の sub-expression の評価順序については、warning は出さない。 ||, &&, ? : 以外の演算子に関しては operand の評価順序は unspecified であ るが、#if 式は副作用を生じないので、この評価順序は結果には影響しないから である。MCPP では、整数定数トークンの評価は常に出現と同時に左から右に行 い、それらの間の演算は常に演算子のグルーピングの規則に従って、その項の値 が必要になった時に初めて行う。 mcpp_std では、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 式で負数がからむビット演算は整数の内部表現によって結果の値が異 なるが、大半のマシンは2の補数の表現をとっているので、それを前提とすれば portability の問題はほとんど存在しない。ただし、負数の右ビットシフトおよ び operand の片方または双方が負数の除算は portability が乏しいので、 warning を出す。 4.Token separator としての複数の white spaces の sequence。Standard C では translation phase 3 でこれを one space に圧縮するかどうかは implementation-defined とされているが、通常はユーザはまったく気にする必 要はない。Portability が問題になるのは、preprocessing directive 行に または がある場合だけである。MCPP ではこれら は space に変換するが、その時は warning を出す。複数の space, tab の sequence は one space に黙って圧縮する。 5.処理系独自の組み込みマクロについては warning は出さない。 6.#pragma sub-directive についても原則として warning は出さない。た だし、コンパイラ本体が #pragma を認識しない処理系では、MCPP 自身が処理す る #pragma once, #pragma __setlocale, #pragma MCPP *, 以外は warning を 出す。これらの MCPP が処理する #pragma で引数が間違っている場合も、 warning を出す。また、GNU C V.3 での #pragma GCC poison (dependency, system_header) のように、処理系付属のプリプロセッサは処理するが MCPP は 処理しない #pragma についても warning を出す。 7.C99 では、UCN sequence が # 演算子によって文字列化される場合、\ を \\ というふうに重ねるかどうかは implementation-defined となっているが、 これについては warning は出さない。MCPP では \ は重ねない。 したがって、MCPP ではプリプロセスのレベルでの portability のチェックを ほぼ完全に行うことができます。 poststd モードでは、[2.4] にある仕様の違いを除けば、mcpp_std のデフォ ルトモードと同様です。 ウォーニングがいくつ出ても、「成功」の状態値を返します。-W0 のオプショ ンで起動すると、ウォーニングは出ません。 [5.5.1] 文字、トークンおよびコメントに関するウォーニング Illegal control character 0x1b in quotation 文字列リテラル、文字定数、header name 中に white space でないコント ロールコードがあります。これらはコンパイラ本体でエラーになるかもしれ ません。そうでなくても、感心しないソースです。コントロールコードは文 字列リテラル、文字定数では escape sequence で書くべきです。 Illegal multi-byte character sequence "XY" in quotation 文字列リテラル、文字定数、header name 中の XY の1バイト目は multi- byte character(漢字)の1バイト目ですが、2バイト目は multi-byte character の2バイト目ではありません("XY" の表示は化けるはず)。こ れは multi-byte character とみなさず、1バイト目を single byte character として、2バイト目は次の文字として扱います。 いわゆる外字も規定の範囲のコードであれば、ウォーニングは出ません。ま た、規定の範囲にも実際には文字のない穴がところどころにありますが、 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 は2バイトです。 UTF-8 では multi-byte character は2バイトまたは3バイトであり、漢字 は3バイトで encode されます。1バイト目は 0xc2-0xef、2バイト目と3 バイト目は 0x80-0xbf の範囲にありますが、詳細は省略します。どちらに しても、3バイト目までがあるべき範囲になければなりません。 なお、EUC-JP の 0x8f + 0xa1-0xfe + 0xa1-0xfe の3バイト encoding (JIS X 0212 のいわゆる補助漢字)は1文字としては認識できず、0x8f, 0xa1-0xfe + 0xa1-0xfe の2文字として認識されますが、その結果、ウォー ニングも出ず、#if 式中のワイド文字定数の評価以外は、正しく動作します。 EUC-JP では1バイト目が 0x8e の文字(JIS X 0201 のいわゆる半角カタカ ナ)は2バイト 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進の数値トークン中に 8 または 9 の文字があります。mcpp_prestd でだ け出ます。mcpp_std では通常の行の数値トークンについては正誤の判定は しません。#if 式にこのトークンがあれば、それは Not an integer エラー の一種となります。 Unterminated string literal, catenated to the next line 論理行中で閉じていない文字列リテラルは通常はエラーですが、-lang-asm (-x assembler-with-cpp, -a) オプションで起動された場合は、これは行を またぐ文字列リテラルと解釈して、'\n' を挿入して次の行と連結します。 こういうソースの書き方をするメリットは何もないので、「隣接する文字列 リテラルの連結」の機能を使って書いてください。 [5.5.2] 完結しないソースファイルのウォーニング 次のウォーニングは mcpp_prestd でしか出ません(mcpp_std ではエラー)。 入力の終わりでない場合は、これらのウォーニングを無視して処理を続けますが、 その結果はさらに妙なエラーを引き起こすでしょう。oldprep モードでは unterminated macro call 以外はウォーニングさえも出ません。 End of file with no newline, skipped the line End of file with \, skipped the line End of file with unterminated comment, skipped the line 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 DEBUG == TRUE でコンパイルされた MCPP では、これに続いて以前の定義の あるファイル名と行番号が表示されます。 マクロが以前とは違った内容で再定義されました。ソースが整理されていな いに違いありません。同じ名前のマクロの定義が重複している場合は、次の 条件を満たしていないとこのウォーニングが出ます。 1.パラメータの数が同じ。 2.置換リストが同じ(ただし、トークン間の1個以上の white spaces は いくつあっても1つとみなす。poststd では token separator があっても なくても自動的に a space に変換するので、token separator の違いは問 題にならない)。 3.mcpp_std ではパラメータ名も同じ。poststd ではこれはチェックしな い。mcpp_prestd でももちろんチェックしない。 次の2つは mcpp_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)) と解釈されて、このウォーニングが出ます。 "and" is defined as macro C++ で and がマクロとして定義されました。 C95 では でマクロとして定義される "and" 等の11種の名前 は、C++ では operator token です。C++ ではこれはマクロとして定義する ことができないのですが、これを operator として実装していない処理系で も使えるように、マクロとして定義することを許しながらウォーニングを出 します。 次の4つは mcpp_std の場合だけです。 No sub-directive #pragma 行に何の引数もありません。この行は無視されます。 Unknown argument "name" #pragma name というディレクティブはありません。PRAGMA == FALSE の場 合だけ出ます。 Unknown encoding "encoding" #pragma __setlocale( "encoding") で指定された "encoding" という encoding は実装していません。 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") としなければなりません(冗長な仕様である)。 "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 されたのかもしれません。 GNU C では次のウォーニングが出ます。 Ignored #ident Ignored #sccs #ident, #sccs の行は無視します。 この小節 ([5.5.3]) の以下の #pragma に関するウォーニングでは、PRAGMA = = TRUE であれば、ウォーニングは出てもこの行はそのまま出力されます。 次の3つは DEBUG == TRUE and/or DEBUG_EVAL == TRUE の場合だけです。 Unknown argument "name" #pragma MCPP debug, #debug の引数として "name" は実装されていません。 No argument #pragma MCPP debug, #debug の引数がありません。 Not an identifier "123" #pragma MCPP debug, #debug の引数が identifier ではありません。 ただし、GNU C 用では #pragma GCC に poison, dependency, system_header のどれかが続く行は class 2 のウォーニングを出したうえで捨てます。これは GNU C V.3 ではプリプロセッサが処理するものですが、MCPP は処理しません。 次は mcpp_prestd で出ます(mcpp_std ではエラー)。 Not in a #if (#ifdef) section in a source file Line number "0x123" isn't a decimal digits sequence 次は OK_IF_JUNK == FALSE の場合だけ出ます。mcpp_std では #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 でだけです (mcpp_std のその他の場合はエラー、oldprep ではエラーもウォーニングも出 ない)。 Excessive token sequence "junk" [5.5.4] #if 式に関するウォーニング 次の3つは #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 ではなく演算子として扱いますが、あやしげなマクロです。 OK_SIZE == TRUE の場合です。 Macro "MACRO" is expanded to 0 token マクロ MACRO が0個のトークンに展開されました。#if 式でこれが発生す ると、たいていは何らかのエラーになります。このウォーニングはエラーの 原因を明らかにするためのものです。 次も #if, #elif, #assert の引数に関するウォーニングですが、評価をスキ ップされる部分式では出ません(-W8 オプションでは出る)。 Undefined escape sequence '\x' \x という escape sequence はありません。単なる \x という2バイトの sequence として評価します(\x に16進文字列が続く escape sequence はもちろんある)。UCN のケタ数が足りない場合も同様です。 次は #if (#elif, #assert) 行の定数式の演算に関するウォーニングです。や はりスキップされる部分式に関しては出ません(-W8 オプションでは出る)。 MCPP での実際の演算は、MCPP をコンパイルした処理系のコンパイラ本体の仕様 によります。 Negative number "-1" is converted to positive "4294967295" 符号なしと符号つきとの混合演算の結果、符号つきの負数が符号なしの正数 に変換されました。これはエラーではありませんが、たぶんソースの何らか の間違いでしょう。2項演算子 *, /, %, +, -, <, >, <=, >=, ==, !=, &, ^, | の両辺、および3項演算子 ? : の第2・第3 operand については、 その片方が符号なしの場合は、他方は符号つきでも符号なしに変換されます。 Illegal shift count "-1" ビットシフト演算子 <<, >> の右 operand の値が負数です。または long のビット幅を超えています。これもソースの間違いでしょう。 "op" of negative number isn't portable 2項演算子 op の結果は、operand の一方または双方が負数である場合は portability がありません。op は /, %, >> のどれかです。左 operand が 負数の場合の >> 演算子は、算術シフト命令を持つ CPU 上の処理系の間で は portability があるはずですが(1ビットのシフトで2で割った結果に なる)、そうでない CPU 上の処理系との間では portability がありません。 Result of "op" is out of range 演算子 op による演算の結果が unsigned long の範囲外となりました。op は2項演算子 *, /, %, +, -、および単項 - のどれかです。Unsigned long はオーバーフローすることがないのでエラーにはなりませんが、代数的な演 算結果が範囲外となる場合はウォーニングが出ます。 [5.5.5] マクロ展開に関するウォーニング DEBUG == TRUE でコンパイルされた MCPP では、そのマクロ定義が表示され、 そのマクロ定義のあるソースファイル名と行番号も表示されます。 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 に至るまで エラーではありませんが、もし意図せずにこの種のマクロを使ってこのウォ ーニングが出たなら、それはマクロ定義またはマクロ呼び出しの間違いです。 意図して使ったのであれば、きわめて異常なマクロです。 このウォーニングは mcpp_std の場合だけです。mcpp_std でも -@compat オプションを指定すると、このウォーニングは出ません。mcpp_prestd でも 同じ現象は起こりますが、ウォーニングは出ません。poststd では再走査で 置換リストの後ろのテキストは取り込まないので、このウォーニングは決し て出ません(unterminated macro call のエラーになる場合と、まったく違 った展開結果になる場合とある)。 Less than necessary N argument(s) in macro call "macro( a)" マクロ呼び出しの引数が足りません。通常はこれはエラーですが、可変引数 マクロで引数が一つだけ足りなかった場合はウォーニングにとどめています。 GNU C の可変引数マクロと C99 のそれとの間の移植上の障害を減らすため です。 次の2つは oldprep モードだけです(他のモードではエラー)。 Less than necessary N argument(s) in macro call "macro( a)" マクロ呼び出しの引数が足りません。macro には引数は N 個必要です。足 りない引数には 0 個のトークンを割り当てて、処理を続けます。 More than necessary N argument(s) in macro call "macro( a, b, c)" マクロ呼び出しの引数が多すぎます。macro の引数は N 個のはずです。余 計な引数を捨てて、処理を続けます。 [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] の範囲はエラーではなくウォーニ ングにとどめています。 mcpp_std の場合です。 C90 では #line で 32767 よりは小さいがそれに近い番号を指定した場合、そ の時点ではエラーにならないものの、いずれこの範囲をオーバーします。オーバ ーした場合、MCPP では warning を出した上で行番号をそのまま増やし続けてい きますが、しかし、コンパイラ本体によってはこれを受け取れないかもしれませ ん。#line の指定がむやみに大きいことが問題です。 Line number 32768 got beyond range ソースの行番号が 32768 に達しました。その時点で1回だけ 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 は mcpp_std の場合で、mcpp_prestd では #warning です。 [5.6] Warning (class 2) 間違いではないが portability に問題のあるケースについてのウォーニング です。 次の5つは mcpp_std の場合だけです。 Parsed "//" as comment // から行末までをコメントとして解釈します。C99 および C++ では合法で すが、C90 でもウォーニングを出した上でコメントとして扱います。 Variable argument macro is defined 可変引数マクロは C99 の仕様です。C90, C95, C++ で可変引数マクロが定 義されました。 Empty argument in macro call "MACRO( a, ," マクロ呼び出しにカラの引数があります。MCPP ではその引数は0個の pp- token sequence であるとみなして reasonable な処理をします。しかし、 カラ引数は C99 では合法であるものの C90 では undefined であり、 portability がありません(',' さえもないのはカラ引数ではなく引数がな いとみなし、エラーとする。0個の引数と1個のカラ引数とは構文上、区別 がつかないが、どちらであってもエラーにはしない)。ソースにカラ引数を 書くのは一般には良いスタイルではありません。可能な場合は、 #define EMPTY 等として、この EMPTY をカラ引数の部分に書くのが良いでしょう。 Skipped the #pragma line GNU C V.3 では #pragma GCC という形の pragma があり、その中に はプリプロセッサが処理するものもありますが、MCPP はそれらはサポート していません。 処理系付属のプリプロセッサは処理するが MCPP は処理しない#pragma につ いては、このウォーニングが出ます。 Not a valid preprocessing token "+12" ## 演算子によって2つの pp-token を連結したところ、"+12" という invalid なものになりました。通常はエラーですが、-lang-asm (-x assembler-with-cpp, -a) オプションで起動された場合はエラーにはなりま せん。 次は mcpp_std の poststd モードの場合だけです。 Header-name enclosed by <, > is an obsolescent feature という形の header name は廃止したい仕様です。"stdio.h" を 使ってください。 次の2つのウォーニングは特定のシステムだけのものです。それらのシステム では正しいプログラムですが、portability がないので、念のためにウォーニン グを出します。 #include_next is not allowed by Standard #warning is not allowed by Standard これらのディレクティブは GNU C では有効ですが、規格外のものであり、 portability がありません。 Converted \ to / #include directive の header name 中に \ が含まれているので、これを / に変換して処理します。これは DOS/Windows 等のOSでは正規の path- delimiter ですが、規定では undefined です。/ を使ったほうが間違いが ありません。このウォーニングは1回しか出ません。DOS/Windows 系に implement された MCPP だけのものです(ただし、\ が " の直前にある場 合はこの " を文字列リテラルの delimiter とは解釈しないので、 unterminated string literal のエラーとなる)。 '$' in identifier "THIS$AND$THAT" Identifier 中に '$' が含まれています。このウォーニングは1回しか出ま せん。DOLLAR_IN_NAME を TRUE にしてコンパイルされた MCPP でだけ出ま す。それらのシステムでは '$' は identifier 中の有効な文字ですが、 portability はありません。他のシステムでは '$' は1文字だけの pp- token となるので、THIS$AND$THAT は THIS $ AND $ THAT の5つの pp- token に分解されます(その結果、compile phase でエラーになるはずであ る)。 [5.7] Warning (class 4) Standard C ではいくつかの translation limits について、最低限保証すべ き値を規定しています。プリプロセッサはこの値を超えた translation limits を持っているほうが仕様が良いとも言えますが、しかしそれに依存するソースは portability が制限されます。MCPP ではこれらの translation limits は "system.H" のマクロを定義することで任意に設定できるようになっていますが、 mcpp_std ではこの値が 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 のネストが8レベルを超えました。9レベルになった時だけこの ウォーニングが出ます。 More than 8 nesting of #if (#ifdef) sections #if, #ifdef, #ifndef のネストが8レベルを超えました。9レベルになっ た時だけこのウォーニングが出ます。 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 式のカッコのネストが32レベルを超えました。33レベルになった時 だけ出ます。 More than 31 parameters マクロ定義のパラメータの数が31を超えました。 Identifier longer than 31 bytes "very_very_long_name" Identifier の長さが31バイトを超えています。 __STDC_VERSION__ >= 199901L の場合はこれらの translation limits は次の 通りです。Identifier の長さでは、UCN と multi-byte-character はそれぞれ 1文字と数えます(ソースのバイト数ではない)。 ソースの論理行の長さ : 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] が '\n' と違うコードである場合)のコードは space に変換します。これが directive 行にある場合は Standard C では undefined です。コメント、 文字列リテラル、文字定数中のコードは変換しません(変換してもよいので あるが、文字セットは処理系依存なので、MCPP ではあまり制約を課さない。 必要なチェックはコンパイラ本体で行われるであろう)。他方で、token separator としての [TAB] も a space に変換しますが、これはコンパイル 結果に何の影響も与えないので(プリプロセッサにとってもコンパイラ本体 にとっても [TAB] は space と同じ意味しか持たない)、ウォーニングは出 しません。 [CR] が出てくるのはたいていは、DOS/Windows 系のソースを UNIX 系に持 ってきてそのままコンパイルしようとした場合です。改行コードを変換して からコンパイルしてください。[FF] は実際のソースに時々見られるもので す。「改ページ」を意図しているのでしょうが、感心しないスタイルです。 Undefined symbol "name", evaluated to 0 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 式は移植性がありません。 mcpp_std でだけ出ます。poststd では #if 式中の文字定数は認めないので エラーになります。次のものも同様です。 Multi-character or multi-byte character constant '字' isn't portable Multi-character character constant と multi-byte character character constant の値の評価の仕方は処理系依存なので、これを使った #if 式は移 植性がありません。mcpp_std で出ます。 次の2つは mcpp_std の場合だけ出ます。 Macro with mixing of ## and # operators isn't portable 関数様マクロ定義の置換リスト中に ## # というトークンの並びがあります が、この2つの演算子の優先順位は Standard C では unspecified なので、 移植性がありません。MCPP では # を先に適用します。なお、関数様マクロ 定義で逆向きの # ## というトークンの並びがあるとエラーになります。# 演算子のオペランドはパラメータでなければならないからです。 Macro with multiple ## operators isn't portable マクロ定義の置換リスト中に複数の ## 演算子が間に1つのトークンまたは パラメータだけをはさんでありますが、## 演算子の評価順序は Standard C では unspecified なので、このマクロは移植性のない場合があります。 MCPP では ## 演算子は左から右へ順に適用していきます。 [5.8] Warning (class 8) ソースの間違いである可能性は少ないが念のために注意を促す意味で、このメ ッセージが出ます。これをチェックするのは -W8 のオプションで起動された場 合だけです。 スキップされる #if group の中の preprocessing directive は通常は #if, #ifdef, #ifndef, #elif, #else, #endif の対応関係しかチェックしませんが、 -W8 では Illegal directive, Unknown directive のチェックもします。また、 mcpp_std では #if のネストが8レベルを超えた場合もウォーニングを出します。 Illegal #directive "123" (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) というただし書きが付けられます。 overflow した場合は wrap-round させた値で以後の演算を行います。0 による 除算の場合は結果に最大の整数値を使って以後の演算を行います。 Constant "123456789012" is out of range Integer character constant 'abcde' is out of range Wide character constant L'abc' is out of range CHARBIT bits can't represent escape sequence '\x123' CHARBIT*2 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 number "-1" is converted to positive "4294967295" Result of "op" is out of range Illegal shift count "-1" "op" of negative number isn't portable sizeof is disallowed in C Standard #if sizeof が実装されるのは mcpp_prestd で OK_SIZE == TRUE の場合だ けですが、その場合でもこれは Standard C では使えませんよというお節介 をします。 "MACRO" wasn't defined #undef に定義されていない名前を指定しています。これは少なくとも Standard C ではエラーではありません。 Macro "macro" needs arguments 引数付きマクロとして定義されているものと同じ名前が単独で現れています。 展開せず、そのまま残します。mcpp_prestd の場合だけ出ます(mcpp_std では何ら問題ないので、ウォーニングは出ない)。 String literals "str1" "str2" are concatenated 隣接する文字列リテラル "str1" "str2" が連結されて "str1str2" になり ました。"str1", "str2" と書くつもりではなかったことを確かめるために、 注意を促します。なお、wide-character-string-literal と character- string-literal が隣接しているのは、C90 では undefined で、C++ Standard でも同様ですが、C99 では wide-character-string-literal とし て連結されることになっています。どちらにしても MCPP では wide- character-string-literal として連結します。mcpp_std の CONCAT_STRINGS == TRUE の場合です。 [5.9] Warning (class 16) Trigraph と digraph は使う必要のない環境ではまったく使わないものです。 その環境でもしこれらが検出されれば、注意を要するでしょう。-W16 オプショ ンはこれを検出するものです。他方で、trigraph あるいは digraph が常用され ているソースではこのウォーニングが頻発することになってうるさいでしょうか ら、これを他のウォーニングとは別のクラスにしてあります。どちらにしても、 これらは trigraph あるいは digraph が有効な状態でだけ検出されます。すべ て mcpp_std の場合です。 2 trigraph(s) converted この物理行中の2つの trigraph sequence を変換しました。本当に tri- graph のつもりで書いたのでしょうか?  2 digraph(s) converted この行中の2つの digraph sequence を変換しました。本当に digraph の つもりで書いたのでしょうか?  mcpp_std のデフォルトモードでは、HAVE_DIGRAPHS == FALSE であれば、プ リプロセスが終わってから digraph を次のように通常の token に変換して 出力します(したがって、コンパイラ本体が digraph に対応している必要 はない)。 <% -> { <: -> [ %: -> # %> -> } :> -> ] %:%: -> ## しかし、poststd モードでは translation phase 1 で通常の pp-token に 変換してしまいます。この違いは、digraph が # 演算子による文字列化の 対象になった時に現れます。デフォルトでは digraph sequence のまま文字 列化しますが、poststd では通常の pp-token に変換されたものが文字列化 されます。また、文字列リテラルの中に digraph sequence に相当する character sequence があった場合、デフォルトではそのままですが、 poststd ではこれも対応する pp-token の character sequence に変換され ます。 デフォルトモードではこのウォーニングは digraph の「変換」を対象とし ているので、preprocessing-directive 行に現れて消えてしまう digraph はカウントされません。 [5.10] 診断メッセージ索引 診断メッセージ fatal error warning class error 1 2 4 8 16 "..." isn't the last parameter [5.4.7] "/*" in comment [5.5.1] "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] "__STDC__" shouldn't be redefined [5.4.7] "__STDC__" shouldn't be undefined [5.4.8] "__VA_ARGS__" without corresponding "..." [5.4.7] "and" is defined as macro [5.5.3] "defined" shouldn't be defined [5.4.7] "op" of negative number isn't portable [5.5.4] [5.8] ## after ## [5.4.7] #error [5.4.10] #warning [5.5.7] #include_next is not allowed by Standard [5.6] [5.8] '$' in identifier "THIS$AND$THAT" [5.6] 2 digraph(s) converted [5.9] 2 trigraph(s) converted [5.9] CHARBIT bits can't represent escape sequence '\x123' [5.4.6] [5.8] CHARBIT*2 bits can't represent escape sequence L'\x12345' [5.4.6] [5.8] Already seen #else at line 123 [5.4.3] Bad defined syntax [5.4.5] Bad push_macro syntax [5.5.3] Bad pop_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 the character 0x24 [5.4.5] Can't use a character constant 'a' [5.4.5] Can't use a string literal "string" [5.4.5] Can't use the operator "++" [5.4.5] Constant "123456789012" is out of range [5.4.6] [5.8] Converted 0x0c to a space [5.7] 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] Empty parameter [5.4.7] End of file with \, skipped the line [5.4.2] [5.5.2] End of file with unterminated comment, skipped the line [5.4.2] [5.5.2] End of file with no newline, skipped the line [5.4.2] [5.5.2] End of file with unterminated #asm block started at line 123 [5.4.2] [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] 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.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" [5.4.1] Illegal multi-byte character sequence "XY" in quotation [5.5.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 [5.4.6] [5.8] The macro is redefined [5.5.4] Less than necessary N argument(s) in macro call "macro( a)" [5.4.9] [5.5.5] Line number "32768" got beyond range [5.5.6] Line number "0x123" isn't a decimal digits sequence [5.4.4] [5.5.6] Line number "32769" is out of range [5.5.6] Line number "2147483648" is out of range of [1,2147483647] [5.4.4] Line number "32768" is out of range of [1,32767] [5.5.6] 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 BLK_NEST nesting of #if (#ifdef) sections [5.3.3] More than 8 nesting of #if (#ifdef) sections [5.7] [5.8] More than 8 nesting of #include [5.7] More than 32 nesting of parens in #if expression [5.7] 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 1024 macros defined [5.7] More than NMACPARS parameters [5.4.7] More than 31 parameters [5.7] More than necessary N argument(s) in macro call "macro( a, b, c) [5.4.9] 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 number "-1" is converted to positive "4294967295" [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] Operand of _Pragma() is not a string literal [5.4.12] Operator ">" in incorrect context [5.4.5] Out of memory (required size is 0x123 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] Replacement text "sub(" of macro "head" involved subsequent text [5.5.5] Rescanning macro "macro" more than RESCAN_LIMIT times at "something" [5.4.9] Result of "op" is out of range [5.4.6] [5.5.4] [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] String literals "str1" "str2" are concatenated [5.8] This is not a preprocessed source [5.3.4] This preprocessed file is corrupted [5.3.4] 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 output line [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 include directories "dir" [5.3.3] Too many include files [5.3.3] 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