M C P P - P O R T I N G . T X T == How to port 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. Augmented GNU C-compatible features. 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++. Created configure script. 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 *. 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.履歴 3.各処理系に移植する方法:概要 [3.1] 移植ずみの処理系 [3.1.1] 共通の設定 [3.1.2] FreeBSD / GNU C V.2.*, V.3.* [3.1.3] Linux / GNU C V.2.*, V.3.* [3.1.4] CygWIN / GNU C V.2.* [3.1.5] LCC-WIN32 V.3.* [3.1.6] Visual C++ .net [3.1.7] Borland C V.4.*, V.5.* [3.1.7.1] MS-DOS 処理系用の mcpp を WIN32 処理系で コンパイル [3.1.8] Plan 9 ed.4 / pcc [3.2] DECUS cpp で対応していた処理系 [3.3] noconfig.H, configed.H, system.H [3.4] system.c, mbchar.c [3.5] lib.c [3.6] 標準ヘッダ [3.7] makefile とMCPP を使ったリコンパイル [3.7.1] Plan 9 / pcc でのコンパイル [3.8] MCPP をコンパイルできる処理系 [3.9] コンパイルする処理系とターゲットの処理系 [3.10] 対応していない処理系 [3.11] MS-DOS のメモリモデル 4.各処理系に移植する方法:詳細 [4.1] noconfig.H, configed.H, system.H の設定 [4.1.1] PART 1 ターゲットシステムの設定 [4.1.1.1] 事前定義マクロ [4.1.1.2] Include ディレクトリ等 [4.1.1.3] 行番号情報の出力仕様その他 [4.1.1.4] 処理系の言語仕様に応じた設定 [4.1.1.5] Multi-byte character [4.1.1.6] ターゲットとホストに共通の設定 [4.1.2] PART 2 ホストシステムの設定 [4.1.3] PART 3 MCPP の動作仕様の設定 [4.1.3.1] 新旧モードの指定 [4.1.3.2] 動作モードの細部の指定 [4.1.3.3] 特殊な指定 [4.1.3.4] Translation limits の指定 [4.2] system.c [4.3] mbchar.c [4.4] lib.c [4.extra] malloc() 5.バグ報告と移植の報告 [5.1] バグかどうか? [5.2] malloc() 関連のバグチェック [5.3] バグ報告を [5.4] 移植の報告を [5.5] GNU C 以外の処理系での configure の情報を [5.6] データを送ってくれれば移植してみます [5.7] 検証セットによる他の処理系のテスト報告を [5.8] 改善のご意見を 6.MCPP の長い道のり [6.1] 構想3日、制作6年 [6.2] V.2.3 へ [6.3] 「未踏ソフトウェア創造事業」に採択 ☆ 1.概要 ☆ MCPP は Martin Minow の DECUS cpp を元に kmatsui(松井 潔)が全面的に 書き直したCプリプロセッサです。MCPP という名前は Matsui cpp という意味 です。これはソースで提供するもので、各処理系で使うには、その処理系に合わ せてソースに若干の変更を加えた上でコンパイルして、MCPP の実行プログラム を作る必要があります。* このドキュメントはソースを各処理系に移植する方法を説明しています。でき あがった実行プログラムの動作仕様については、mcpp-manual.txt というマニュ アルを参照してください。 これらのソース、ドキュメントはすべて free software として提供します。 MCPP は次のような特徴を持っています(この [1.1] - [1.2] は mcpp-manual. txt と重複している)。 * MCPP の実行プログラムは各処理系のプリプロセッサと置換して使うもので ある。したがって、実行プログラムの名前はその処理系のプリプロセッサの 名前になる。多くの処理系ではそれは cpp である。 [1.1] OSや処理系を選ばない portable なソース GNU/Linux, DOS/Windows 等の多くのOSをサポートしている porbable なプ リプロセッサであり、そのソースは Standard C (ANSI/ISO 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 準拠」と称する既存のプリプ ロセッサにいかに多くの問題があるかがわかります。* 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 と呼ぶことがあります。 * この cpp は V.2.2 までは単に cpp と呼んでいたが、一般の cpp と紛らわ しいので、V.2.3 からは MCPP と呼ぶことにした。このドキュメントでは V. 2.2 までのバージョンも MCPP と呼ぶ。また、このドキュメントの名前は V. 2.2 までは cpp.doc としていたが、V.2.3 からは porting.txt と変更し、 さらに V.2.5 からは mcpp-porting.txt と変更した。私自身の名前も、V.2. 2 までは Psycho としていたが、V.2.3 からは kmatsui と変更した。 ☆ 2.履歴 ☆ [2.1] DECUS cpp は Martin Minow によって作られ、1984/05 に usenet / net.sources で公開されました。DECUS というのは、DEC Users' Society とい う DEC 社のコンピュータのユーザグループだそうです。DECUS cpp は DEC の PDP-11 / RT11, PDP-11 / RSX, VAX / VMS, VAX / ULTRIX 等のシステムの当時 のC言語処理系のために書かれたCプリプロセッサです。移植性の良い書き方が されているので、他のシステムに移植することは比較的容易で、オリジナル版で もすでに DEC 以外のいくつかの UNIX システムに対応していたようです。 [2.2] 私がバージョンアップの元にしたのは、C Users' Group の配付ディ スク #243 です。このソース中にある修正履歴を見ると、原作者による最終修正 が 85/06 となっています。その後、原作者がバージョンアップをしているのか どうか、私は知りません。 [2.3] その後 88/12 までに何人かによって MS-DOS 上のいくつかの処理系 にも移植されました。CUG のディスクに入っているのはこのバージョンです。 [2.4] ftp.oreilly.com/pub/examples/imake/DECUS-cpp.tar.gz にもソー スがあり、その time-stamp は 93/02 となっていますが、実際の内容は CUG の ものより古く、85/01 のものです。なお、これに含まれている Martin Minow の README によると、このプログラムは public domain となっています(この README 自体も 84 または 85 年のものと思われる)。 [2.5] 89/04 に Gigo らによって OS-9/6x09 の Microware C に移植され たものが NIFTY-SERVE / FOS9 / lib 2 に登録されていました。 [2.6] MCPP V.2 は、私がこれらを元に全面的に書き直したものです。移植 性をさらに向上させ、Standard C に完全に対応させるため、ソースファイルの 分割の仕方も変え、多くのマクロを追加し、関数と変数の追加・分割・書き換え・ 改名を大幅に行っています。ソースの量もオリジナル版の3倍になっています。 ドキュメントと検証セットはすべて、私がまったく新しく書いたものです。 私はこれらを free software として公開します。私自身は DECUS とは何の関 係もありません。 なお、オリジナル版には版数が付けられていませんが、MCPP と対比する時に は、そちらを「DECUS cpp」または「旧版」と呼ぶことにします。 [2.7] Standard C のマクロ展開の実装方法については、E. Ream 作の MS- DOS 上の PDS である CPP V.5.3 (1989/08, CUG #319) のソースも参考にさせて いただきました。そのほか、GNU C / cpp の動作や、J. Roskind の JRCPP のド キュメントからもいくつかの示唆を得ています。 [2.8] MCPP V.2.0 は検証セット V.1.0 とともに 1998/08 に NIFTY SERVE / FC / LIB 2 で公開され、ベクター社のサイトにも転載されました。 [2.9] MCPP V.2.1 は、V.2.0 に C99 1998/08 draft に対応するための修 正を加えたものです。検証セット V.1.1 とともに 1998/09 に NIFTY SERVE / FC / LIB 2 およびベクター社のサイトに同時にアプロードされました。 [2.10] MCPP V.2.2 は V.2.1 を 1998/07 に決まった C++ Standard (ISO/ IEC 14882:1998) に対応して update したものです。検証セット V.1.2 ととも に 1998/11 に NIFTY SERVE / FC / LIB 2 およびベクター社のサイトに同時に アプロードされました。 [2.11] MCPP V.2.3 は V.2.2 を C99 に対応して update し、さらに Linux / GNU C 2.95, GNU C 3.2 等への移植を追加して、GNU C / cpp との互換性を向 上させたものです。また、実行時オプションを追加し、一部を変更しました。V. 2.3 ではドキュメントの英語版も作成されました。MCPP に付属する検証セット には、GNU C / testsuite の一部として自動的にテストを実行することのできる edition が追加されました。 [2.12] MCPP は V.2.3 の開発の途中で、検証セット V.1.3 とともに、情報 処理振興事業協会 (IPA) の平成14年度「未踏ソフトウェア創造事業」に新部 裕・プロジェクトマネージャによって採択され、2002/07 - 2003/02 の間は IPA の資金援助と新部PMの助言のもとに開発が進められました。英語版ドキュメン トもこのプロジェクトの中で、有限会社・ハイウェル(東京)に翻訳を委託し、 それに私が修正とテキスト整形を加えてできあがったものです。このプロジェク トの中で cvs repository と ftp site が用意され、V.2.3 はそこで 2002/08 に pre-release 1 が、2002/12 に pre-release 2 が、2003/02 にリリース版が 開発されました。その後、2003/03 に V.2.3 patch 1 が出されています。* [2.13] MCPP はさらに平成15年度にも「未踏ソフトウェア創造事業」に伊 知地 宏 PM によって継続して採択され、2003/06 - 2004/02 の間は IPA の資金 援助と伊知地PMの助言のもとに V.2.4 への update 作業が進められました。 そして、2003/11 には V.2.4 prerelease が開発されました。このバージョンで は Visual C++ .net への移植が追加され、また、MCPP の make を自動化する configure スクリプトが作成されました。なお、MCPP はそれまで明確なライセ ンス表示をしていませんでしたが、この時から BSD スタイルのライセンス表示 をするようになりました。さらに、2004/02 にはリリース版が開発されました。 このバージョンでは multi-byte character の処理が拡張され、Plan 9 / pcc にも移植されました。また、英語版ドキュメントもハイウェルに翻訳を委託し、 日本語版に合わせて update されました。 [2.14] 2004/03 には MCPP V.2.4.1 がリリースされました。これは再帰的 マクロの展開方法を修正したものです。 [2.15] 2005/03 には MCPP V.2.5 がリリースされました。このバージョン では、POST_STANDARD というコンパイル時のモードは STANDARD モードにその実 行時オプションの一つとして吸収され、OLD_PREPROCESSOR というコンパイル時 の設定は PRE_STANDARD モードの実行時オプションとして吸収されました。再帰 的マクロの展開方法は再修正されて完全なものとなりました。また、GNU C V.3. 3, 3.4 に対応した一方で、16 ビットシステムでの処理系に関するドキュメント の多くを削除しました。 * 「未踏ソフトウェア創造事業」(Exploratory Software Project) の概要は 次のところで知ることができる。 http://www.ipa.go.jp/jinzai/esp/ なお、情報処理振興事業協会 (IPA) は 2004/01 に独立行政法人・情報処理 推進機構 (IPA) に改組された。 MCPP のソースおよびドキュメントと検証セットは開発中の revision を含 めて、次の 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 これらのアーカイブファイル中のテキストファイルは、NIFTY および 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 ☆ 3.各処理系に移植する方法:概要 ☆ MCPP のソースは4本のヘッダファイルと8本の *.c ファイルからなっていま す。OSや処理系に依存する部分は configed.H, noconfig.H, system.H, system.c の4本のソースにまとめてあります。また、ライブラリ関数の一部の Cによるソースが lib.c にあります。したがって、MCPP を何らかの処理系で使 うには、それに合わせてこれらのソースファイルに変更を加える必要があります。 MCPP をコンパイルするには2つの方法があります。1つは configure スクリ プトを実行して、config.h というヘッダファイルと Makefile を自動生成する 方法です。あとは単に make; make install とするだけですみます。configed.H というヘッダファイルはこの場合に使われます。しかし、configure は UNIX 系 のシステムと CygWIN でしか使えません。 もう1つは各処理系用の差分ファイルを使ってヘッダファイルに変更を加え、 必要ならさらにヘッダファイルを編集した上で、その処理系専用の makefile を 使って make する方法です。noconfig.H というヘッダファイルはこの場合に使 われます。差分ファイルと makefile は noconfig というディレクトリにありま す。Configure の使えるシステムでも、ヘッダファイルを直接、編集することで 細かい制御をすることができます。しかし、差分ファイルはすでに移植ずみの処 理系用のものしかありません。 [3.1] 移植ずみの処理系 この節では差分ファイルを使う方法について説明します。Configure について は INSTALL を見てください。 私自身が動かすことのできるC処理系は次のもので、このいずれにも MCPP を 移植してあります。すなわち、このソースをコンパイルでき、生成されたプリプ ロセッサが正しく動作することを確認しています。いずれも CPU は i386 系を 使っています。* FreeBSD 5.3 GNU C V.3.4.2 VineLinux 3.1 GNU C V.2.95.3, V.3.2, V.3.3.2, V.3.4.3 CygWIN 1.3.10 GNU C V.2.95.3 WIN32 LCC-WIN32 V.3.2 WIN32 Visual C++ .net 2003 WIN32, MS-DOS Borland C++ V.4.02J, V.5.5J Plan 9 ed.4 pcc これらの処理系で Standard C モードのプリプロセッサを作るための修正はき わめて簡単で、noconfig.H, system.H の数個のマクロ定義を変更するだけです。 system.c の変更は必要ありません。 noconfig ディレクトリの *.dif というファイルは FreeBSD 5.3 / GNU C 3.4 用の noconfig.H, system.H を各処理系用に修正する差分ファイルです。Visual C++ .net 2003 を例にとると、src ディレクトリで patch -c < ..\noconfig\vc2003.dif とすると、修正されます。patch は UNIX の標準的なコマンドで、MS-DOS 等に も移植されています。MS-DOS 5.0 に標準で付いていた patch とはまったく別物 です。 Include ディレクトリの指定などは、差分ファイルによる修正とは別に、ユー ザが自分のシステムに合わせて修正しなければなりません。 こうして修正したソースをコンパイルするための各処理系用の makefile も添 付してあります([3.7] 参照)。 copy ..\noconfig\visualc.mak Makefile として src ディレクトリにコピーします。 以下の作業も src ディレクトリで行います。作業は特に断らない限り、 noconfig.H の修正です。 * このほか、MCPP V.2.2 までは次のものをサポートしていたが、その後、私 はこれらの処理系を使わなくなった。V.2.3 ではソースとドキュメントに設 定を残していたが、V.2.4 ではそれも削除した。 MS-DOS Turbo C V.2.0 OS-9/6x09 level 2 Microware C V.2.5 では次の処理系に関するドキュメントを削除した。ソースにはまだ 設定を残しているが、それも次のバージョンでは削除する予定である。 GO32 / DJGPP V.1.12-M4 GNU C V.2.7.1 MS-DOS LSI C-86 V.3.3 試食版 [3.1.1] 共通の設定 以下のどの処理系でも、デフォルトの include directory の設定を C_INCLUDE_DIR1, C_INCLUDE_DIR2 というマクロに書いておきます。C と異なる C++ 固有の include directory がある場合は、それを CPLUS_INCLUDE_DIR1, CPLUS_INCLUDE_DIR2, CPLUS_INCLUDE_DIR3 に書きます(これらのディレクトリ は実行時に環境変数や -I オプションで指定することもできる)。 Include directory はこのほか、system.c でも設定されています。UNIX で言 えば system.c で設定されるのはいわゆる OS-specific なもの(通常は /usr/ include)といわゆる site-specific なもの(通常は /usr/local/include)で す。DOS/Windows 系のOSに関しては、system.c では include directory は何 も設定されません。 また、必要なら CPU_STD1, CPU_STD2 等で定義される組み込みマクロ名も変更 します。 Multi-byte character の encoding はデフォルトでは、UNIX 系では EUC-JP、 DOS/Windows 系では shift-JIS、Plan 9 では UTF-8 としていますが、必要なら MBCHAR というマクロを書き換えて他の encoding に変更します。32 ビット以上 のシステムでは multi-byte character encoding は実行時に環境変数・オプシ ョン・#pragma で変更することもできます。 処理系によっては shift-JIS や Big5 等の encoding に対応していないため、 multi-byte character の中に '\\' と同じ 0x5c の値のバイトがあると tokenization でエラーになることがありますが、そういう処理系では MCPP は 特殊な処理をしてコンパイラの欠陥を補います。この設定については [4.1.1.5] を見てください。 添付の makefile については、処理系のバイナリの置かれているディレクトリ の設定を必要に応じて書き換えます。 [3.1.2] FreeBSD / GNU C V.2.*, V.3.* ソースは FreeBSD 5.3 上の GNU C V.3.4.* でコンパイルして FreeBSD 5.3 用の MCPP を生成する状態になっています。このままコンパイルすればできあが りです。 GNU C の他のバージョンであれば、 #define VERSION_MSG \ "GNU C compiled by GNU C 3.4" および #define COMPILER_EXT_VAL "3" #define COMPILER_EX2_VAL "4" #define COMPILER_CPLUS_VAL "3" となっているところのバージョン番号を変更します。COMPILER_EXT_VAL は GNU C の major version number を、COMPILER_EX2_VAL は minor version number を書きます。COMPILER_CPLUS_VAL は __GNUG__ マクロの値で、 COMPILER_EXT_VAL と同じになります。 GNU C V.2.7-2.95 であれば、次の2つのマクロの定義をそれぞれ FALSE, 199409L に変更します。 #define HAVE_INTMAX_T TRUE #define STDC_VERSION 0L FreeBSD のバージョンが 5.* でなければ、 #define SYSTEM_EXT_VAL "5" /* V.4.*: 4, V.5.*: 5 */ の値を変更します。 さらに include directory が FreeBSD 5.3 の標準と違っている場合は、 #define CPLUS_INCLUDE_DIR1 "/usr/include/c++" #define CPLUS_INCLUDE_DIR2 "/usr/include/c++/backward" となっているディレクトリを変更します。CPLUS_INCLUDE_DIR3, C_INCLUDE_DIR1 の設定も必要かもしれません。 GNU C V.3 ではプリプロセスがコンパイラ (cc1, cc1plus) に吸収されてしま ったので、MCPP を使うには、gcc, g++ の呼び出しを shell-script に置き換え て、mcpp => cc1, mcpp => cc1plus の順序で実行されるようにしなければなり ません。その方法については、mcpp-manual.txt [3.9.7] を見てください。 他の UNIX でもコンパイラが GNU C であれば、このバージョン表示や、 include ディレクトリの設定、OS固有の組み込みマクロの設定、等を変えるだ けですむのではないでしょうか([4.1.1] 参照)。 [3.1.3] Linux / GNU C V.2.*, V.3.* Linux / GNU C ではまず、 #define SYSTEM SYS_FREEBSD を #define SYSTEM SYS_LINUX に変更し、 #define COMPILER_SP3_VAL "int" を #define COMPILER_SP3_VAL "long int" に変更します。 そして、FreeBSD の場合と同じように、VERSION_MSG, COMPILER_EXT_VAL, COMPILER_EX2_VAL, COMPILER_CPLUS_VAL, CPLUS_INCLUDE_DIR1, CPLUS_INCLUDE_DIR2, C_INCLUDE_DIR1, HAVE_INTMAX_T, STDC_VERSION 等のマク ロの値を変更します。 Linux は distribution によって標準の include directory が異なることも あるので、 echo '' | gcc -xc -E -v - echo '' | g++ -xc++ -E -v - として確かめてから設定してください。 また、システムの標準の GNU C とは別のバージョンをインストールした場合 はそのバージョン固有の include ディレクトリが別に作られるでしょうから、 そのディレクトリを上記のマクロで指定します。vlinux_gcc32.dif という差分 ファイルは VineLinux 上に GNU C V.3.2 を私がインストールした例です。 noconfig ディレクトリの vlinux_gcc2953.dif, vlinux_gcc32.dif, vlinux_gcc332.dif はFreeBSD / GNU C V.3.4 用のソースを VineLinux 3.1 / GNU C V.2.95.3, V.3.2, V.3.3.2 用に修正する差分ファイルです。 なお、glibc の getopt() は POSIX 等の標準のものとは仕様が異なるので、 使わずに、lib.c のものを使ってください。noconfig.H は Linux ではデフォル トで glibc が使われていると判断します。 [3.1.4] CygWIN V.1.* / GNU C 2.* CygWIN V.1.3.10 / GNU C V.2.95.3 では noconfig.H に cyg1310.dif にある ような変更を加えます。 他の version では、VERSION_MSG, C_INCLUDE_DIR1, CPLUS_INCLUDE_DIR1, COMPILER_EXT_VAL, COMPILER_EX2_VAL, COMPILER_SP3_VAL 等のマクロを変更す ることで対応できるでしょう。GNU C 3.* については、[3.1.2] を参照してくだ さい。 [3.1.5] LCC-WIN32 V.3.* LCC-WIN32 V.3.2 では lcc32.dif のような変更を加えます。 他の version では VERSION_MSG マクロを変更します。 LCC-WIN32 の long long にはかつてはバグがあって使えませんでしたが、少 なくとも V.3.2 (2003/08) ではちゃんと使えるようになっています。 [3.1.6] Visual C++ .net Visual C++ .net 2003, 2002 ではそれぞれ vc2003.dif, vc2002.dif のよう な変更を加えます。 Visual C の他のバージョンでは、VERSION_MSG マクロを変更するほか、 _MSC_VER という組み込みマクロの値を COMPILER_EXT_VAL というマクロの設定 を変えることで、また、_M_IX86 という組み込みマクロの値を COMPILER_SP2_VAL というマクロの設定を変えることで対応させます。また、 Visual C は少なくとも V.6.0 以降はプリプロセッサが c1.dll, c1xx.dll とい うコンパイラに吸収されたようですが、プリプロセッサが独立していたバージョ ンでは ONE_PASS というマクロを FALSE とします([4.1.1.3] 参照)。 [3.1.7] Borland C++ V.4.*, V.5.* Borland C V.5.5 / bcc32 では bc55_w32.dif のような変更を加えます。 Borland C V.4.0 / bcc / large memory model では bc40_16l.dif のような 変更を加えます。 Borland C/C++ の別のバージョンでは VERSION_MSG のほか、__TURBOC__, __BORLANDC__, __BCPLUSPLUS__ という組み込みマクロの値をそれぞれ noconfig. H の COMPILER_STD2_VAL, COMPILER_EXT_VAL, COMPILER_CPLUS_VAL というマク ロの設定を変えることで、対応させます([4.1.1.1] 参照)。Digraphs と __STDC_VERSION__ の実装されているバージョンであれば、HAVE_DIGRAPHS, STDC_VERSION の設定を変更します。 Borland C 4.* までのバージョンでは、 #define SEARCH_INIT CURRENT とします。 また、WIN32 版 (bcc32) ではなく MS-DOS 版 (bcc) の場合は #define SYSTEM SYS_WIN32 ではなく #define SYSTEM SYS_MSDOS とします。 [3.1.7.1] MS-DOS 処理系用の cpp を WIN32 処理系で コンパイル また、移植する目的の処理系と MCPP をコンパイルする処理系とは必らずしも 同じでなくても良いので、例えば Borland C 用の MCPP を MS-DOS 上の処理系 である LSI C-86 でコンパイルしてもかまいません。この場合はそのほうがコー ドサイズがコンパクトになります。逆に LSI C-86 用の MCPP を Borland C / bcc32 等でコンパイルすることもできます。WIN32 でコンパイルすれば、その MCPP は WIN32 上でしか動きませんが、tranlation limits を大きくできます。 ここでは Borland C 4.0 / bcc32 で MS-DOS 版 Borland C 4.0 用の cpp を コンパイルする場合を例にとります。noconfig.H では次のようにします。 #define SYSTEM SYS_MSDOS #define COMPILER BORLANDC #define HOST_SYSTEM SYS_WIN32 #define HOST_COMPILER BORLANDC #define VERSION_MSG \ "Borland C compiled by Borland C V.4.0/bcc32" noconfig.H の PART 1 は target-compiler 用の設定にし、PART 2 は host- compiler 用の設定にします(Borland C 同士の場合は PART 2 はデフォルトの ままでかまわない)。 添付の bc4_16_by_bc4_32.dif は MS-DOS 版 Borland C V.4.0 用 MCPP を Borland C V.4.0 / bcc32 でコンパイルするための設定をする差分ファイルです。 makefile は noconfig/borlandc.mak を使い、インストールするディレクトリの 設定を書き換えて、-DWIN32 というオプションを付けて make します。 [3.1.8] Plan 9 ed.4 / pcc Plan 9 では plan9r4.dif の変更を加えてコンパイルします。 plan9r4.dif は他の *.dif とは形式が違っています。Plan 9 には diff はあ りますが、patch がありません。そこで、plan9r4.dif には diff -c ではなく diff -e による差分を書き出して、その後ろに w という1字だけの行を書き加えてあります。したがって、これを使ってソースを 書き換えるには、次のようにします。 ed noconfig.H < ../noconfig/plan9r4.dif Plan 9 でのコンパイルは他のシステムとはかなり違っています。それについ ては [3.7.1] を見てください。 [3.2] DECUS cpp で対応していた処理系 PDP-11 上の RT-11 / DECUS C, RSX / DECUS C、VAX 上の VMS / VAX-11 C、 PDP-11 / UNIX, VAX / ULTRIX の何かのC (pcc ?) には DECUS cpp が対応して いたようです。MS-DOS 上の Microsoft C, Lattice C のかなり古い版にも対応 していたようです。これらはさすがにもう不要と思われ、また私自身がメンテナ ンスできないので、削除しました。 [3.3] noconfig.H, configed.H, system.H system.H は HAVE_CONFIG_H というマクロが 1 に定義されていると configed. H を include し、そうでなければ noconfig.H を include します。configed.H, noconfig.H には PART 1 と PART 2 という部分があり、system.H には PART 3 があります。 これらのファイルには、各処理系に移植する時に必要ないくつかのマクロが定 義されています。まだ移植されていない処理系に移植するには、PART 1 に数行 ないし十数行を書き足します。また、Standard C 以外のモードの MCPP を作る には、PART 3 にある数個のマクロを書き替えます。 PART 1 はOSと target 処理系に依存する定義で、PART 2 は host 処理系に 依存する定義、そして PART 3 は MCPP の動作仕様の定義です。 デフォルトの設定と違う設定で移植する場合は、これらのファイルの全体に必 ず目を通してください。 [3.4] system.c, mbchar.c configed.H (noconfig.H), system.H のマクロだけでは吸収できないOSや処 理系の差異は、system.c で吸収しています。未実装の処理系に移植するには、 ここに数行から数十行のソースを書き足すことが必要になるでしょう。 このファイルに記述されているのは、MCPP 起動時のオプション、usage 文、 include ディレクトリ、ヘッダファイルやソースファイルをオープンする時のO S固有のディレクトリパスの扱い、#pragma の処理、処理系固有の拡張ディレク ティブの処理、等です。ほとんどは target OS と target 処理系の設定です。 V.2.4 からは mbchar.c というソースファイルが追加されました。ここには multi-byte character 処理のルーチンと文字タイプのテーブルがあります。未 実装の multi-byte character encoding を実装する場合は、ここに処理ルーチ ンと文字タイプ・テーブルを書き足します。 [3.5] lib.c ライブラリ関数のうち、Standard C にない getopt(), stpcpy() のCによる ソースをここに書いてあります。また、memmove(), memcmp(), memcpy(), strstr(), strcspn() は Standard C にはありますが、古い処理系ではない場合 もあるので、Cソースを用意しました(本当は、いわゆるブロック転送命令を持 つ CPU では、この5つと stpcpy() はアセンブラで書き直したほうが良い)。 getopt() は glibc のものは大幅な拡張が施されていてかえってやっかいなの で、こちらのものを使ってください。 memmove() はないものの memcpy() が memmove() の仕様を持っている処理系 もあります(古い BSD 系の処理系がそのようである)。その場合は noconfig.H またはシステムの strings.h か何かに #define memmove( dst, src, size) memcpy( dst, src, size) と書いておけばすみます。 fgets() はどの処理系にもあるものですが、細部の仕様が Standard C とは違 っている場合があるので、Cソースを載せておきます。処理系付属のものでも実 用上は問題ないでしょう。Standard C では、1バイトも読まないで EOF となっ た時はバッファには何も書き込まないことになっているのですが(したがって直 前に読んだ行がバッファに保存される)、'\0' を書き込んでしまうものがあり ます。そういう fgets() ではソースファイルが で終わ っている場合や なしで終わっている場合の診断メッセージに不都合 が出るだけです。 fgets() 以外のライブラリ関数はいずれも、処理系によって微妙に違う恐れの ある仕様に依存した使い方はしていないので、どの処理系のものでもバグさえな ければ大丈夫でしょう。 MCPP で使っている関数はごくありふれたものばかりです。また例えば #if 式 での整数定数の評価には strtoul() も atol() も使わず、自前でやっています。 strtoul() は古い処理系にはなく、atol() では out of range のチェックがで きないからです。 lib.c 中の xyz という関数を使うには、noconfig.H (configed.H) の PART 2 にある HOST_HAVE_XYZ というマクロを FALSE に定義します。configed.H では ターゲット処理系とホスト処理系とが同じであると仮定していますが、異なる場 合は PART 2 を編集する必要があります。 [3.6] 標準ヘッダ MCPP のソースでは stdio.h, ctype.h, errno.h を無条件で include してい ます。これを持たない処理系はまずないでしょう。これらのヘッダファイルは必 ずしも Standard C の規定に従っている必要はありません。stdlib.h, string.h, stddef.h, time.h は Standard C で規定されているものですが、古い処理系に はないので、その場合は必要な関数宣言やマクロ定義を直接使えるように noconfig.H (configed.H) の PART 2 に書いてあります。同機能の関数の名前が 違っている場合のことも考慮してあります。 これらの部分は #if 1 - #else - #endif, #if 0 - #else - #endif で囲んで あるので、必要に応じてこの 1 と 0 を逆にしてください。 configed.H ではターゲット処理系の設定を使っているので、ホスト処理系が 違っている場合は、PART 2 を編集します。 [3.7] makefile と MCPP を使ったリコンパイル noconfig ディレクトリにある *.mak は個別の処理系用の makefile です。詳 細な設定ができます。make そのものは各処理系に付属のもの、またはそのシス テムの標準的なものを想定しています。Visual C では make ではなく nmake を 使います。 まず、処理系を xyz とすると、FreeBSD / GNU C 以外では patch -c < ../noconfig/xyz.dif として noconfig.H, system.H を修正します。さらに自分のシステムに合わせて noconfig.H の C_INCLUDE_DIR? 等のマクロをエディタで修正します。そして、 使用する noconfig/xyz.mak を Makefile にコピーし、ディレクトリ指定等を自 分のシステムに合わせて修正した上で、 make make install make clean としてください。 他の処理系では、これらを参考に必要な makefile を書いてください。ソース の依存関係は単純で、 main.c, control.c, eval.c, expand.c, support.c, system.c, mbchar.c は system.H, internal.H に依存する lib.c は system.H に依存する system.H は configed.H または noconfig.H に依存する という関係になっています。system.H は internal.H より先に include する必 要があります。 スタックサイズはシステムの使う分に NMACWORK + (NEXP * 30) + (sizeof (int) * 100) を追加してください。MODE == STANDARD ではさらに (sizeof (char *) * 12 * RESCAN_LIMIT) を追加します(NMACWORK, NEXP, RESCAN_LIMIT は system.H で定義されているマクロ)。 MS-DOS のようにワイルドカードを shell(コマンドプロセッサ)が展開しな いシステムでは、MCPP でも展開しないようにコンパイルしておいたほうが安全 です(-o オプションが指定されない限り、第2引数が出力ファイルの指定とみ なされるので)。 MCPP 自身を使って MCPP をリコンパイルするには、処理系のプリプロセッサ のあるべき場所にこの実行プログラムをおきます。例えば FreeBSD, Linux, CygWIN であれば、処理系付属の cpp0 を cpp0_gnuc とでも rename し、MCPP を mcpp_std 等としておきます。その時に使うものを cpp0 にリンクするのが良 いでしょう。すなわち、使うプリプロセッサを mcpp_std とすると、 ln -sf mcpp_std cpp0 とします。DOS/Windows では使うものを cpp.exe にコピーします。* この場合は初めから、 make NAME=mcpp_std とすれば、後で rename しなくてすみます(同じことを BC make では make -DNAME=mcpp_std とする。UCB make では -D は付けても付けなくても良い。GNU make では -D は付けてはいけない)。 添付の makefile では freebsd.mak, linux.mak 以外は make install ではこ まかい処理はしないので、手で補ってください。処理系付属のプリプロセッサは、 make install で消してしまうことのないように、あらかじめ別名のファイルに コピーしておいてください。 Visual C, Borland C のような1パスコンパイラで MCPP を使ってリコンパイ ルする場合は、MCPP の出力ファイルをコンパイラに与えるソースファイルとし ます(例えば main.c というソースをプリプロセスしたものを main.i といった 名前で出力して、それを cl や bcc32 にコンパイルさせる)。 MCPP を使ってリコンパイルする時は、ヘッダファイルの "pre-preprocess" の機能を使うと、プリプロセス時間が大幅に短縮されます。添付の makefile を 使う場合は、UCB make, GNU make, MS nmake では、 make PREPROCESSED=1 BC make では make -DPREPROCESSED=1 とすると、自動的にヘッダファイルを pre-preprocess した上でコンパイルしま す。LCC-Win32 の make では if 文による場合分けができないので、makefile を修正してリコンパイルする必要があります。修正の内容は makefile そのもの に書いてあります。 UCB make, GNU make, MS nmake では、MALLOC=KMMALLOC というオプションを 付けて make すると、私が書いた malloc() をリンクします。これについては [4.extra] を見てください。BC make では同じことを -DKMMALLOC というオプシ ョンで指定します。LCC-Win32 make で私の malloc() をリンクするためには、 makefile を修正する必要があります。 * FreeBSD では cpp を置くディレクトリは /usr/libexec である。mcpp- manual.txt [2.1] を参照のこと。 Vine Linux 2.6 (i386) では /usr/lib/gcc-lib/i386-redhat-linux/2.95.3 というひどく奥深いディレクトリになっている。Linux / GNU C では distribution やその version に応じて makefile のこのディレクトリの指 定を書き換える必要がある。Include directory もいろいろあるので、確か めなければならない。 また、GNU C 2.95 や 3.2 では cpp のほかに cpp0 があり、gcc から呼び 出されるのは cpp0 のほうである。 なお、mcpp-manual.txt [3.9.5], [3.9.7] も参照のこと。GNU C V.3 では プリプロセスがコンパイラ (cc1, cc1plus) に吸収されてしまったので、 MCPP を使うには gcc, g++ の呼び出しを shell-script に置き換える必要 がある。 [3.7.1] Plan 9 / pcc でのコンパイル Plan 9 には Dennis Ritchie の書いたプリプロセッサと Ken Thompson の書 いたコンパイラがあります。コンパイラはプリプロセス機能を内蔵したものです (ただし、#if が実装されていない)。 コンパイラ・ドライバには cc と pcc の2つがあります。Cc はプリプロセッ サを呼び出さずにコンパイラを呼び出すものです。Pcc すなわち portable cc for ANSI & POSIX environment はプリプロセッサを呼び出してからコンパイラ を呼び出すものです。Include directory もこの両者で異なっています。 MCPP のソースには #if が使われているので、pcc でないとコンパイルできま せん。ここで説明するのは、MCPP を pcc でコンパイルして Ritchie の cpp と 置き換えて使う方法です。 Plan 9 ではまた、make は mk という名前のコマンドとなっており、makefile も mkfile という名前にするのが慣例です。mkfile の書式は他の make とはか なり違っていて、Plan 9 専用のものとなっています。また、if, else は使えな いので、コンパイルの仕方を変える時は mkfile を書き換える必要があります。 MCPP をコンパイルするには MCPP の src ディレクトリで、noconfig.H に [3. 1.9] にあるような方法で変更を加えた上で、 cp ../noconfig/plan9.mk mkfile mk mk install mk clean とします。すると、mcpp_std という名前の実行プログラムが /$objtype/bin/ ディレクトリにコピーされるので、 cp /$objtype/bin/cpp /$objtype/bin/cpp_ritchie として Ritchie cpp を保存した上で、 cp /$objtype/bin/mcpp_std /$objtype/bin/cpp とします。$objtype という環境変数は CPU が x86 の場合は 386 となるので、 直接 386 とタイプすればすみます。 MCPP の "pre-preprocess" 機能を使って MCPP をリコンパイルするには、 mkfile の PREPROCESSED=0 という行を PREPROCESSED=1 と書き換えた上で、 mk preprocessed とします。mk install 以下は上記と同様です。 [3.8] MCPP をコンパイルできる処理系 各処理系に移植するためにはいくつかの設定が必要ですが、MCPP のソースを コンパイルすること自体は、K&R 1st. の仕様を満たしている処理系であれば十 分できます。正確に言えば、K&R 1st. と Standard C との共通仕様を満たして いれば十分です。 C++ でもコンパイルできます(C++ であるかどうかは、#ifdef __cplusplus で判断している)。次の手順でコンパイルします。 1. lib.c 以外の *.c を *.cc あるいは *.cpp と rename する。 2. make する。MCPP を使って "pre-preprocess" する場合は cpp に -+ オ プションを付ける。 添付の *.mak では、処理系によって make に CPLUS=1 または -DCPLUS=1 の オプションを付けて起動します。しかし、C++ でコンパイルしてもメリットは何 もありません。 char 型は符号付きでも符号なしでもかまいません。 浮動小数点演算は不要です。 プリプロセッサも同様で、それどころかいわゆる Reiser model cpp (MCPP の OLD_PREPROCESSOR モードがこれに近い)でも可です。#if 式に int の定数 式しか使えないものや、その評価がかなりいいかげんなものでも大丈夫でしょう。 MCPP 自身を使ってリコンパイルする場合も、MCPP の実行プログラムは system. H でどういうモードの設定をして生成したものでもかまいません(ただし、標準 ヘッダがそのモードの MCPP の持たない仕様を前提としている場合は話は別であ る)。 このソースは処理系の微妙な差に影響されないように書いてあります。 Standard C で新設された機能は使っていません。Standard C に完全に対応した プリプロセッサのソースを portable に書くためには、こういう方法を採る必要 があるのです。* もっとも、実際に各処理系でコンパイルするためには、さらにその処理系のバ グも回避する必要があります。これはやってみないと何が出てくるかわかりませ ん。私が移植したいくつかの処理系でも、バグであることを確かめその回避方法 を見つけるまでにかなりの時間がかかってしまったことが何回かあります。 * ただし、printf() での long long の length modifier の記述には文字列 リテラルの連結という Standard C の仕様を使っている。Length modifier が処理系によって異なるため、この方法をとるしかないのである。そして、 long long を持つ処理系が文字列リテラルの連結ができないということはな いはずである。 [3.9] コンパイルする処理系とターゲットの処理系 MCPP のソースをコンパイルする処理系(ホスト)と、それによって生成され た MCPP の実行プログラムを使う処理系(ターゲット)とは、必ずしも同じであ る必要はありません。これが違っている場合は、noconfig.H (configed.H) の SYSTEM, COMPILER でターゲットを指定し、HOST_SYSTEM, HOST_COMPILER はホス トを指定します。また、PART 1 にある諸定義はターゲット用のもので、PART 2 にあるものはホスト用の設定です。system.c は主としてターゲット用のもので す。lib.c はホスト用の設定でコンパイルします。 ただし、次のような制限があります。 1.ホスト処理系はターゲット処理系と同じOS上のものであるか、またはク ロスコンパイラであることが必要です。 2.ホスト処理系の long (unsigned long) はターゲット処理系のそれより範 囲が狭くてはいけません。これは Standard C で規定されている条件でもありま す。C99 では long long (unsigned long long) について同じことが言えます。 なお、ここで言うホストとターゲットというのは、クロスコンパイラのそれと は関係ありません。クロスコンパイルするのはコンパイラ本体の仕事で、プリプ ロセッサは原則としてそれには関知しません。Cpp を「クロスコンパイラに」移 植する場合は、そのクロスコンパイラがここで言うターゲット処理系です。ホス ト処理系としてはクロスコンパイラでないものを使うことになるはずです。Cpp を「クロスコンパイラで」コンパイルする場合は、そのクロスコンパイラがホス ト処理系で、クロスコンパイラのターゲットがターゲット処理系となります。 UNIX 上で動作する Windows 用プログラムのクロス処理系の MCPP を作るよう な場合は、noconfig.H の PART 1 で MBCHAR を EUC_JP ではなく SJIS としま す。すなわち、multi-byte character encoding の指定はターゲットのそれに合 わせます(encoding は実行時に指定することもできる)。 ただし、MCPP では、MCPP をコンパイルするホスト処理系の文字セットとそれ を使うターゲット処理系の文字セットとはともに ASCII であると仮定していま す。クロスコンパイラのホストとターゲットの文字セットについても同様です。 もしこの両者で文字セットが異なる場合は、mbchar.c の type_*[] と必要なら noconfig.H の ALERT, VT をターゲット処理系のそれに合わせ、文字列リテラル の連結を MCPP ではやらないようにすれば(CONCAT_STRINGS を FALSE とする) 良いのではないかと思いますが(文字列リテラルと文字定数中の numeric escape sequence 以外の文字はクロスコンパイラのコンパイラ本体が変換し、隣 接する文字列リテラルの連結もしてくれるのであれば)、私は ASCII 以外の文 字セットもクロスコンパイラというものも使ったことがないので、よくわかりま せん。 [3.10] 対応していない処理系 MCPP が対応していないのは、特殊な文字セットを持つ処理系と特殊な CPU で す。 EBCDIC には対応していません。EBCDIC は mbchar.c の type_*[] という文字 タイプのテーブルを書き替えるだけで対応できるのではないかと思いますが、私 のところにはテストする環境がありません。 また、整数演算が2の補数でない CPU にも、対応していません。2の補数で ない場合は、#if 式でオーバーフローが発生した時に、おかしくなるかもしれま せん。 [3.11] MS-DOS のメモリモデル MCPP を MS-DOS 上の処理系に移植する場合は、メモリモデルによって translation limits とメモリ消費と速度が異なるので、目的に応じて適当なメ モリモデルを選択する必要があります。 小さいプログラムの処理には small data memory model でもすみます。しか し、マクロ定義が数百個以上もあったり、1 KB 以上に及ぶ長大なマクロ定義が あったり、長大な定義を持つマクロをネストして呼び出したりするソースをコン パイルする必要があるなら、large data モデルでコンパイルしておいてくださ い(small data モデルでコンパイルした cpp に比べて5〜6割、速度が遅くな るが)。 MCPP の全機能を使うためには large code モデルでコンパイルする必要があ ります。small code モデルの場合は、system.H で OK_MAKE や DEBUG_EVAL を FALSE にするなどして、一部の機能を実装しないようにします。 ☆ 4.各処理系に移植する方法:詳細 ☆ [4.1] noconfig.H, configed.H, system.H の設定 これらのヘッダファイルに記述されていることの意味は、たいていは読めばわ かると思います。コメントも多く書き込んであります。さらに念のために以下に 注釈を書いておきます。 PART 1, PART 2 は noconfig.H (configed.H) にあり、PART 3 は system.H にあります。 まず、ターゲットシステム(MCPP を移植するシステム)とホストシステム (MCPP をコンパイルするシステム)を指定します。 SYSTEM ターゲット処理系が動くOSを指定します。OSの名前はこの直後に定義さ れています。名前が定義されていないOSは適宜定義してください。 COMPILER ターゲット処理系を指定します。コンパイラの名前はこの直後に定義されて います。名前が定義されていないコンパイラは適宜定義してください。 VERSION_MSG -v オプションや usage() 文で表示するためのバージョン情報を文字列リテ ラルで書きます。 HOST_SYSTEM, HOST_COMPILER ホストOS、ホスト処理系を指定します。ターゲットと同じ場合は、 #define HOST_SYSTEM SYSTEM #define HOST_COMPILER COMPILER としておきます。 SYSTEM, COMPILER の命名には一定の規則がありますが、ソースを見たほうが わかりやすいでしょう。いささか仰々しい形になっていますが、SYSTEM はイン クルードファイルのパスリストの形式やOS標準のインクルードディレクトリ等 を知るためにしか使われていないので、あまり考える必要はありません。 [4.1.1] PART 1 ターゲットシステムの設定 [4.1.1.1] 事前定義マクロ CPU_OLD, CPU_STD1, CPU_STD2, SYSTEM_OLD, SYSTEM_STD1, SYSTEM_STD2, SYSTEM_EXT, SYSTEM_EX2, COMPILER_OLD, COMPILER_STD1, COMPILER_STD2, COMPILER_EXT, COMPILER_EX2 MCPP で pre-define する処理系固有のマクロの名前を文字列リテラルで指 定します。不要なものは定義しないでおきます(0 個のトークンに定義して はいけない)。*_OLD で生成されるのは '_' (underline) で始まらない古 い流儀のマクロで、これらは MCPP 実行時に -S オプションで に 1 以上を指定すると pre-define されません。*_STD?, *_EXT, *_EX2 では 必ず _ で始まるマクロ名を指定します。*_STD1 は __ で始まるもので、* _STD2 は __ で始まって __ で終わるものです。SYSTEM_EXT, SYSTEM_EX2, COMPILER_STD1, COMPILER_STD2, COMPILER_EXT, COMPILER_EX2 ではそのマ クロの値も SYSTEM_EXT_VAL, SYSTEM_EX2_VAL, COMPILER_STD1_VAL, COMPILER_STD2_VAL, COMPILER_EXT_VAL, COMPILER_EX2_VAL で指定します。 これは整数を "" で囲んだ文字列リテラルで指定します。0 個のトークンに 展開されるマクロは "" と定義します。指定されなければ、そのマクロの値 は 1 になります。その他の predefined マクロ(CPU_*, SYSTEM_OLD, SYSTEM_STD1, SYSTEM_STD2, COMPILER_OLD で指定されるもの)はすべて 1 の値を持ちます。 CPU_SP_OLD, CPU_SP_STD 処理系固有の特殊な事前定義マクロの名前を文字列リテラルで書きます。値 はすべて 1 となります。 SYSTEM_SP_OLD, SYSTEM_SP_STD 処理系固有の特殊な事前定義マクロの名前を文字列リテラルで書き、その値 を SYSTEM_SP_OLD_VAL, SYSTEM_SP_STD_VAL で定義します。 COMPILER_SP1, COMPILER_SP2, COMPILER_SP3 処理系固有の特殊な事前定義マクロの名前を文字列リテラルで書き、その値 を COMPILER_SP1_VAL, COMPILER_SP2_VAL, COMPILER_SP3_VAL で定義します。 COMPILER_CPLUS, COMPILER_CPLUS_VAL -+ オプション(C++ プリプロセス)を指定した時に定義される処理系固有 の事前定義マクロの名前とその値を上記と同じように文字列リラテルで指定 します。COMPILER_CPLUS_VAL を指定しないと、そのマクロの値は 1 になり ます。名前は '_' で始まるものでなければなりません。必要がなければ COMPILER_CPLUS そのものを定義しないでおきます。 以上の設定で事前定義されたマクロはすべて -N オプションで無効となります。 [4.1.1.2] Include ディレクトリ等 C_INCLUDE_DIR1, C_INCLUDE_DIR2, CPLUS_INCLUDE_DIR1, CPLUS_INCLUDE_DIR2, CPLUS_INCLUDE_DIR3 MCPP でサーチする標準ヘッダファイルの include directory を指定します。 CPLUS_INCLUDE_DIR? は C++ の include directory がCのものと違ってい る場合に、設定します(MCPP を起動する時に -+ オプションを指定するこ とで有効となる)。UNIX の /usr/include, /usr/local/include は system. c で設定されるので、C_INCLUDE_DIR? にはそれ以外の処理系固有のものを 指定します。 ENV_C_INCLUDE_DIR, ENV_CPLUS_INCLUDE_DIR MCPP でサーチする標準ヘッダファイルの include directory を実行時に環 境変数で指定する場合のために、その環境変数の名前に定義します。 ENV_CPLUS_INCLUDE_DIR は C++ の include directory を指定する環境変数 の名前です。デフォルトではそれぞれ "INCLUDE", "CPLUS_INCLUDE" に定義 されます。GNU C に実装する場合は "C_INCLUDE_PATH", "CPLUS_INCLUDE_PATH" がデフォルトです。 サーチパスはその外、system.c で設定されるもの、-I オプションで設定さ れるものがあります(それらの優先順位については mcpp-manual.txt [4.2] を参照)。 ENV_SEP この環境変数に複数のパスを書く時の separator を文字定数で書きます。 /usr/local/abc/include:/usr/local/xyz/include と書く時の ':' や C:BC55/INCLUDE;C:BC55/LOCAL/INCLUDE と書く時の ';' がこれです。 SEARCH_INIT ターゲット処理系が include ファイルをサーチする時のルールを書きます。 #include "../dir/header.h" といったディレクティブを処理する時に、 include ディレクトリをサーチする前にどのディレクトリをサーチするのか というルールです。カレントディレクトリからの相対パスをサーチする処理 系では CURRENT とし、ソースファイル(include 元)のあるディレクトリ からの相対パスをサーチする処理系では SOURCE とします。この両者の組合 わせである場合は (CURRENT & SOURCE) とします。 [4.1.1.3] 行番号情報の出力仕様その他 LINE_PREFIX MCPP から compiler-proper にファイル名と行番号情報を伝える形式を設定 します。 #line 123 "fname" というCのソースの形式がデフォルトとなっています。その他の形式を使う 処理系では、この "#line " の部分を置き換える sequence を文字列リテラ ルで書いておきます。 # 123 "fname" という形式なら "# " と定義し、どちらでもない独自の形式ならそれに合わ せて定義します(場合によっては system.c の sharp() 等に書き足さなけ ればならないかもしれない)。 処理系によっては、GNU C のように、付属プリプロセッサの出力は1番目の 形ではないが、compiler-proper は1番目の形も認識する場合があります。 一般には、1番目のほうがCのソースと同じ形で一般性があるので好ましい と言えます。Compiler-proper がこれを認識すれば、同一OS上での cpp は処理系が違っても1つでも足りるくらいです。 しかし、実行時オプションが違うという問題があるので、実際にはそう簡単 ではありません。また、GNU の tool の中には GNU C / cpp 固有の出力形 式をあてにしているものもあるので、GNU C ではそちらを選んだほうが無難 です(GNU C 用ではそちらをデフォルトにしている)。 Visual C++ .net や Borland C のような1パスコンパイラの前段に MCPP を使う場合は、組み込みプリプロセッサに出力を渡すことになるので、MCPP の出力はCのソースになっていなければなりません。したがって、行番号の 受け渡しは1番目の形式でなければなりません。 EMFILE で too many open files (for the process) を意味する errno の値を表すマクロが EMFILE でない場合は、EMFILE をそのマクロ名に定義 します(もちろん、 自体に書き加えてもかまわない)。 ONE_PASS ターゲット処理系がプリプロセッサの分離されていないいわゆる「ワンパス コンパイラ」であればこれを TRUE に、そうでなければ FALSE に定義しま す。これを TRUE とすると、#pragma MCPP put_defines (#put_defines) で は、処理系の事前定義マクロはすべてコメントで囲んで出力します。ワンパ スコンパイラでは MCPP の出力をこれに与えると再度プリプロセスされるこ とになるので、二重定義を避けるためです。 ただし、GNU C V.3 はワンパスコンパイラと言えますが、独立したプリプロ セッサを使うこともできるので、このマクロは FALSE としておきます。 FNAME_FOLD ファイル名の大文字と小文字が区別されないOSではこれを TRUE と定義し、 そうでなければ FALSE とします。 [4.1.1.4] 処理系の言語仕様に応じた設定 HAVE_PRAGMA #pragma を認識する compiler-proper であればこれを TRUE とし、そうで なければ FALSE とします。 EXPAND_PRAGMA MODE == STANDARD で -V199901L オプションを指定した時に #pragma 行の 引数が STDC 以外であればマクロ展開の対象となる処理系では、これを TRUE に定義します。デフォルトでは FALSE としています。Visual C では #pragma 行の引数は常にマクロ展開の対象となるので、これは TRUE としま す。しかし、MCPP は Visual C 用でも、C99 で #pragma の引数が STDC ま たは MCPP で始まらない場合だけマクロ展開の対象とします。 HAVE_DIGRAPHS Digraphs 処理が実装されている場合は TRUE、そうでなければ FALSE とし ます。 CAN_CONCAT_STRINGS 隣接する文字列リテラルがコンパイラによって連結される処理系では、これ を TRUE とします。 STDC ターゲット処理系の事前定義マクロ __STDC__ のデフォルト値に定義します。 __STDC__ が定義されていなければ 0 とします。 STDC_VERSION ターゲット処理系の事前定義マクロ __STDC_VERSION__ のデフォルト値に定 義します。__STDC_VERSION__ が定義されていなければ 0L とします。 CHARBIT, UCHARMAX, LONGMAX, ULONGMAX ターゲット処理系の の CHAR_BIT, UCHAR_MAX, LONG_MAX, ULONG_MAX の値を書きます。unsigned long のない処理系では ULONGMAX は LONG_MAX の値を使います。 がなくても定義は簡単です。 HAVE_C_BACKSLASH_A ターゲット処理系のコンパイラ本体が '\a', '\v' という escape sequence を認識する場合はこれを TRUE にし、そうでない場合は FALSE にします。 [4.1.1.5] Multi-byte character MBCHAR というマクロは multi-byte character の encoding を指定するもの です。16 ビットシステムでは、指定された1種類の encoding しか使えません。 しかし、32 ビット以上のシステムでは、下記の数種の encoding がすべて同時 に実装されます。MBCHAR はデフォルトの encoding を指定するだけで、実行時 に encoding を環境変数・オプション・#pragma で変更することができます(使 い方については mcpp-manual.txt の [2.3], [2.8], [3.4] を参照)。 MBCHAR ターゲットの multi-byte character すなわち日本語では漢字の encoding の種類を定義します。 16 ビットシステムと 32 ビット以上のシステムのどちらでも指定できる encoding は次の通りです。 EUC_JP : 日本の extended UNIX code (UJIS) SJIS : 日本の shift-JIS (MS-Kanji) GB2312 : 中国の EUC-like な GB-2312(簡体字) BIGFIVE : 台湾の Big Five(繁体字) KSC5601 : 韓国の EUC-like な KSC-5601 (KSX 1001) これらはいずれも shift-states を持たない、1文字が2バイトを占める encoding です。なお、multi-byte character, wide character の encoding が2バイトであるにもかかわらず、wchar_t が4バイトの型にな っている処理系もありますが、プリプロセッサは wchar_t の型には関知し ません。ソース上では multi-byte character や wide character が2バイ トを占めているので、それに従って処理をします。 32 ビット以上のシステムでは次の encoding も指定できます。 ISO2022_JP : ISO-2022-JP1 という国際規格の日本語 UTF8 : unicode の encoding の1種である UTF-8 ISO-2022-* は shift-states を持つ encoding です。UTF-8 は2バイトの unicode を1バイトないし3バイトで encode するものです。漢字は3バイ トになります。 MBCHAR を 0 に定義した場合、16 ビットシステムでは multi-byte character の処理は実装されませんが、32 ビット以上のシステムでは multi-byte character の処理をしないのがデフォルトの仕様になるだけで、 環境変数・オプション・#pragma で実行時に変更されます。 SJIS_IS_ESCAPE_FREE コンパイラ本体が shift-JIS の処理をする場合は TRUE とし、しない場合 は FALSE とします。 Shift-JIS では漢字の2バイト目が '\\' と同じ 0x5c の値になることがあ ります。コンパイラ本体が shift-JIS を認識しない場合は、これを escape sequence と解釈してしまい、tokenization でエラーになります。 SJIS_IS_ESCAPE_FREE を FALSE とすると、MCPP が処理を補います。すなわ ち、最終出力の時に、文字列リテラルまたは文字定数の中の shift-JIS 漢 字第2バイトが 0x5c であった場合は、そこに 0x5c のバイトをもう1つ付 加します。これによって、英語版のコンパイラを一応 shift-JIS に対応さ せることができます。 BIGFIVE_IS_ESCAPE_FREE 同様に、コンパイラ本体が Big Five の処理をする場合はこれを TRUE とし、 しない場合は FALSE とします。 IS02022_JP_IS_ESCAPE_FREE 同様に、コンパイラ本体が ISO-2022-JP の処理をする場合はこれを TRUE とし、しない場合は FALSE とします。IS0-2022-* では '\\' ばかりでなく、 '\'' や '"' と一致するバイトも出現します。ISO2022_JP_IS_ESCAPE_FREE が FALSE の場合は MCPP は、'\\', '\'', '"' と一致するバイトの直前に すべて 0x5c のバイトを1つ挿入します。 なお、multi-byte character に関するコンパイラの動作は実行する時の環境 によって変わる場合があります。自分の使う環境に合わせて設定してください。 これについては、mcpp-manual.txt の [2.8] も参照してください。 [4.1.1.6] ターゲットとホストに共通の設定 次の4つは便宜上、PART 1 に書いてありますが、ターゲット処理系とホスト 処理系の双方が指定の型を持つ場合に TRUE とし、そうでない場合は FALSE と します。HAVE_LDBL は後述する OK_SIZE が TRUE の場合だけ使われるものです。 HAVE_UNSIGNED_LONG unsigned long というデータ型を持つ処理系ではこれを TRUE とします。 HAVE_LONG_LONG long long というデータ型を持つ処理系ではこれを TRUE とします。 Visual C や Borland C 5.5 のように、long long はないが __int64 とい う同じサイズの型があり printf() で表示するための length modifier も 用意されている場合は、これは TRUE とします。 HAVE_LONG_DOUBLE long double というデータ型を持つ処理系ではこれを TRUE とします。 HAVE_INTMAX_T intmax_t というデータ型が定義されていればこれを TRUE とします。 LL_FORM ターゲット処理系が long long を持っている場合は、その処理系の最大の 整数型の値を printf() で表示するための length modifier を文字列リテ ラルで定義します。C99 では "j" です。また、C99 では long long の length modifier は "ll" (ell-ell) です。Visual C, Borland C 5.5 では __int64 の値を表示する "I64" を使います。 [4.1.2] PART 2 ホストシステムの設定 configed.H ではターゲット処理系とホスト処理系とが同一であると仮定して いますが、異なる場合は PART 2 を書き直す必要があります。 PROTO Compiler-proper が関数のプロトタイプ宣言のできるものであればこれを TRUE とし、そうでなければ FALSE とします。 HOST_HAVE_GETENV 処理系が getenv() という関数を持っていればこれを TRUE とし、そうでな ければ FALSE とします。ターゲットのOSも当然、環境変数を持っている 必要があります。 HOST_HAVE_GETOPT, HOST_HAVE_STPCPY, HOST_HAVE_MEMMOVE, HOST_HAVE_MEMCPY, HOST_HAVE_MEMCMP, HOST_HAVE_STRSTR, HOST_HAVE_STRCSPN ホスト処理系のライブラリに getopt(), stpcpy(), memmove(), memcpy(), memcmp(), strstr(), strcspn() があればそれぞれ TRUE に、なければ FALSE に定義します。FALSE とされた関数には lib.c のものが使われます。 FILENAMEMAX ホスト処理系の の FILENAME_MAX の値です。FILENAME_MAX がな い場合は、MS-DOS では 80、他のOSでは BUFSIZ としておいてかまいませ ん。 HOST_HAVE_C_BACKSLASH_A ホスト処理系のコンパイラ本体が '\a', '\v' という escape sequence を 認識しない場合は、この定義を FALSE にします。 あとはライブラリ関数の宣言、size_t の型定義等ですが、見ればわかるでし ょう。 [4.1.3] PART 3 MCPP の動作仕様の設定 [4.1.3.1] 新旧各種のモードの指定 MODE マクロの展開方法、使えるディレクティブ、使える predefined マクロ等、 プリプロセッサの根幹となる動作の仕様を指定します。PRE_STANDARD, STANDARD の2種があります。これらのマクロはそれぞれ 0, 1 の値を持ち ます。 PRE_STANDARD C90 以前のプリプロセス仕様です。K&R 1st. はこれです。 STANDARD Standard C (C90, C99, C++98) のプリプロセス仕様です。 STANDARD と PRE_STANDARD のモードとでは、マクロ展開方法の違いが多 くあります。Standard C と pre-Standard との違いだと思って間違いあり ません。最も大きな違いは、関数様マクロ(引数付きマクロ)の展開で、引 数にマクロが含まれている場合、MODE == STANDARD では引数を先に完全に 展開してから元マクロの置換リスト中のパラメータと置き換えるのに対して、 PRE_STANDARD では展開せずにパラメータと置換し、再スキャン時に展開す ることです。 また、MODE == STANDARD では直接にも間接にもマクロの再帰的展開はし ません。PRE_STANDARD では再帰的なマクロ定義があると、展開時にエラー となります。 行末にある \ の扱いも MODE によって異なり、MODE == STANDARD では tri-graph 処理の後、tokenization の前に の sequence を削除しますが、MODE == PRE_STANDARD では文字列リテラルの中 にある場合と #define 行にある場合に限ってこれを削除します。 いわゆる tokenization(トークン分割、トークンの切り出し)も MODE によって微妙に違います。 MODE == STANDARD では「token base での動作」という建前に忠実に tokenization を行います。具体的には、MODE == STANDARD では、マクロを 展開するとその前後に space を挿入して、前後の token との意図しない連 結が発生するのを防ぎます。MODE == PRE_STANDARD は伝統的・便宜的・暗 黙的な tokenization と、「character base でのテキスト置換」によるマ クロ展開方法の痕跡を残しています。 これらの点については、cpp-test.txt の [1] をご覧ください。 MODE == STANDARD では preprocessing number という数値トークンを規 定通りに扱います。PRE_STANDARD では、数値トークンはCの整数定数トー クンおよび浮動小数点数トークンと同じです。整数定数での接尾子 'U', 'u', 'LL', 'll'、浮動少数点定数での接尾子 'F', 'f', 'L', 'l' は トー クンの一部として認識しません。 ワイド文字の文字列リテラルと文字定数は MODE == STANDARD でしか単一 のトークンとして認識されません。 Digraph, #error, #pragma, _Pragma() operator は MODE == STANDARD でないと使えません。-S オプション(strict-ansi モード)と -+ オ プション(C++ プリプロセッサとして動作する)も MODE == STANDARD でし か使えません。Pre-defined マクロ __STDC__, __STDC_VERSION__ は MODE == STANDARD の時に定義され、PRE_STANDARD の時は定義されません。[4.1. 3.2] で述べる OK_DIGRAPHS, CONCAT_STRINGS も MODE == STANDARD でない と TRUE にできません。 UCN (universal-character-name) は MODE == STANDARD でしか使えませ ん。 Trigraphs も MODE == STANDARD でしか実装できません。 #if defined, #elif は PRE_STANDARD では使えません。#include, #line の引数にマクロを使うことは PRE_STANDARD ではできません。Pre-defined マクロ __FILE__, __LINE__, __DATE__, __TIME__ は PRE_STANDARD の時は 定義されません。 他方で、#assert, #asm (#endasm), #put_defines, #debug は MODE == PRE_STANDARD の時しか実装できません。 #if 式は MODE == STANDARD では long / unsigned long または long long / unsigned long long で評価しますが、MODE == PRE_STANDARD では long だけで評価します。 診断メッセージの出方もモードによって少し違っています。それについて は mcpp-manual.txt の [5] を見てください。 以上に述べたこと以外で K&R 1st. に Standard C と異なる明確な規定の ないことがらについては、MODE == PRE_STANDARD では C90 の規定に従いま す。 コンパイル時の MODE の設定のほかに、実行時オプションで指定されるモ ードもあります。MODE == STANDARD でコンパイルされた MCPP には自称 "post-Standard" モードを指定するオプションがあり、MODE == PRE_STANDARD でコンパイルされた MCPP には "Reiser cpp" モードを指定 するオプションがあります。また、MODE == STANDARD には "compat" モー ドという特殊なオプションもあります。しかし、これらはいずれもコンパイ ル時の設定は関係ありません。 [4.1.3.2] 動作モードの細部の指定 OK_SIZE これを TRUE にすると、#if, #elif 行で sizeof (type) が使えます。 これを TRUE にして GNU C でコンパイルする場合は、少なくとも eval.c のコンパイルでは -ansi, -pedantic, -pedantic-errors オプションは付け ないでください。 CPLUS -+ オプションで C++ のプリプロセッサとして動作させた時に、標準組み込 みマクロ __cplusplus がこの値に事前定義されます。ISO C++ Standard で は 199711L です。-V オプションによって実行時に変更できます。 OK_TRIGRAPHS Trigraphs 処理を実装する場合は TRUE、そうでなければ FALSE とします。 ただし、MODE == PRE_STANDARD ではどちらにしても trigraph は実装でき ません。 TFLAG_INIT OK_TRIGRAPHS == TRUE の場合の trigraph 処理の初期状態を規定します。 これを TRUE にすると、default で trigraph が認識され、-3 オプション で起動すると認識しません。FALSE の場合は逆に default で認識せず、-3 オプションで認識するようになります。 OK_DIGRAPHS Digraphs を実装する場合はこれを TRUE に定義します。MODE == STANDARD では、HAVE_DIGRAPHS == FALSE の処理系の場合、digraphs は MCPP が通常 のトークンに変換します。 DIGRAPHS_INIT OK_DIGRAPHS == TRUE の場合の digraph 処理の初期状態を規定します。こ れを TRUE にすると、default で digraph が認識され、-2 オプションで起 動すると認識しません。FALSE の場合は逆に default で認識せず、-2 オプ ションで認識するようになります。 OK_PRAGMA_OP これを TRUE にすると、__STDC_VERSION__ >= 199901L の時に _Pragma() operator が有効になります。この演算子は C99 のものです。 これは MODE == STANDARD でしか TRUE にできません。 OK_UCN MODE == STANDARD で -V199901L または -+ オプションを指定した時に UCN (universal-character-name) を有効にするには、これを TRUE に定義しま す。デフォルトでは TRUE としています。 OK_MBIDENT MODE == STANDARD で -V199901L オプションを指定したときに identifier 中に multi-byte character を使えるようにするには、これを TRUE に定義 します。デフォルトでは FALSE としています。 CONCAT_STRINGS 隣接する文字列リテラルを MCPP が連結します。そのために必要な最小限の escape sequence の変換も、それに先立ってやります。CAN_CONCAT_STRINGS == FALSE の処理系のためのものです。ただし、CONCAT_STRINGS と OK_UCN を共に TRUE としても、UCN の multi-byte-character への変換はしません。 これは MODE == STANDARD の時しか TRUE にできません。 なお、この連結では、前の文字列の最後が16進または8進の escape sequence で、後の文字列の先頭がそのまま連結するとこの escape sequence の一部となってしまう恐れのある場合は、後者の文字を3ケタの 8進 escape sequence に変換した上で連結するという方法で、誤動作を回 避しています。ところが、Borland C には8進 escape sequence に関して は微妙なバグや奇怪な仕様があります。MCPP では文字列リテラルの連結が このバグの影響を回避するように工夫しています。 expr_t, uexpr_t 最も大きい整数の型に typedef で定義します。intmax_t, uintmax_t とい う型があればそれに、そうでなければ long long, unsigned long long を 持つ処理系ではそれに、そうでなければ __int64, unsigned __int64 を持 つ処理系ではそれに、そうでなければ long, unsigned long に、unsigned long もない処理系では long, long に定義します。なお、C99 では long long, unsigned long long が必須となっています。 EXPR_MAX uexpr_t の最大値に定義します。 * UCN は C++, C99 の仕様で、Unicode の文字の値を \u または \U で始まる 16進 escape sequence で表記するものである(mcpp-manual.txt [3.7], cpp-test.txt [1.8], [3.5] 参照)。 [4.1.3.3] 特殊な指定 DOLLAR_IN_NAME これを TRUE にすると、識別子中に $ を使えるようになります。 TOP_SPACE これを TRUE にすると、行頭のコメントを含む white spaces を(原則とし て a space に圧縮したうえで)出力します。FALSE にすると削除します。 Cのソースでは行頭の white spaces には何の意味もないので FALSE でか まわないはずなのですが、アセンブラプログラムをCソース中に置くという 規格違反ソースを コンパイルする場合などには、TRUE でコンパイルしてお くことが必要になります。MCPP をC以外の何かの言語のプリプロセッサに 流用する場合にも、必要になるかもしれません。デフォルトでは TRUE とし ています。PRE_STANDARD で #asm が実装されている場合は、#asm ブロック 内では TOP_SPACE がどうであろうと行頭の white spaces は削除せず、 tokenization も行いません。 OK_MAKE これを TRUE にすると、makefile 用依存関係行を出力する -M* オプション が実装されます。 次の2つは MCPP 自身のデバッグ用のものですが、tokenization やマクロの 展開機序や #if 行の評価機序をトレースすることができます。 DEBUG これを TRUE にすると、デバッグ用のルーチンを実装します。 DEBUG_EVAL これを TRUE にすると、#if, #elif 行に関するデバッグ用ルーチンを実装 します。 [4.1.3.4] Translation limits の指定 RESCAN_LIMIT マクロ展開時の再走査回数の限度を定義します。MODE == PRE_STANDARD で は再帰的なマクロ展開によって無限ループが発生しえますが、それがこのリ ミットにひっかかります。MODE == STANDARD では再走査回数は少ないので、 あまり大きな値を設定する必要はありません。 NBUFF 論理行(ソースの物理行の行末の \ を取って接続した行)の最大長 +1 を 定義します。コメントを a space に変換した後の行(コメントによって複 数の論理行にまたがることもありうる)もこの長さにおさまっていなければ なりません。 NMACWORK マクロ展開の内部的なバッファのサイズを定義します。すなわち、1つの論 理行中のマクロを展開した結果(マクロ呼び出しが複数行にまたがる場合は、 それを展開した結果)はこのサイズにおさまっていなければなりません。こ れはまた、1つのマクロ定義の置換リストを内部的に記憶する際の最大長と しても使われます。 NWORK MCPP の出力する最大行長を定義します。これは compiler-proper の受け取 れる最大行長+1 を越えてはいけません。また、NBUFF, NMACWORK の値を越 えてもいけません。NWORK < NMACWORK の場合、マクロ展開後の行長がこれ を越えた時は、MCPP がこれ以下の行長に分割して出力します。文字列リテ ラル(CONCAT_STRINGS == TRUE の場合は連結後の文字列リテラル)の長さ は NWORK-2 の範囲に収まっていなければなりません(文字列リテラルの長 さというのは、char 配列の要素数のことではなく、ソース上での文字列リ テラルというトークンの長さである。両端の " を含み、\n 等は2バイトと 数える。ワイド文字列リテラルでは先頭の L も含む)。 IDMAX 識別子の最大長を定義します。これより長い名前もエラーにはなりませんが、 この長さに切り詰められます。 NMACPARS 関数様マクロの引数の最大数を定義します。これは UCHARMAX よりも大きく はできません。 NEXP #if 行の式のカッコでくくられるネストレベルの限度を定義します(本当は ネストレベルがこれで直接決まるわけではない。正確には、式中の定数トー クンの数がこの2倍、演算子トークンの数がこの3倍まで使える。かっこは 一対で2つと数える)。 BLK_NEST #if (#ifdef, #ifndef) section のネストレベルの限度(#if, etc. が何段 階にネストできるか)を定義します。 NINCLUDE サーチする include directory の最大数を定義します(#include のネスト レベルのことではない。ネストレベルには制限はない)。 SBSIZE マクロを内部的に hash で分類して記憶する際の hash table の要素数を定 義します。必ず2のベキ乗でなければなりません。マクロの数より小さくて も動作は正常にしますが、大きいほうが処理はやや速くなります。 それぞれ大きい値にするほど仕様は上等になりますが、NWORK, NBUFF, NMACWORK, SBSIZE は大きいとそれだけ大きなメモリを食います。その結果、ア ドレス空間の小さいシステムではマクロ定義の数が制限を受けます(正確にはマ クロ定義の数そのものではなく、それぞれのマクロ定義の長さの合計が問題。マ クロ定義の内部的な形式は internal.H の struct defbuf に書いてある)。 NWORK 等は system.H の default の値のままで、MS-DOS のスモールモデルでコ ンパイルした MCPP の場合、ごく単純なマクロ定義であれば数百個までは大丈夫 ですが、長大なマクロ定義が多いと限度はずっと下がるでしょう。 NMACWORK, NEXP, RESCAN_LIMIT はスタックを消費します。 他のものはメモリはさほど必要としませんが、system.H のデフォルトの値以 上にしても実用上の意味はほとんどないでしょう。 C90, C99 の要求する translation limits の最低限度は system.H の最後の ほうに書いてあります。C++ Standard の translation limits も書いてありま すが、これはCと異なり、要求仕様ではありません。 [4.2] system.c 主としてターゲット処理系に関するいくつかの設定を実装しています。 PATH_DELIM OSの path-delimiter を定義しています。PATH_DELIM は \ としてはいけ ません(プログラムのつごうで)。DOS/Windows 系では / としています。 ユーザプログラムではもちろん \ も使えますが、それを内部的には / に変 換します。 OBJEXT 処理系の生成するいわゆるオブジェクトファイルの接尾子を文字列リテラル で定義します。UNIX 上の処理系の "o"、DOS/Windows 上の処理系の "obj" 等です。これは -M* オプションを指定した時の makefile 用依存関係行の 出力に使われるものです。 do_options() MCPP を起動する時のオプションを実装しています。まだ implement されて いない処理系に implement する時には、その処理系付属のコンパイラドラ イバに合わせて、ここに何行か書き足すことが必要でしょう。do_options() に追加した時は、それに対応して次の set_opt_list(), usage() にも書き 足します。 do_options() は getopt() を呼び出します。そのため、1つのオプション 文字は引数なしか引数ありのどちらかに決めなければなりません。-P と -P- といったオプションの使い方は原則としてできません(しかし、処理系付属 のプリプロセッサとの互換性のために必要な場合は、無理やりできなくもな い。-M オプションの実装を参照)。また、-trigraphs といった長いオプシ ョンは t をオプション文字として rigraphs を引数とすることで実装する しかありません。 set_opt_list() MCPP のオプション文字を設定します。 usage() Usage 文が書かれています。アルファベット順になっています。 set_sys_dirs() Include directory を設定しています。noconfig.H (configed.H) のマクロ C_INCLUDE_DIR?, CPLUS_INCLUDE_DIR? で指定された処理系固有のディレク トリのほか、UNIX 系OSでの /usr/include, /usr/local/include もここ で設定しています(noconfig.H, configed.H のマクロ ENV_C_INCLUDE_DIR, ENV_CPLUS_INCLUDE_DIR で定義された名前の環境変数による include directory 指定は set_env_dirs() で設定される)。 do_pragma() #pragma の処理を実装しています。MCPP 自身が処理しない #pragma sub- directive はnoconfig.H (configed.H) のマクロ HAVE_PRAGMA が TRUE の 場合はそのまま出力して compiler-proper に渡し、そうでない場合は warning とともに捨てます。MCPP 自身が処理する #pragma MCPP debug 等 はここから呼び出す関数で処理します。MCPP 自身が処理する #pragma sub- directive は原則として MCPP という名前で始まるようにしています。その 後の扱いは原則として MCPP が処理しない #pragma と同様ですが、ただし、 #pragma MCPP put_defines, #pragma once は出力しません。ヘッダの「プ リプリプロセス」の機能を使って、出力を再度プリプロセスする場合のつご うがあるためです(mcpp-manual.txt の [3.1] 参照)。Standard C では処 理系固有の拡張 directive は #pragma sub-directive として実装すること になっています。この関数は #if MODE == STANDARD 〜 #endif で囲んであ ります。 do_old() Standard C に合致しない preprocessing directive (#pragma sub- directive でない #assert, #asm, #endasm, #include_next, #warning, # put_defines, #debug 等)が必要な場合は、その処理をする関数を書き足し たうえで、ここからそれを呼び出すようにします。呼び出される関数の定義 は #if MODE == PRE_STANDARD 〜 #endif で囲みます。(ただし、GNU C 用 では #include_next, #warning は MODE == STANDARD でも使えるようにし てある)。 [4.3] mbchar.c ここには multi-byte character 処理ルーチンがあります。MCPP の移植では このファイルをいじる必要は普通はないはずです。しかし、未実装の multi- byte character encoding を実装する場合は、ここに文字タイプのテーブルと処 理ルーチンを書き加えることになります。 Multi-byte character 処理の枠組みは、16 ビットシステム用と 32 ビット以 上のシステム用とで違っており、メモリーの少ない 16 ビットシステム用は実装 できる encoding が限定されています。 char type[] 16 ビットシステム用の文字タイプのテーブルです。基本文字セットが ASCII であることを前提として、EUC-JP, shift-JIS, KS C 5601, GB 2321- 80, Big Five のものが implement されています。これらはユーザ定義文字 (外字)のエリアも含んでいます。他の文字セットの場合は、これに書き足 す必要があります。Shift-states のない2バイト encoding であれば、 system.H の MBCHAR を定義し、このテーブルを対応させるだけですむはず です。 short type_*[] 32 ビット以上のシステム用の文字タイプのテーブルです。次の4つのテー ブルがあり、それぞれ下記の encoding の文字タイプが定義されています。 いずれも基本文字セットが ASCII であることを前提としています。 type_euc[] : EUC_JP, GB2312, KSC5601 type_bsl[] : SJIS, BIGFIVE type_iso2022_jp[] : ISO2022_JP type_utf8[] : UTF8 char * encoding_name[][] Multi-byte character encoding の名前のテーブルです。 set_encoding() #pragma MCPP setlocale、-m オプション、および環境変数 LC_ALL, LC_CTYPE, LANG の処理をするルーチンです。 mb_init() Multi-byte character encoding に応じた初期設定をするルーチンです。 mb_read_*() Multi-byte character を読むルーチンです。次の3つがあります。これら の関数はそれぞれ下記の encoding の multi-byte character 用のものです。 mb_read_2byte() : EUC_JP, GB2312, KSC5601, SJIS, BIGFIVE mb_read_iso2022_jp() : ISO2022_JP mb_read_utf8() : UTF8 mb_eval() Multi-byte character, wide character の文字定数の値を評価するルーチ ンです。#if 式の評価で使われます。 Multi-byte character の encoding については次の文献を初めとして、いく つかの資料を参照しました。 Ken Lunde "Understanding Japanese Information Processing" , 1993/09, first edition, O'Reilly & Associates, Inc. 安岡孝一・安岡素子『文字コードの世界』 , 1999/09, 東京電機大学出版局 [4.4] lib.c 処理系によっては持っていないかもしれない、あるいはあっても仕様に問題の あるライブラリ関数のソースがここに書いてあります。それぞれ、#if ! HOST_HAVE_XYZ 〜 #endif で囲んであるので、HOST_HAVE_XYZ == FALSE の時に この XYZ 関数が使われます。 [4.extra] malloc() 「kmmalloc -- デバッグ機能を持つ malloc()」というのは、私がCで書いた malloc(), free(), realloc(), calloc() の portable なソースです。これはメ モリ効率を改善するとともに、デバッグのつごうを考えて書いてあります。デバ ッグ用のルーチンも添付してあります。これをリンクしておくと、思わぬバグが ひっかかってくることがあります。*1, *2 noconfig/*.mak で -DKMMALLOC -D_MEM_DEBUG -DXMALLOC というオプションを 与えているのは、この私の malloc() 等とデバッグルーチンをリンクするための ものです。これをリンクした MCPP がEFREEP, EFREEBLK, EALLOCBLK, EFREEWRT, ETRAILWRT というエラー番号で途中で exit することがあれば、それは MCPP の バグを意味します。 ことに、MS-DOS / Borland C の large data memory model でコンパイルする 場合は、処理系付属の malloc()(というより free())は異常に速度が遅いので、 これらの関数を酷使している MCPP ではなるべく避けてください。WIN32 / Visual C++ の malloc() もかなり遅いので、避けたいところです。 BSD_MALLOC, DB_MALLOC, MALLOC_DBG というマクロのどれかを 1 に定義して MCPP をコンパイルすると、私の malloc() とは別のそれぞれデバッグ機能を持 った malloc() が使われます。いずれにしても、処理系付属のものではない malloc() を使うには、コンパイルする前にライブラリを作っておかなければな りません。これについては kmmalloc のドキュメントを見てください。 *1 kmmalloc は次のところにある。 http://download.vector.co.jp/pack/dos/prog/c/kmmalloc-2.5.lzh *2 CygWIN ではライブラリの組み立てが他の malloc() を使えないようになっ ているので、私の malloc() は使っていない。 ☆ 5.バグ報告と移植の報告 ☆ [5.1] バグかどうか? プリプロセスの Standard C 適合性を検証するための Validation Suite を MCPP といっしょに公開しています。Standard C のプリプロセスのすべての規定 を検証できるものにしたつもりです。もちろん、MCPP はこれを使ってチェック してあります。それも、上記のすべての処理系でコンパイルしてチェックしてあ ります。したがって、バグや誤仕様はほとんどないと思いますが、しかし、まだ いくつか残っている恐れもあります。ことにまだ移植されていない処理系に新し く移植した場合は、処理系のバグにひっかかる可能性があります。 もし、不可解な動作が発見されたら、ぜひご報告ください。その際には、次の 点のチェックをお願いします。 1.Standard C モードの場合、自分の Standard C 解釈に間違いはないかど うかを確かめるため、まず Validation Suite を使ってみる。GNU C / testsuite の使えるシステムでは、configure して make check で自動テストが できる。 2.自分の MCPP の移植に間違いはないかどうか、ドキュメントを確かめる。 3.バグを再現するサンプルソースを抽出する。 4.バグを引き出す部分を #pragma MCPP debug と #pragma MCPP end_debug ではさんで MCPP の動作をトレースしてみる。この をさらに 増やしてより詳細にトレースしてみる。 もし、"Bug: ..." という診断メッセージが出たら、それは間違いなく MCPP または処理系の(たぶん MCPP の)バグです。また、たとえむちゃくちゃな「ソ ース」でも、それを食わせることで MCPP が暴走するなら、それもバグです。 もちろん、Standard C モード以外のモードの MCPP は Validation Suite で は「間違い」だらけの動作をしますが、それは仕様です(それでも暴走はしない はず)。どういう仕様かは [4.1.3] を見てください。 [5.2] malloc() 関連のバグチェック 私が書いた kmmalloc という malloc() 等のライブラリがあります([4.extra] 参照)。 もし、私のこの malloc() 等をリンクした MCPP で 120 から 124(処理系に よっては 2120 から 2124)のエラー番号で途中で exit することがあれば、そ れは間違いなく MCPP または処理系の(たぶんライブラリ関数の)バグです。 また、テストに使うサンプルソースのどこかに #pragma MCPP debug memory と書いておくと、その個所および終了時にヒープメモリに関する情報が出力され ますが、ここで Heap error: ... というメッセージが出ることがあれば、それ も間違いなく MCPP または処理系のバグです。 これらのバグが発見されたら、サンプルソースの各部分を #if 0 と #endif ではさんでテストを繰り返し、バグを発生する部分を絞り込んでみてください。 malloc() 等はなるべく私の版をリンクしてテストしてください。 [5.3] バグ報告を バグ報告には次のようなデータを付けてくださるようお願いします。 1.MCPP を移植した処理系。 2.移植した方法(system.H 等の設定)。 3.バグと思われるものを再現できるサンプルソース。 4.その処理結果。 [5.4] 移植の報告を MCPP はほとんどの処理系に比較的簡単に移植できるように書いてあるつもり です。しかし、私が持っている処理系は少数です。私が持っていない処理系への 移植がうまくいくものかどうか、不安もあります。それらの処理系への移植の報 告をお待ちしています。それをソースにフィードバックして、完全なものにして いきたいと思います。 移植の報告は次のような形でお願いします。 1.処理系。 2.noconfig.H (configed.H), system.H, system.c の設定。なるべくオリジ ナルとの差分ファイルが良いが、簡単なものなら文章(メモ)でも、量が多けれ ばそのファイル丸ごとでも可。 正しく移植できたかどうかを確かめるには、まずプリプロセッサを入れ替えて、 ヘッダファイルの "pre-preprocess" の機能を使って自分自身をリコンパイルし てみるのが手っ取り早いでしょう。 さらに Standard C モードであれば Validation Suite を使います。ただ、こ れはファイルの数が多いので、デバッグを繰り返す時には手間がかかりすぎます。 デバッグ中はまず、n_std.c をコンパイルして、正常にコンパイル・実行される かどうかを見ます。処理系付属のコンパイラドライバでは MCPP に渡す方法のな いオプションもありますが、それについては mcpp-manual.txt の [2.1] を見て ください。先に MCPP を通してからコンパイルする手もあります。この2つのソ ースが正常に処理できれば、移植は成功しています。 もしこれがうまくいかない場合は、 n_std.t というサンプルを使って、どこ が悪いのか、目でチェックします。これがうまくいったら、e_std.t, m_*.t, unspcs.t, warns.t, misc.t もチェックします。"post-Standard" モードでは n_post.t, e_post.t を使います。 これらを cpp -QCz23 というオプションを付けて処理します(post-Standard モードでは -3 は不可)。STDC == 0 でコンパイルしてあれば -S1 -V199409L オプションも付けます。-C オプションでコメントも出力されるので、処理結果 が期待通りかどうかがすぐわかります。 -Q オプションで診断メッセージは mcpp.err というファイルに出力されるの で、それをページャー等で読みます。 -z オプションで、ヘッダファイルの出力は省略されます。 -2 -3 で digraph と trigraph が有効になります。-S1 -V199409L で __STDC__ が 1 に __STDC_VERSION__ が 199409L になります。 C99 対応のテストをするためには、-V199901L オプションを付けて n_std99.t, e_std99.t のチェックをします。 Validation Suite の cpp_test.c というプログラムを使うと、n_*.c, i_*.c のサンプルのテストを自動的に行うことができます(ただし、これは○×をつけ るだけで、詳細はわからない。また、e_*.?, u_*.?, unspcs.?, warns.? 等のテ ストは含まれない。MCPP 自身のテストをするためには、n_std.c をコンパイル するほうが早い)。 なお、Validation Suite は V.1.3 で GNU C の testsuite に対応しました。 したがって、MCPP を GNU C のどれかのバージョンに移植した場合は、GNU C / testsuite がインストールされていれば、GNU C のプリプロセッサを MCPP に置 き換えると、MCPP の自動テストができます。これについては cpp-test.txt [2. 2.3], mcpp-manual.txt [3.9.5]-[3.9.7] を見てください。 [5.5] GNU C 以外の処理系での configure の情報を MCPP V.2.4 以降は UNIX 系システムでは configure ができるようになりまし た。しかし、UNIX 系システムでの GNU C 以外の処理系については私はまったく 知らないので、configure ではいくつかのオプションを指定してもらわなければ なりません。 これらのオプションで指定する内容については、その処理系を使っている人は 知っているか、または調べることができるはずです。おわかりの方はぜひ教えて ください。Configure に取り込んでゆきたいと思います。 Configure については INSTALL をご覧ください。 [5.6] データを送ってくれれば移植してみます 移植がうまくいかない場合は、そのようすをお知らせください。 次のデータを付けてくれれば、移植したソースをお返しできるかもしれません。 Configure の使える環境では、これらのデータのうちのかなりの部分を configure によって知ることができます。 1. OSとそのパスリストの形式(私は UNIX 系, DOS/Windows 系, OS-9 し か知らない)。 2. 処理系の名前とバージョン。 3. 基本文字セットは ASCII か。そうでなければどういう文字セットか。 Multi-byte character(漢字)の encoding はシフト JIS か EUC-JP か、それ とも何か。Shift-JIS のように と同じコードが multi-byte character に含まれる encoding の場合、コンパイラ本体はそれを認識するか。 4. Shell(コマンドプロセッサ)は大文字と小文字を区別するか。 5. ファイル名の大文字と小文字は区別されるか。 6. 実装したい実行時オプション。コンパイラドライバから渡されるオプショ ン。プリプロセッサ単体で動かす時のオプション(getopt() で実装できないも のは不可)。 7. プリプロセッサが分離されている処理系か、それともいわゆるワンパスコ ンパイラか。 8. その処理系の事前定義マクロとその値。C++ の時はどうなるか(コンパイ ラドライバから -D オプション等でプリプロセッサに渡されるマクロと、プリプ ロセッサ自身が事前定義するマクロとを区別すること)。 9. 関数のプロトタイプ宣言ができるか。 10. unsigned long 型があるか。 11. long long, long double はどうか。long long がある場合、printf() での long long の length modifier は何か。long long がなくても同じサイズ の型があるか。 12. があるなら、CHAR_BIT, UCHAR_MAX, LONG_MAX, ULONG_MAX の値。 がなければこの4つに相当する値(1バイトが8ビットなら system.H のデフォルト値と同じはず)。 13. に FILENAME_MAX があればその値。 14. #pragma を通す処理系かどうか。#pragma 行の引数はマクロ展開の対象 となるか。 15. getenv() 関数があるか。Include directory を指定する環境変数にはど ういう名前を使うか。環境変数で複数のパスを記述する時の separator には何 を使うか。 16. 通常使う include directory。#include でヘッダファイルをサーチする 時の規則。 17. \a, \v は使えるか。 18. const 修飾子は使えるか。 19. ヘッダファイル , , , はあ るか。size_t, time_t 型の定義はどうか。 20. 必要な関数で、ライブラリに無いものがあるか。 21. コンパイラ本体で文字列リテラルの連結ができるか。 22. コンパイラ本体は digraph を認識するか。 23. 識別子に $ を使うか。 24. #asm, #endasm はあるか。これではさまれたブロックのコンパイラ本体 への受け渡し形式はどうか。その他の規格外 directive にはどんなものがある か。 25. プリプロセッサで処理すべき #pragma sub-directive には何があるか。 26. コンパイラ本体が受け取れる行長はどのくらいまでか(Validation Suite にある test-l/l_37_8.c をコンパイルするとわかる)。 27. コンパイラ本体では、識別子は何バイトまで識別されるか。 28. メモリ空間はどのくらいか。 29. コンパイル後、リンク前の「オブジェクトファイル」の接尾子は何か (UNIX 上の処理系の .o や MS-DOS 上の処理系の .obj に相当するもの)。 30. 次の t_line.c というサンプルをプリプロセッサだけに通した結果(単 体プリプロセッサを使うか、またはオプションでプリプロセス後の出力を指定す る)。これは行番号とファイル名の情報をコンパイラ本体に渡す方法を見るため のものである。 の内容は長すぎるので、途中をカットして最初の10 〜20行と最後の10〜20行があれば十分である。 さらに、#line 1000 が処理された結果が #line 1000 "t_line.c" とならず # 1000 "t_line.c" とかその他の形式になる処理系では、その部分を #line 1000 "t_line.c" と書き替えてコンパイラ本体に渡して、これを認識できるかどうか を見る(#line 1000 "t_line.c" でエラーにならなければ error line; の行で エラーメッセージが出るはずであるが、その時に行番号がどう出るか)。 /* t_line.c */ #include #line 1000 error line; main( void) { return 0; } ホスト処理系とターゲット処理系が違う場合はその双方について上記のデータ があれば、何とかなるでしょう。 こうして並べてみると、チェックすべきことがずいぶんたくさんありますね。 しかし、多くの処理系では移植ずみの処理系と共通の特性が多いでしょうから、 一応動作するだけの移植であればさほどの手間ではないはずです。比較的手間の かかるのは実行時オプションと #pragma、さらに規格外仕様の実装です。これは 一応動作するようになってから、徐々にやってゆくこともできます。唯一面倒な のは、処理系のバグにひっかかった場合です。 [5.7] 検証セットによる他の処理系のテスト報告を 私が持っている処理系のプリプロセッサを私の検証セットでテストした結果は、 cpp-test.txt [5] にまとめてあります。 その他の処理系についてテストした結果をお知らせください。項目が多いので かなりの手間ですが。 cpp_test.c によるテストであれば手間はかからないので、これだけでもお願 いします。GNU C の場合は、検証セットによる自動テストができます。 [5.8] 改善のご意見を バグ報告のほかにも、MCPP の使い勝手、診断メッセージ、MCPP のソース、 Validation Suite、私の Standard C 解釈、ドキュメントの書き方、などについ てご意見をお寄せください。 趣味で作ったプリプロセッサですが、V.2.0 までだけでも6年半もかけて凝り に凝った労作です。凝りついでにできるだけ良いものにしたいと思っています。 Cプリプロセッサについては、私の持っていない処理系への移植とテスト以外は、 やるべきこと、やって意味のあることはすべてやったつもりです。多少とも問題 が残っていれば、手を入れたいと思います。 Martin Minow のソースはとてもきれいな、クセのない、わかりやすいもので、 これを読むだけでも私にとってはずいぶん勉強になりました。改作によってスタ イルの統一性が損なわれ、きたなくなったところがあちこちにあるのが気掛かり です。 こういうものに興味を持つ人はかなり限られていると思いますが、多くのコメ ントと情報をお待ちしています。 ご意見と情報は comp.std.c, fj.comp.lang.c 等の newsgroup、またはメール でお願いします。 ☆ 6.MCPP の長い道のり ☆ [6.1] 構想3日、制作6年 1992/01 に DECUS cpp をいじりだした時には、こんな長丁場になるとは夢に も思いませんでした。正月休みにちょこっとバージョンアップしてみようと思っ ただけだったのです。 やり始めて、ソースをちゃんと読まないとダメだとわかり、2か月くらいかけ て読みました。読みがいのあるソースだったからでもあります。次にいくつかの 仕様を C90 対応にバージョンアップしました。ここまでは当初の目的の通りで した。 しかし、ここで私は自分が C90 のプリプロセス仕様を正確には知っていない ことに気付きました。P. J. Plauger & Jim Brodie "Standard C" (1989) を読 んだところ、function-like マクロの展開方法は、私の先入見をひっくり返すも のでした(ある邦訳書はここを誤訳していたが)。そこで規格書を買って、プリ プロセスに関する難解な文章をくり返し読みました。その結果、C90 のプリプロ セスは伝統的なものとは多くの点で異なっていることがわかりました。#, ## 演 算子が追加されたことは、そのほんの一部分にすぎなかったのです。 ことに function-like マクロの展開ルーチンにはかなり頭を悩ましました。E. Ream の cpp のソースを参考に2〜3週間考えて、C90 用マクロ展開ルーチンを 新しく書きました。私がプログラムのアルゴリズムでこんなに一生懸命考えたの は、後にも先にもないことです。1992/04 のことでした。 さて、これで峠を越して、今度こそ cpp いじりはおしまいだと思ったのです が、ところがそれからさらに6年あまりたってしまいました。といっても、この 間にはさほど頭を悩ます問題はなかったのです。にもかかわらず、時間はずいぶ んかかりました。考えるだけ考えたら飽きてきて、cpp いじりに集中しなくなっ たせいもあります。しかし、それだけではありません。この間にやったのは次の ようなことです。 1.仕様をさらに明確にする。Standard モードでは規格に完全に対応させる。 2.Standard C のモードを中心にプログラム構造・データ構造を再構成する。 3.Portability を上げるため、ソースのスタイルを変える。 4.デバッグをする。処理系のバグや不備に対処する。 5.テストプログラムすなわち Validation Suite を作る。 6.他の処理系のテストをする。 7.ドキュメントを書く。 8.1997/07 には新しいパソコンを買ったため、初めて使う WindowsNT/95, X Window System とそのソフトのインストールと習得に追われた。そうしているう ちに C99-1997/11 draft が出て、これへの対応が必要となった。 中でも時間のかかったのはドキュメントでした。ことに後半の4年くらいはソ ースをいじった時間はほんの少しで、ドキュメント書きが作業の大半を占めてい ました。おかげで大変な分量になってしまいましたが、しかし、時間がかかった のは量が多いせいばかりではありません。ドキュメントを書いていると、仕様の 不明確なところが次々と出てくるのです。そのたびに規格書を読み返し、ソース を少しずついじりました。ソースをいじった時間は少なくても、回数は少なくあ りません。規格書もプリプロセス規定だけではなく、全体を ANSI C の Rationale も含めてよく読んでみました。私はプリプロセッサを作ることを通し て C90 の勉強をしたようなものです。さらにはこれを通して、C90 の規定の問 題点も明確に把握することができました。 テストプログラムは初めは簡単なサンプルを何本か書いただけでした。ところ が、書いて cpp をテストするたびに意外なバグが見つかるのです。そこで、C90 プリプロセスの全規定をテストする Validation Suite を書くことにしました。 そして、Valadation Suite を書くことを通して、C90 の問題点がさらに明らか になってきました。C90 の不規則な部分に対応するのは、自分にとってはわずら わしいばかりであまり意味のないことでしたが、それよりも意味のある部分のほ うがはるかに多かったことは確かです。 この作業を通して私が学んだのは、次のようなことです。 1.プログラムの仕様は、詳細なドキュメントを書き終えるまで確定しない。 2.プログラムのデバッグは、全仕様をテストするサンプルが完成するまで終 わらない。 この考え方は完全主義的なものです。世の中のことは完全主義ではうまくゆか ないものが多く、プログラムも例外ではありませんが、中には完全主義が重要な 意味を持つ分野もあります。言語処理系はその一つでしょう。 趣味だから何年もかけて徹底的にやることができたとも言えます。それにして も6年半は長すぎました。こんなに時間をかけて完全なプログラムを作って、い ったいだれが使うのだろうという疑問がずっと続いていました。趣味で作るプロ グラムとしては、このくらいが規模の限度なのでしょう。今後は、趣味で大掛か りなことはしないようにしようと思っています。 しかし、MCPP はもう作ってしまったので、今後もメンテナンスをしていくつ もりです。せっかくですから皆さん、コメント、報告、移植をお願いします。 [6.2] V.2.3 へ V.2.0 を公開した後、さらに V.2.1, V.2.2, V.2.3 と update を繰り返して きています。C99 や正式に承認された ISO / C++ に対応させたり、対応処理系 を増やしたり、バグをとったりというのがその内容です。 V.2.2 までは簡単に update できていました。V.2.2 は V.2.0 から3か月し かたっていません。ところが、V.2.3 は V.2.2 から4年あまりもたってしまい ました。私の身辺が多忙になり、時間がとれなくなったのが主な原因です。2000 /07 に 60 歳になって、仕事を週4日に減らしてから、いくらか時間がとれるよ うになり、cpp いじりに復帰しました。 V.2.3 は時間だけでなく、手間も比較的かかっています。GNU C V.2.9x に実 装してみたところ、GNU C / cpp との互換性確保のためにかなり手を加えなけれ ばならないことがわかったからです。オプションをいくつか追加し、拡張仕様を 実装しました。また、一部のエラーをウォーニングに格下げしたり、頻発するウ ォーニングをデフォルトのウォーニングクラスからはずしたりして、規格による 制限を緩和しています。 こうした変更の多くは後向きのものであり、楽しいものではありませんでした。 ことに C90 以前の "traditional" な仕様の一部を C99 の仕様と両立させなけ ればならないというのは、はなはだ不本意なことでした。しかし、これが現在の オープンソース界の実情であれば、それにある程度合わせるのはやむをえません。 規格による制約を緩和したことで、他の処理系用の版も、処理系付属のプリプ ロセッサと置き換えて使うためには使いやすくなったと思います。 [6.3] 「未踏ソフトウェア創造事業」に採択 V.2.3 への update の途中で、MCPP および Validation Suite は情報処理振 興事業協会 (IPA) の平成14年度「未踏ソフトウェア創造事業」というものに 採択されました。たまたまこの事業のことを知ったので応募してみたところ、新 部 裕・プロジェクトマネージャが採択してくれたのです。こうして 2002/07 か ら 2003/02 までは IPA の資金援助と新部PMの助言のもとに、開発が進められ ることになりました。ドキュメントの英訳も、ハイウェルが引き受けてくれるこ とになりました 比較的小さいソフトウェアながらも、これだけ時間をかけ、私のライフワーク のようになってしまったものです。その完成度には自信がありましたが、世に出 る機会がなく、残念な思いをしてきました。その機会がついに与えられたのです。 私はこのプロジェクトを遂行するため、仕事を週3日に減らしました。 私がこのプロジェクトでやることとして当初考えていたのは、次のようなこと でした。 1、英語版のドキュメントを作成する。それを使って、MCPP と検証セットを 国際的な評価の場に出してゆく。C処理系の大半がアメリカ製となっている現状 では、英語版ドキュメントの存在は評価と普及のために必須だからである。 2、これまではフリーの処理系を評価と移植の主な対象としてきたが、今後は 市販の主要な処理系の検証セットによる評価と、MCPP のそれらへの移植を進め る。 3、これまで対応してきた処理系についても、より新しいバージョンに対応さ せる。 しかし、MCPP は単なるCプリプロセッサでありC処理系の一部にすぎず、 「未踏ソフトウェア」としては目玉に欠けるきらいがあるため、新部 PM から次 のような助言がありました。 1、GNU C 3.x に対応させる。 2、GNU C 3.x の testsuite で私の検証セットが利用できるようにする。 3、開発をオープンな形で進めてゆく。 私自身もこれらはぜひやりたいことであるので、喜んで計画に追加しました。 ところが、私の計画は遅延に遅延を重ねました。まず、ディスククラッシュに 見舞われました。また、何か新しいことをするたびに使ったことのないソフトウ ェアを使って、そのたびに時間がかかりました。GNU C をソースからコンパイル したのも初めてですが、これはいくつかのトラブルに見舞われました。大量のド キュメントの更新と大量の英訳のチェックと修正にも、かなりの時間がかかりま した。その上、母の入院という事態まで発生しました。プロジェクトは市販の処 理系への対応等、計画の一部を断念する結果となりました。 私はこれまで一つの穴を深く掘ってゆくようなことしかしてこなかったので、 穴を少し広げようとするとひどく手間がかかってしまうのです。アマチュアプロ グラマが何かを掘り下げるには、こういうやり方をしなければできることではあ りません。しかし、その成果を世に出すためには、穴をいくらか広げなければな らないのでした。 穴を広げる過程で、私は新部 PM の助言と励ましを得て、いくつかの未経験の ソフトウェアを習得し、開発の前線というものに触れることができました。自分 の文章がこなれた英文に翻訳されて戻ってくるのも、大変うれしいことでした。 時間に追われるのは苦しいことですが、内容はどれも新鮮で楽しいことでした。 「未踏ソフトウェア創造事業」はこれでおしまいではありませんでした。平成 15年度にも、伊知地 宏・プロジェクトマネージャが MCPP を継続プロジェク トとして採択してくれたのです。こうして、前年度の積み残しの課題を初めとし て、私にとっては未経験の領域のいくつかの課題に取り組むこととなりました。 今回も私の6年前のパソコンにトラブルが発生し、ハードウェアと OS を upgrade する過程でさらにいくつかのトラブルに見舞われました。未経験のソフ トウェアの習得にも時間を要し、開発はやはり遅れ気味でした。いったん退院し て比較的元気になっていた母の容態が、プロジェクトが大詰めに近づくのと並行 して以前にも増して悪くなってきたことも、心配の種でした(*)。しかし、伊知 地PMが目標を無理のないところに設定してくれたおかげで、あわてずにじっく りと課題に取り組むことができました。 Visual C++ への移植、Plan9 への移植、configure スクリプトの作成、ISO- 2022-JP, UTF-8 を含む多様な multi-byte character encoding への対応、等の 課題を達成することができました。また、ソースコードの整理という目立たない ながらも作者としてはこだわりのある課題にも取り組みました。日本語版と英語 版のドキュメントの更新という手間のかかる作業も、ハイウェルの協力を得て達 成することができました。 この成果によって、私は伊知地PMから何と「スーパークリエータ」という評 価を受けることができました。私の実力にとっては過分の評価ですが、長年にわ たる MCPP の積み重ねを認めていただいたものと思い、大変喜んでいます。 2年近くにわたる「未踏ソフトウェア」のプロジェクトによって、MCPP は世 界一高品質な C/C++ プリプロセッサに仕上がったつもりです。熟年のアマチュ アプログラマとして非力ながらもよくやったと自分では納得しています。 未踏ソフトウェアのプロジェクトが終わってからも、MCPP の改良作業は続け られています。まだいくつもの課題が残っています。これらの課題を達成し、 MCPP を普及させるために、今後も着実に取り組みを続けてゆくつもりです。 * 母は 2004 年 2 月に死去した。 [eof]