mcpp は Martin Minow の DECUS cpp を元に kmatsui(松井 潔)が全面的に書き直したCプリプロセッサです。mcpp という名前は Matsui cpp という意味です。これはソースで提供するもので、各処理系で使うには、その処理系に合わせてソースに若干の変更を加えた上でコンパイルして、mcpp の実行プログラムを作る必要があります。*1
このドキュメントはソースを各処理系に移植する方法を説明しています。できあがった実行プログラムの動作仕様については、mcpp-manual.html というマニュアルを参照してください。
これらのソース、ドキュメントはすべてオープンソース・ソフトウェアとして提供します。
mcpp は次のような特徴を持っています。
注:
*1 mcpp V.2.6.3 からはコンパイルずみの何種類かのバイナリ・パッケージも次のサイトで提供するようにした。しかし、このドキュメントではそれには触れない。バイナリ・パッケージについてはこの web page を参照のこと。
Linux, FreeBSD, Windows 等の多くのOSをサポートしている portable なプリプロセッサであり、そのソースは Standard C (ANSI/ISO/JIS C) の処理系でさえあればコンパイルできる広い portability を持っています。ライブラリ関数は古典的なものしか使っていません。
各処理系に移植するためには、多くの場合、ヘッダファイル中のいくつかのマクロ定義を書き替えてコンパイルするだけですみます。最悪の場合でもソースファイルに数十行書き足す程度です。
Multi-byte character(漢字)の処理は日本の EUC-JP, shift-JIS, ISO2022-JP、中国の GB-2312、台湾の Big-5、韓国の KSC-5601 (KSX 1001) に対応しており、UTF-8 も使えます。Shift-JIS, ISO2022-JP, Big-5 の場合、コンパイラ本体が漢字を認識しない処理系では、mcpp がそれを補います。
Standard C 準拠の動作モードのほかに、K&R 1st. のモードや "Reiser" model cpp のモードもあり、さらには自称 post-Standard 仕様のモードまであります。C++ のプリプロセッサとして動作する実行時オプションもあります。
Standard C モードは既存の多くのプリプロセッサと違って、規格を完全に実装しているつもりです。C90, C95, C99, C++98 のすべてに対応しています。Standard C プリプロセスの reference model となるものを目指して作ってあります。これらの規格のバージョンは実行時オプションで指定することができます。*1
ほかにいくつかの有用な拡張機能も持っています。マクロの展開機序や #if 式の評価機序をトレースする #pragma MCPP debug もあります。ヘッダファイルを "pre-preprocess" しておくこともできます。
いくつかの有用な実行時オプションも備えています。ウォーニングのレベルを指定するオプションや、include directory を指定するオプション等です。
ソースにどんな間違いがあっても mcpp は暴走したり見当外れなメッセージを出したりせず、正確でわかりやすい診断メッセージを出して適切な処理をします。移植上で問題となる点についても警告を発します。
高品質でありながら、コードサイズは比較的小さく、メモリ消費も比較的少なくてすみます。
詳細なドキュメントも付属しています。
mcpp の欠点を強いて挙げれば、速度がやや遅いことです。GCC 3.*, 4.* / cc1 に比べると2倍から3倍の時間がかかります。しかし、Borland C 5.5 / cpp と同じくらいの速度で、ヘッダファイルの pre-preprocess の機能を使うともう少し速くなるので、特に遅いほうではありません。正確であること、portable なソースであること、少ないメモリでも動作すること等のためには、この程度の処理時間はやむをえないと考えています。
なお、プリプロセッサの Standard C 準拠度をテストするための検証セットである "Validation Suite for Standard C Preprocessing"、その解説およびそれを使ってテストした各種プリプロセッサの採点簿 cpp-test.html を mcpp とともに公開しています。これを見ると、「Standard C 準拠」と称する既存のプリプロセッサにいかに多くの問題があるかがわかります。*2
注:
*1 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 と呼ぶことがある。C++ の規格は ISO/IEC 14882:1998 およびその正誤訂正版である ISO/IEC 14882:2003 で、この両者をここでは C++98 と呼ぶ。
*2 この 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.6.2 からは mcpp-porting.html と変更した。私自身の名前も、V.2.2 までは Psycho としていたが、V.2.3 からは kmatsui に変更した。
このドキュメントはかつてはテキストファイルでしたが、V.2.6.2 からは html ファイルに変わりました。
このドキュメントでは次のようにフォントを使い分けています。
注:
*1 「未踏ソフトウェア創造事業」(Exploratory Software Project) の概要は次のところで知ることができる。
mcpp V.2.3 から V.2.5 までは次のところに置いてきたが、
2006/04 に次のところに移った。
cpp V.2.2 はベクター社のサイトの次のところにある。dos/prog/c というディレクトリに入れられているが、MS-DOS 専用ではない。ソースは UNIX, WIN32/MS-DOS 等に対応している。
http://www.vector.co.jp/soft/dos/prog/se081188.html
http://www.vector.co.jp/soft/dos/prog/se081189.html
http://www.vector.co.jp/soft/dos/prog/se081186.html
これらのアーカイブファイル中のテキストファイルは、Vector のものは DOS/Windows 系に合わせて、改行コードは [CR]+[LF]、漢字は shift-JIS で encode してある。SourceForge のものは V.2.5 までは UNIX 系に合わせて改行コードは [LF]、漢字は EUC-JP である。V.2.6 からは [CR]+[LF] / shift-JIS の zip 版と [LF] / EUC-JP の tar.gz 版の2種類のアーカイブファイルを置くようにした。
mcpp のソースは5本のヘッダファイルと7本の *.c ファイルからなっています。OSや処理系に依存する部分は configed.H, noconfig.H, system.H, system.c の4本のソースにまとめてあります。configed.H と noconfig.H とは同時に使われることはなく、必ずどちらか一方が使われます。また、ライブラリ関数の一部のCによるソースが system.c にあります。したがって、mcpp を何らかの処理系で使うには、それに合わせてこれらのソースファイルに変更を加える必要があります。
mcpp の実行プログラムは build する方法に応じて何種類かあります。 Build する方法には次の2つの次元があります。
以下の 3.1 - 3.9 ではこの stand-alone の compiler-specific-build を説明します。「GCC 版」「Visual C 用」等と表記しているのはすべて、それぞれ GCC-specific-build, Visual C-specific-build のことです。
mcpp をコンパイルするには2つの方法があります。1つは configure スクリプトを実行して、config.h というヘッダファイルと Makefile を自動生成する方法です。あとは単に make; make install とするだけですみます。configed.H というヘッダファイルはこの場合に使われます。しかし、configure は UNIX 系のシステムと CygWIN, MinGW でしか使えません。
もう1つは各処理系用の差分ファイルを使ってヘッダファイルに変更を加え、必要ならさらにヘッダファイルを編集した上で、その処理系専用の makefile を使って make する方法です。noconfig.H というヘッダファイルはこの場合に使われます。差分ファイルと makefile は noconfig というディレクトリにあります。Configure の使えるシステムでも、ヘッダファイルを直接、編集することで細かい制御をすることができます。しかし、差分ファイルはすでに移植ずみの処理系用のものしかありません。
この章では差分ファイルを使う方法について説明します。Configure については INSTALL を見てください。
注:
*1 これを V.2.6, V.2.6.1 では stand-alone-build と呼んでいた。 V.2.6.2 で subroutine-build ができたのに伴って、呼称を変更した。
*2 mcpp V.2.6.3 からは mcpp.sourceforge.net で何種類かのバイナリ・パッケージが提供されるようになったが、これはすべて stand-alone の compiler-independent-build である。
私自身が動かすことのできるC処理系は次のもので、このいずれにも mcpp を移植してあります。すなわち、このソースをコンパイルでき、生成されたプリプロセッサがそれぞれの処理系の上で正しく動作することを確認しています。いずれも CPU は x86 系を使っています。Ubuntu だけが 64 ビット版で、他は 32 ビット版です。
FreeBSD 6.3 GCC V.3.4.6 Vine Linux 4.2 GCC V.2.95.3, V.3.2, V.3.3.6, V.3.4.3, V.4.1.1 Fedora Linux 9 GCC V.4.3.0 Debian LInux 4.0 GCC V.4.1.2 Ubuntu Linux 8.04 / x86_64 GCC V.4.2.3 Mac OS 10.5 GCC V.4.0.1 CygWIN 1.3.10 GCC V.2.95.3 CygWIN 1.5.18 GCC V.3.4.4 MinGW (MSYS 1.0.11) GCC V.3.4.5 WIN32 Visual C++ 2003, 2005, 2008 WIN32 Borland C++ V.5.5J WIN32 LCC-Win32 2003-08, 2006-03
また、他のユーザから提供された Visual C++ V.6.0, Visual C++ 2002, C++Builder 2007 (aka BCC V.5.9) の情報もあり、それらの処理系でコンパイルすることもできます。
これらの処理系で mcpp をコンパイルするための修正は簡単で、noconfig.H の数個のマクロ定義を変更するだけです。
noconfig ディレクトリの *.dif というファイルは FreeBSD 6.* / GCC 3.4 用の noconfig.H を各処理系用に修正する差分ファイルです。Visual C++ 2005 を例にとると、src ディレクトリで
patch -c < ..\noconfig\vc2005.dif
とすると、修正されます。patch は UNIX の標準的なコマンドで、Windows 等にも移植されています。patch を使わなくても、差分ファイルを見てエディタで修正してもかまいません。
Include ディレクトリの指定などは、差分ファイルによる修正とは別に、ユーザが自分のシステムに合わせて修正しなければなりません。
こうして修正したソースをコンパイルするための各処理系用の makefile も添付してあります(3.7 参照)。
copy ..\noconfig\visualc.mak Makefile
として src ディレクトリにコピーします。
以下の作業も src ディレクトリで行います。作業は特に断らない限り、noconfig.H の修正です。
以下のどの処理系でも、compiler-specific-build を作るためには、
#define COMPILER INDEPENDENT
となっている行を
#define COMPILER MSC
等と、その処理系を表すマクロに変更します。そして、
#define VERSION_MSG "GCC 3.4"
という行を次のように適宜書き換えます。
#define VERSION_MSG "Visual C 2005"
COMPILER の定義は make のオプションで上書きすることもできます。 例えば、
nmake COMPILER=MSC nmake COMPILER=MSC install
等とします。差分ファイルで noconfig.H を書き換えた場合は、compiler-specific-build のための設定もその処理系用に書き換えられるので、COMPILER は書き換える必要はありません。make で COMPILER を指定すると compiler-specific-build が生成され、指定しないと compiler-independent-build が生成されます。
また、デフォルトの include directory の設定が noconfig.H のものと異なる場合は、それを C_INCLUDE_DIR1, C_INCLUDE_DIR2 というマクロに書いておきます。C と異なる C++ 固有の include directory がある場合は、それを CPLUS_INCLUDE_DIR1, CPLUS_INCLUDE_DIR2, CPLUS_INCLUDE_DIR3 に書きます(これらのディレクトリは実行時に環境変数や -I オプションで指定することもできる)。noconfig.H で設定するのは処理系固有の include directory です。
Include directory はこのほか、system.c でも設定されています。UNIX で言えば system.c で設定されるのはいわゆる OS-specific なもの(通常は /usr/include)といわゆる site-specific なもの(通常は /usr/local/include)です。Windows では system.c では include directory は何も設定されません。Windows では noconfig.H でもデフォルトでは include directory は設定されないので、自分で書くか、または環境変数 INCLUDE, CPLUS_INCLUDE で指定する必要があります。
また、必要なら COMPILER_STD1, COMPILER_STD2 等で定義される組み込みマクロ名も変更します。
Multi-byte character の encoding はデフォルトでは、UNIX 系では EUC-JP、Windows では shift-JIS としていますが、必要なら MBCHAR というマクロを書き換えて他の encoding に変更します(Multi-byte character encoding は実行時に環境変数・オプション・#pragma で変更することもできる)。
処理系によっては shift-JIS や Big5 等の encoding に対応していないため、multi-byte character の中に '\\' と同じ 0x5c の値のバイトがあると tokenization でエラーになることがありますが、そういう処理系では mcpp は特殊な処理をしてコンパイラの欠陥を補います。この設定については 4.1.1.5 を見てください。
添付の makefile については、BINDIR という変数で処理系のバイナリの置かれているディレクトリを書きます。
GCC V.3, V.4 ではプリプロセスがコンパイラ (cc1, cc1plus) に吸収されてしまったので、GCC-specific-build の mcpp を使うには、gcc, g++ の呼び出しを shell-script に置き換えて、mcpp => cc1, mcpp => cc1plus の順序で実行されるようにしなければなりません。添付の makefile では、
make COMPILER=GNUC make COMPILER=GNUC install
とすると、これが自動的に設定されます。詳細は mcpp-manual.html#3.9.7 を見てください。
ユーザが BINDIR 等への書き込み権限を持っていない場合は、UNIX 系では sudo make COMPILER=GNUC install とします。Windows ではあらかじめ administrator アカウントで、そのディレクトリの permission を変更しておきます。
ソースは FreeBSD 6.* 上の GCC (GNU C) V.3.4.* でコンパイルして compiler-independent-build の mcpp を生成する状態になっています。FreeBSD 6.* / GCC V.3.4.* 用の compiler-specific-build を作るには、
#define COMPILER INDEPENDENT
となっている行を
#define COMPILER GNUC
として、コンパイルすればできあがりです。 COMPLIER は make COMPILER=GNUC で上書きすることもできます。
GCC の他のバージョンであれば、VERSION_MSG というマクロおよび
#define COMPILER_EXT_VAL "3" #define COMPILER_EXT2_VAL "4" #define COMPILER_CPLUS_VAL "3" #define GCC_MAJOR_VERSION 3
となっているところのバージョン番号を変更します。 COMPILER_EXT_VAL は GCC の major version number を、COMPILER_EXT2_VAL は minor version number をいずれも文字列リテラルで書きます。COMPILER_CPLUS_VAL は __GNUG__ マクロの値で、COMPILER_EXT_VAL と同じになります。 また、GCC_MAJOR_VERSION は COMPILER_EXT_VAL と同じ値を数値で書きます。
FreeBSD のバージョンが 6.* でなければ、
#define SYSTEM_EXT_VAL "6" /* V.5.*: 5, V.6.*:6 */
の値を変更します。
さらに include directory が FreeBSD 6.* の標準と違っている場合は、
#define CPLUS_INCLUDE_DIR1 "/usr/include/c++/3.4" #define CPLUS_INCLUDE_DIR2 "/usr/include/c++/3.4/backward"
となっているディレクトリを変更します。CPLUS_INCLUDE_DIR3, C_INCLUDE_DIR1 の設定も必要かもしれません。
GCC V.2.7-2.95 であれば次のマクロの定義を 199409L に変更します。
#define STDC_VERSION 0L
他の UNIX 系 OS でもコンパイラが GCC であれば、このバージョン表示や、include ディレクトリの設定、OS固有の組み込みマクロの設定、等を変えるだけですむかもしれません(4.1.1 参照)。
Linux / GCC ではまず、
#define SYSTEM SYS_FREEBSD
を
#define SYSTEM SYS_LINUX
に変更します。 そして、FreeBSD の場合と同じように、COMPILER, VERSION_MSG, COMPILER_EXT_VAL, COMPILER_EXT2_VAL, COMPILER_CPLUS_VAL, GCC_MAJOR_VERSION CPLUS_INCLUDE_DIR1, CPLUS_INCLUDE_DIR2, C_INCLUDE_DIR1 等のマクロの値を変更します。
GCC 2.* では STDC_VERSION の値を変更し、さらに、
#define COMPILER_SP3_VAL "int"
を
#define COMPILER_SP3_VAL "long int"
に変更します。
include directory は
gcc -xc -E -v /dev/null g++ -xc++ -E -v /dev/null
として確かめてから設定してください。
noconfig ディレクトリの linux_gcc2953.dif, linux_gcc32.dif, linux_gcc336.dif, linux_gcc343.dif は FreeBSD 6.* / GCC V.3.4 用のソースを VineLinux 4.0 / GCC V.2.95.3, V.3.2, V.3.3.6, V.3.4.3 用に修正する差分ファイルです。 また、linux_gcc412.dif は Debian 4.0 / GCC V.4.1.2 用に修正するものです。 それぞれ compiler-specific-build ではさらに COMPILER を変更します。Distribution の標準の GCC と追加インストールした GCC とで include directory がかなり異なる点に注意してください。
なお、glibc の getopt() は POSIX 等の標準のものとは仕様が異なるので、使わずに、system.c の mcpp_getopt() を使ってください。
Mac OS X では Xcode というパッケージをインストールすると GCC もインストールされます。 /usr/bin に多くの gcc, cc, g++, c++ 等々が現れますが、Intel-Mac の Mac OS X 10.5 (Leopard) の場合は、i686-apple-darwin9-gcc-4.0.1, i686-apple-darwin9-g++-4.0.1 というのがそのマシン用の native code を生成するコンパイラです。 同時にクロスコンパイラもインストールされます。 Intel-Mac の場合は、powerpc-apple-darwin9-gcc-4.0.1, powerpc-apple-darwin9-g++-4.0.1 というのが powerpc 用のバイナリを生成するクロスコンパイラです。 単なる gcc (g++) は gcc-4.0 (g++-4.0) へのリンクで、これはデフォルトでは native compiler として動作しますが、-arch ppc というオプションを付けて起動すると powerpc 用のコンパイラ本体が呼び出されます。 また、/Developer/usr/bin にも /usr/bin と同じコンパイラ一式がインストールされます。
これらはいずれも Apple によって多くの拡張を施された Mac OS X 専用の GCC です。 他のシステムの GCC と異なるのは、1つは framework と呼ばれる独特なディレクトリを system header directory としていることです。 もう1つは Intel-Mac と PowerPC-Mac の双方のバイナリを片方の(1台の)マシン上で生成できるようになっていることです。 さらに双方のバイナリを1つにバンドルしてどちらのマシンでも動くようにした universal binary というものを生成するしくみもあります。 実は gcc-4.0, i686-apple-darwin9-gcc-4.0.1, powerpc-apple-darwin9-gcc-4.0.1 等や、そこから呼び出されるコンパイラ本体である /usr/libexec/gcc/SYSTEM/4.0.1 の cc1, cc1plus 等々もすべて i386 用と ppc 用との universal binary なのです(SYSTEM は i686-apple-darwin9 または powerpc-apple-darwin9)。 これらをそのまま x86 マシンから powerpc マシンに持っていけば、native と cross の関係が逆転した状態で動くようになっているものと思われます。 その上、Intel-Mac は ppc 用のバイナリを x86 のコードに変換しながら自動的に動かすようになっています。
こういうことで、/usr/bin と /Developer/usr/bin に多くの gcc, g++ があり、それらへのリンクがあり、x86 用と ppc 用の libexec ディレクトリがあり、1つの実行プログラムに2つのバイナリがバンドルされており、ppc 用のバイナリが x86 で動き、拡張機能があり、非常に紛らわしいので注意してください。 ここでは Intel-Mac 上の Mac OS X 10.5 (Leopard) を例にとりますが、ppc-Mac では i686 と powerpc (ppc) を入れ替えて読んでください。 また、Mac OS X 10.4 (Tiger) では darwin9 を darwin8 と読み替えてください。
ネイティブコンパイラ用の mcpp のインストールは簡単です。 noconfig.H に mac_gcc401_i686.dif の変更を加えると、Intel-Mac 上の Mac OS X 10.5 / GCC 4.0.1 用の設定になります。 Makefile としては mac_osx.mak を使います。 make; sudo make install とすると compiler-independent-build が、make COMPILER=GNUC; sudo make COMPILER=GNUC install とすると GCC-specific-build ができます。 /Developer/usr/bin には通常は PATH は通っていないので、コンパイラは /usr/bin のものが使われます。
Intel-Mac 上で powerpc 用のクロスコンパイラにインストールするには、まず noconfig.H に mac_gcc401_powerpc.dif の変更を加えます。 さらに Makefile を修正します。
compiler-independent-build では NAME, CC, CXX という変数の定義を、いずれも mac_osx.mak 中のコメントにあるように powerpc を含む文字列に変更します。 そして make; sudo make install とします。 これは「クロスコンパイラで」コンパイルされたもので、ppc-Mac 上で動くはずです。
GCC-specific-build では NAME, INCDIR, BINDIR, target_cc, arch を、やはり powerpc (ppc) を含む名前に変更します(CC, CXX は変更しない)。 そして、make COMPILER=GNUC; sudo make COMPILER=GNUC install とします。 これは Intel-Mac 上で動く「クロスコンパイラのための」ものなので、Intel-Mac 上で動きます。
ppcMac 上では、Intel-Mac とは逆に、mac_gcc401_powerpc.dif でネイティブコンパイラ用の設定になり、mac_gcc401_i686.dif でクロスコンパイラ用の設定になるはずです。 クロスコンパイラ用では、上記の Intel-Mac の Makefile の設定で変数に powerpc を含む名前を使ったところを、すべて i686 に変更します。
Universal binary を作るには、mac_osx.mak の UFLAGS という変数定義をコメントアウトしている # を外して、これを有効にするだけです。 あとは前節の設定のままです。
CygWIN V.1.3.10 / GCC V.2.95.3 では noconfig.H に cyg1310.dif にあるような変更を加えます。
CygWIN V.1.5.18 / GCC V.3.4.4 では cyg1518.dif を使います。
さらに CYGWIN_ROOT_DIRECTORY というマクロを自分の環境に合わせて修正します。これは CygWIN の存在する Windows 上のディレクトリを次の形式で定義するものです。
#define CYGWIN_ROOT_DIRECTORY "c:/pub/compilers/cygwin"
path-list 中の大文字・小文字は関係ありません。
他の version でも、VERSION_MSG, CYGWIN_ROOT_DIRECTORY、および include directory のマクロを変更することで対応できるでしょう。
CygWIN は Windows 上のシステムですが、UNIX のファイルシステムがシミュレートされているので、mcpp では UNIX 系システムの GCC とほぼ同様に扱います。Include directories も UNIX 系と同様に組み込まれます。
MinGW / GCC V.3.4.5 では noconfig.H に mingw345.dif のような変更を加えます。
さらに MSYS_ROOT_DIRECTORY, MINGW_DIRECTORY という2つのマクロを自分の環境に合わせて修正します。これは次のように、それぞれ /, /mingw ディレクトリの Windows 上の位置に定義するものです。
#define MSYS_ROOT_DIRECTORY "C:/Program Files/MSYS/1.0" #define MINGW_DIRECTORY "C:/Program Files/MinGW"
path-list 中の大文字・小文字は関係ありません。
他の version でも、これらのマクロと VERSION_MSG、および include directory のマクロを変更することで対応できるでしょう。Include directory のマクロ定義は "C:/dir/mingw/include" という絶対パスでも、"/mingw/include" という MinGW 内のディレクトリでもかまいません。
MinGW では symbolic link がサポートされていないので、gcc から GCC-specific-build の mcpp を起動するのに symbolic link が使えません。その上、MinGW / gcc はたとえ cc1 という名前でも shell-script の起動は拒否します。そこで、mcpp のコンパイルでは cc1.exe という実行プログラムを生成して、この中から mcpp.exe または GCC の cc1.exe, cc1plus.exe を呼び出します。
MinGW の GCC-specific-build では include directories は mcpp が設定しますが、compiler-independent-build では設定されないので、環境変数 INCLUDE, CPLUS_INCLUDE で指定する必要があります。
LCC-WIN32 2003-08-*, 2006-03-* ではそれぞれ lcc0308.dif, lcc0603.dif のような変更を加えます。
他の version では VERSION_MSG マクロを変更します。
Visual C++ 2008, 2005, 2003, 2002, 6.0 ではそれぞれ vc2008.dif, vc2005.dif, vc2003.dif, vc2002.dif, vc6.dif のような変更を加えます。もちろん、compiler-specific-build では COMPILER マクロを書き換えるか、nmake -DCOMPILER=MSC オプションで上書きします。
Visual C の他のバージョンでは、VERSION_MSG マクロを変更するほか、_MSC_VER および _MSC_FULL_VER という組み込みマクロの値をそれぞれ COMPILER_EXT_VAL, COMPILER_EXT2_VAL というマクロの設定を変えることで対応させます。
Borland C V.5.5, V.5.9 (C++Builder 2007) / bcc32 ではそれぞれ bc55.dif, bc59.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 の実装されているバージョンであれば、HAVE_DIGRAPHS の設定を変更します。__STDC_VERSION__ の実装されているバージョンであれば、STDC_VERSION の設定を変更します。
PDP-11 上の RT-11 / DECUS C, RSX / DECUS C、VAX 上の VMS / VAX-11 C、PDP-11 / UNIX, VAX / ULTRIX の何かのCには DECUS cpp が対応していたようです。MS-DOS 上の Microsoft C, Lattice C のかなり古い版にも対応していたようです。これらはさすがにもう不要と思われ、また私自身がメンテナンスできないので、削除しました。
system.H は HAVE_CONFIG_H というマクロが non-0 に定義されていると configed.H を include し、そうでなければ noconfig.H を include します。configed.H, noconfig.H には mcpp の設定の PART 1 と PART 2 という部分があり、system.H には PART 3 があります。
これらのファイルには、各処理系に移植する時に必要ないくつかのマクロが定義されています。まだ移植されていない処理系に移植するには、PART 1 に数行ないし十数行を書き足します。
PART 1 はOSと target 処理系に依存する定義で、PART 2 は host 処理系に依存する定義、そして PART 3 は mcpp の動作仕様の定義です。
configed.H, noconfig.H ではターゲット処理系とホスト処理系とが同じであると仮定していますが、異なる場合は PART 2 を編集する必要があります。
デフォルトの設定と違う設定で移植する場合は、これらのファイルの全体に必ず目を通してください。
configed.H (noconfig.H), system.H のマクロだけでは吸収できないOSや処理系の差異は、system.c で吸収しています。未実装の処理系に移植するには、ここに数十行のソースを書き足すことが必要になるでしょう。
このファイルに記述されているのは、mcpp 起動時のオプション、usage 文、include ディレクトリ、ヘッダファイルやソースファイルをオープンする時のOS固有のディレクトリパスの扱い、#pragma の処理、処理系固有の拡張ディレクティブの処理、等です。ほとんどは target OS と target 処理系の設定です。
ライブラリ関数のうち、Standard C にない getopt(), stpcpy() のCによるソースも system.c に書いてあります。mcpp は getcwd(), stat() も使い、UNIX 系では readlink() も使いますが、この3つは OS に依存する関数であり portable に書くことができないので、ここには含めていません。この3つは Standard C にはありませんが、POSIX では規定されています。mcpp の使う低水準関数はこの3つだけです。これらを持たない処理系はないでしょう。
このほか、MSC では stricmp() を、Mac OS X, CygWIN, MinGW では strcasecmp() も使います。*1, *2
ライブラリ関数はいずれも、処理系によって微妙に違う恐れのある仕様に依存した使い方はしていないので、どの処理系のものでもバグさえなければ大丈夫です。
注:
*1 MinGW 版に限って spawnv() も使われる。
*2 mcpp V.2.6.4 までは lib.c というソースファイルを独立させていたが、その内容が getopt(), stpcpy() の2つだけになったので、V.2.7 から system.c に吸収した。 そして、getopt() は mcpp_getopt() と名前を変えて、リンクのトラブルを解消した。
mcpp のソースでは stdio.h, ctype.h, errno.h, stdlib.h, string.h, stddef.h, limits.h, time.h, sys/types.h, sys/stat.h を無条件で include しています。UNIX 系のシステムでは unistd.h も include します。これらを持たない処理系はまずないでしょう。
noconfig ディレクトリにある *.mak は個別の処理系用の makefile です。詳細な設定ができます。make そのものは各処理系に付属のもの、またはそのシステムの標準的なものを想定しています。Visual C では make ではなく nmake を使います。
まず、処理系を xyz とすると、FreeBSD / GCC 以外では
patch -c < ../noconfig/xyz.dif
として noconfig.H を修正します。次に、noconfig.H の COMPILER と VERSION_MSG というマクロを書き換えます。さらに自分のシステムに合わせて noconfig.H の C_INCLUDE_DIR? 等のマクロを修正します。そして、使用する noconfig/xyz.mak を Makefile にコピーし、ディレクトリ指定等を自分のシステムに合わせて修正した上で、
make make install make clean
としてください。
他の処理系では、これらを参考に必要な makefile を書いてください。ソースの依存関係は単純で、
という関係になっています。 system.H は internal.H より先に include する必要があります。 internal.H はさらに mcpp_lib.h を include します。
mcpp 自身を使って mcpp をリコンパイルするには、処理系のプリプロセッサのある場所にこの実行プログラムをおきます。例えば GCC V.2.95 であれば、処理系付属の cpp0 を cpp0_gnuc とでも rename しておき、その時に使うものを cpp0 にリンクするのが良いでしょう。すなわち、使うプリプロセッサを mcpp とすると、
ln -sf mcpp cpp0
とします。Windows では使うものを cpp32.exe 等にコピーします。*1
mcpp 実行プログラムの名前は
make NAME=mcpp
等として指定することができます(同じことを BC make では make -DNAME=mcpp とする。UCB make では -D は付けても付けなくても良い。GNU make では -D は付けてはいけない)。
添付の makefile では GCC 用(freebsd.mak, linux.mak, mac_osx.mak, cygwin.mak, mingw.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 を修正する必要があります。
注:
*1 FreeBSD では cpp0, cc1 を置く標準のディレクトリは /usr/libexec である。Linux では /usr/lib/gcc-lib/i686-redhat-linux/3.3.2 といったひどく奥深いディレクトリになっている。Linux / GCC では distribution やその version に応じて makefile のこのディレクトリの指定を書き換える必要がある。Include directory もいろいろあるので、確かめなければならない。
また、Linux や FreeBSD では /usr/bin/cpp というものがあるが、これは実際には cpp0 または cc1 を呼び出す。gcc も cpp0 または cc1 を呼び出す。
なお、mcpp-manual.html#3.9.5, mcpp-manual.html#3.9.7 も参照のこと。GCC 3.*, 4.* ではプリプロセスがコンパイラ (cc1, cc1plus) に吸収されてしまったので、mcpp を使うには gcc, g++ の呼び出しを shell-script に置き換える必要がある。
各処理系に移植するためにはいくつかの設定が必要ですが、mcpp のソースをコンパイルすること自体は、C90 (ANSI C) の仕様を満たしている処理系であれば十分できます。プリプロセッサも同様です。*1, *2
char 型は符号付きでも符号なしでもかまいません。
浮動小数点演算は不要です。
このソースは処理系の微妙な差に影響されないように書いてあります。 もっとも、実際に各処理系でコンパイルするためには、さらにその処理系のバグも回避する必要があります。これはやってみないと何が出てくるかわかりません。私が移植したいくつかの処理系でも、バグであることを確かめその回避方法を見つけるまでにかなりの時間がかかってしまったことが何回かあります。
mcpp が対応していないのは、pre-C90 の処理系のほか、特殊な文字セットを持つ処理系と特殊な CPU です。
EBCDIC には対応していません。
また、整数演算が2の補数でない
CPU にも、対応していません。2の補数でない場合は、#if 式でオーバーフローが発生した時に、おかしくなるかもしれません。
注:
*1 mcpp V.2.5 までは K&R 1st. の処理系でもコンパイルできるようにしていたが、すでにその必要はなくなっていると思われるので、V.2.6 からは C90 を前提とするように改めた。それに伴ってソースを整理し、このドキュメントも整理した。
*2 V.2.6.2 までは C++ でもコンパイルできるようにしていたが、V.2.6.3 からは C だけとした。
mcpp のソースをコンパイルする処理系(ホスト)と、それによって生成された mcpp の実行プログラムを使う処理系(ターゲット)とは、必ずしも同じである必要はありません。これが違っている場合は、noconfig.H (configed.H) の SYSTEM, COMPILER でターゲットを指定し、HOST_SYSTEM, HOST_COMPILER はホストを指定します。また、PART 1 にある諸定義はターゲット用のもので、PART 2 にあるものはホスト用の設定です。system.c は主としてターゲット用のものです。
ホストとターゲットの関係には、次のような制限があります。
なお、ここで言うホストとターゲットというのは、クロスコンパイラのそれとは関係ありません。クロスコンパイルするのはコンパイラ本体の仕事で、プリプロセッサは原則としてそれには関知しません。mcpp を「クロスコンパイラに」移植する場合は、そのクロスコンパイラがここで言うターゲット処理系です。ホスト処理系としてはクロスコンパイラでないものを使うことになるはずです。mcpp を「クロスコンパイラで」コンパイルする場合は、そのクロスコンパイラがホスト処理系で、クロスコンパイラのターゲットがターゲット処理系となります。
mcpp の過去のバージョンでサポートしていたもののその後、サポートをやめた処理系について述べておきます。
mcpp V.2.2 までは次の処理系をサポートしていましたが、V.2.4 で削除しました。
MS-DOS Turbo C V.2.0 OS-9/6x09 Microware C
V.2.5 では次の処理系に関するドキュメントを削除しました。
DJGPP V.1.12 GCC V.2.7.1 MS-DOS LSI C-86 V.3.3 試食版
V.2.6 では上の2つの処理系の設定をソースからも削除し、さらに次の処理系に関するドキュメントとソースを削除しました。
MS-DOS Borland C 4.0 Plan 9 pcc
V.2.6 ではまた、MS-DOS 上の処理系やメモリの小さいシステムのための設定をすべて削除し、pre-C90 の処理系のための設定も削除しました。
V.2.7.2 では次の処理系のサポートも削除しました。
Win32 Borland C 4.0
いずれも古い処理系で、ユーザは少なくなっていると思われるものです。
もしそれらの処理系でコンパイルする場合は、compiler-specific-build では多くの設定が必要で簡単ではありませんが、compiler-independent-build なら、その処理系が C90 の仕様をほぼ実装していれば、次のようにして簡単にできます。
DJGPP については、noconfig.H で SYSTEM, HOST_SYSTEM を SYS_WIN32 とし、HAVE_INTMAX_T, HAVE_INTTYPES_H を FALSE とし、system.H で NBUFF をデフォルトの 1/4 くらいに押さえます。*1
MS-DOS 上の処理系については、noconfig.H で SYSTEM, HOST_SYSTEM を SYS_WIN32 とし、HAVE_LONG_LONG を FALSE とし、system.H で NBUFF をデフォルトの 1/16 くらいに、IDMAX をデフォルトの 1/4 くらいに押さえます。さらに directive.c で SBSIZE をデフォルトの 1/8 くらいにします。そして、large memory model でコンパイルします。
ただし、MS-DOS ではメモリの制約が厳しいので、この設定でコンパイルしておいても、長大なマクロ定義の多い場合などは out of memory となることがあります。
注:
*1 DJGPP / GCC 4.1.0 ではこの設定で mcpp V.2.6.1 をコンパイルできたという報告がある。
mcpp を処理系からは独立して単体で動く compiler-independent 版としてコンパイルすることもできます。これは多くの場合はコンパイルさえ通ればすむので簡単です。Compiler-independent 版は実行時オプションなどの仕様は一定で、処理系依存の部分はありません。OS による相違が少しあるだけです。include directory も UNIX 系で /usr/include, /usr/local/include が設定されるだけなので、あとは環境変数や -I オプションで指定する必要があります。*1, *2
configure が使えるシステムで GCC でコンパイルする場合は単に mcpp のルートディレクトリで
./configure; make; make install
とすればすみます。この場合は noconfig.H ではなく configed.H というヘッダファイルが使われます。configure の詳細については INSTALL-jp を参照してください。
configure の使えないシステムでも mcpp がすでに移植されている処理系では、noconfig ディレクトリにある所定の差分ファイルを使って noconfig.H を書き換えればすみます。それ以外の変更は必要ありません。makefile も noconfig ディレクトリにあるものをコピーして使います。インストールするディレクトリは makefile 中の BINDIR という変数に書きます。そして、src ディレクトリで make し、make install します。
mcpp がすでに移植されている処理系とバージョンが少し違うだけの場合は、まず近いバージョンの差分ファイルを適用して、それを編集します。
mcpp がまだ移植されていない処理系では、noconfig.H を編集して数個のマクロを書き換えたり書き加えたりしてください。まず、HOST_COMPILER を適宜定義します。そして、COMPILER を INDEPENDENT に定義し、SYSTEM をその OS に定義し、VERSION_MSG を適宜定義します。ターゲット処理系は存在しないので、PART 1 には設定することはありません。
PART 2 は mcpp をコンパイルするホスト処理系が Standard C の仕様をどれだけ実装しているか、必要な関数を持っているかによって、設定が違ってきます。最も違うのは long long という型の実装です。Visual C 2002, 2003, 2005 や Borland C 5.5 等では __int64 という型になっていて、その値を printf() で表示する指定子が VC 2005 以外では j や ll ではなく I64 なので、LL_FORM というマクロを "I64" に定義します。MinGW では long long がありますが、printf() の指定子は I64 です。 Visual C 2008 では long long という型名も使えるようになりました。
stpcpy() という関数のない処理系では、HOST_HAVE_STPCPY を FALSE に定義します。
makefile は noconfig ディレクトリにあるものを参考にして書いてください(3.7 も参照のこと)。
Mac OS X には x86 と powerpc の双方のバイナリを片方のマシンで作れるように、ネイティブコンパイラとクロスコンパイラとが用意されています。 また、それを使って universal binary というものも作れるようになっています。 これは x86 と powerpc 用のバイナリを1本のファイルに束ねたものです。 これらの仕組みは少し複雑なので、3.1.4 に compiler-specific-build と compiler-independent-build の双方についてまとめて説明してあります。
なお、mcpp V.2.6.3 からは sourceforge のサイトで compiler-independent-build の何種類かのバイナリ・パッケージが提供されるようになりましたが、これはそれぞれのパッケージング方式に固有の方法でパッケージングされたものです。そのパッケージング仕様はそれぞれに対応するソース・パッケージ中にある設定ファイルに書かれています。FreeBSD, Linux, Mac OS X 用のものはすべてコンパイルには configure スクリプトを使っています。
注:
*1 mcpp V.2.4, V.2.5 では compiler-independent 版の仕様は中途半端で、 処理系の仕様に対応した一般のコマンドとしてのプリプロセッサであった。V.2.6 からは処理系から独立した一定の仕様とした。
*2 コンパイルさえ通ればすむと言っても、MS-DOS 上の処理系ではメモリの制約が厳しいので、コンパイルはできても実行すると out of memory になることがある。MS-DOS では translation limits の設定を大幅に押さえてコンパイルしなければならない。3.10 を参照。
mcpp を他の何らかのメインプログラムからサブルーチンとして呼び出すようにコンパイルすることもできます。これは独立したプリプロセッサと同じように、呼び出し元から実行時オプションを受取り、指定された入力ファイルをプリプロセスして出力ファイルに出力して、呼び出し元に戻るものです。必要なら複数のファイルについて繰り返し呼び出すこともできます。しかし、呼び出し元とトークン単位のやりとりをするものではありません。
サブルーチン版では、出力先としてファイルではなくメモリ上のバッファを使うこともできます。
mcpp のソースは、MCPP_LIB というマクロを non-0 に定義してコンパイルすると、サブルーチン版としてコンパイルされるようになっています。サブルーチンの入り口は stand-alone 版の main() が次のように mcpp_lib_main() という名前に変わるだけです。
int mcpp_lib_main( int argc, char ** argv);
GCC では COMPILER マクロは GNUC ではなく INDEPENDENT のままでコンパイルします。GNUC とすると、GCC の libexec ディレクトリにインストールされて GCC から呼び出されるものができてしまうからです。
他方で、Visual C, Borland C, LCC-Win32 等では COMPILER は INDEPENDENT でも MSC, BORLANDC, LCC 等のどちらでもかまいません。これらの処理系ではプリプロセッサが独立していないので、compiler-specific-build でも処理系から呼び出されることはないからです。compiler-specific-build では処理系固有の事前定義マクロやオプションやいくつかの処理系固有の仕様が定義されるので、それがつごうが良いか悪いかでコンパイルの仕方を選びます。
実際にコンパイルする時の手順を configure script を使う場合と noconfig/*.mak ファイルを使う場合とに分けて説明します。
GCC でコンパイルする場合は configure が使えます。
./configure --enable-mcpplib make sudo make install
とすると、Linux や FreeBSD では compiler-independent 版の libmcpp.a, libmcpp.so.$(SHLIB_VER) が生成されて、デフォルトでは /usr/local/lib にインストールされます。 そして、libmcpp.so および libmcpp.so.0 から libmcpp.so.$(SHLIB_VER) へのリンクが張られます。 *.a は static ライブラリ、*.so は shared ライブラリです。 libmcpp.la というファイルも生成されますが、これは libtool というツールが使うためのものです。 $(SHLIB_VER) は mcpp V.2.6.3 では 0.0.0、V.2.6.4 では 0.0.1、V.2.7 では 0.1.0、V.2.7.1 では 0.2.0、V.2.7.2 では 0.3.0 としています。 mcpp_lib.h, mcpp_out.h というヘッダファイルも /usr/local/include にインストールされます。 これは libmcpp を使うプログラムに必要なものです。
Mac OS X では *.so ではなく *.dylib という名前になります。 また、make で CFLAGS+='-arch i386 -arch ppc' 等と、-arch オプションでいくつかの CPU を指定すると、それらに対応する universal binary のライブラリができます。 さらに -isysroot と -mmacosx-version-min= というオプションを使って、使える Mac OS X のバージョンの範囲を広げることができます。 次の例は、Leopard 上で Tiger と互換の i386 と ppc 用の universal binary の shared ライブラリを作るコマンドです。
make CFLAGS+='-isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.4 -arch i386 -arch ppc'
CygWIN, MinGW では *.so ではなく *.dll という名前の DLL となります。 CygWIN では libmcpp.a, libmcpp.dll.a, libmcpp.la が /usr/local/lib に、cygmcpp-0.dll が /usr/local/bin にインストールされます。 MinGW もほとんど同じですが、cygmcpp-0.dll が libmcpp-0.dll という名前になります。 DLL を使うときは libmcpp.dll.a をリンクします。 これは import library というものです。
生成された libmcpp.so をリンクする main_libmcpp.c という簡単なソースもコンパイルされて、mcpp という名前で /use/local/bin にインストールされます。 このソースでわかるように、libmcpp を使うには mcpp_lib.h というヘッダファイルを include します。*1
最小限のドキュメントもインストールされます。
testmain.c をコンパイルしてライブラリとリンクすると、もう少し複雑なテストができます。 これは configure ではやらないので、次節の方法で行います。
注:
*1 libmcpp を使わない stand-alone 版の compiler-independent-build の mcpp も同じ名前で同じディレクトリにインストールされるので、お互いに上書きし合うことになる。 ドキュメントは同じものが同じディレクトリにインストールされるので、これも上書きされる。
noconfig.H と noconfig ディレクトリの makefile を使う場合は(noconfig.H にその処理系のバージョン用の patch を当てて、makefile の各ディレクトリの設定を自分のシステムに合わせて修正した上で)、次のようにします。
make MCPP_LIB=1 mcpplib make MCPP_LIB=1 mcpplib_installこれは compiler-independent 版のコンパイルですが、GCC 以外では COMPILER=MSC 等として compiler-specific 版でコンパイルしてもかまいません。
Linux, FreeBSD, Mac OS X, CygWIN, MinGW では、libmcpp.la が生成されないのとドキュメントがインストールされない以外は、configure して make した場合と同様の結果になります。 Mac OS X では、mac_osx.mak の UFLAGS という変数定義をコメントアウトしている # を外すと、universal binary の設定になります。 Windows 上の処理系ではライブラリの名前は Linux 等とは違って、次のようになります。
FreeBSD / GCC | Linux / GCC | Mac OS X / GCC | CygWIN / GCC | MinGW / GCC | Visual C, Borland C, LCC-Win32 | |
---|---|---|---|---|---|---|
static library | libmcpp.a | libmcpp.a | libmcpp.a | libmcpp.a | libmcpp.a | mcpp.lib |
shared library or DLL | libmcpp.so.$(SHL_VER) | libmcpp.so.$(SHLIB_VER) | libmcpp.$(SHLIB_VER).dylib | cygmcpp-$(DLL_VER).dll | libmcpp-$(DLL_VER).dll | mcpp$(DLL_VER).dll |
import library of DLL | libmcpp-$(DLL_VER).dll.a | libmcpp-$(DLL_VER).dll.a | mcpp$(DLL_VER).lib |
$(SHL_VER) は mcpp V.2.6.3, V.2.6.4 では 0、V.2.7 では 1、V.2.7.1 では 2、V.2.7.2 では 3 です。 $(SHLIB_VER) は mcpp V.2.6.3 では 0.0.0、V.2.6.4 では 0.0.1、V.2.7 では 0.1.0、V.2.7.1 では 0.2.0、V.2.7.2 では 0.3.0 です。 $(DLL_VER) は mcpp V.2.6.3, V.2.6.4, V.2.7, V.2.7.1, V.2.7.2 ともに 0 です。 $(SHLIB_VER) の1ケタ目または $(DLL_VER) が同じ場合は、$(SHLIB_VER) の2ケタ目以降の高いバージョンは低いバージョンの上位互換の関係にあります。
Windows 上の処理系では import library というものも生成されますが、これは DLL を使うためのものです。 DLL を使うときはこの import library をリンクします。 Static library と import library は makefile 中で指定された $(LIBDIR) に、DLL 本体は $(BINDIR) にインストールされます。 DLL を $(BINDIR) にインストールするのは、Windows では DLL は実行プログラムの PATH の通ったディレクトリになければならないからです。
mcpp_lib.h, mcpp_out.h が $(INCDIR) にインストールされます。
ライブラリ版をリンクする main_mcpplib.c もコンパイルされて mcpp という名前で $(BINDIR) にインストールされます。 Windows 上の処理系ではさらに 'DLL_IMPORT=1' というオプションを指定すると、DLL 版の mcpp がリンクされます。
次のようにすると、testmain.c というサンプルをメインプログラムとしてライブラリ版の mcpp がリンクされます。
make testmain make testmain_install
testmain.c にはメモリ上のバッファに出力する例も書いてあります。 メモリに出力するには、メインプログラム中で mcpp_lib.h というヘッダファイルを include して、そこで宣言されている関数を使います。 上記のコマンドに 'OUT2MEM=1' というオプションを付け加えると、testmain.c でこれが有効になります。 OUT2MEM というマクロは単に testmain.c のためのもので、mcpp のためにはこのマクロは必要ありません。
ライブラリ版を使うときは、testmain.c を参考に main プログラムを書き、noconfig/*.mak を参考に makefile を書いてください。
ライブラリには static library と shared library(Windows 上では dynamically-linking library: DLL)とあります。 Static library は *.o (*.obj) ファイルを集めたもので、コンパイル時にリンクされます。 その変数や関数の global name はそのまま呼び出し側の global name となり、名前の衝突の危険もあります。 このことは、V.2.6.1 まではライブラリ化を予定せずに開発されてきた mcpp にとっては大きな問題となります。 これに対して shared library (DLL) は実行時にリンクされるもので、他のプログラムと共有されます。
Windows 上の DLL ではライブラリの global name は外からは見えず、明示的に export された名前だけが外から import 可能です。 mcpp の場合は mcpp_lib.h にある名前だけを import できます。
UNIX 上の shared library では、一般にはライブラリの global name は外から丸見えで、名前の衝突に神経を使わなければなりません。 しかし、GCC 4.0 以降で mcpp V.2.7 以降をコンパイルした場合は、mcpp_lib.h にある名前以外は外からは見えません。*1, *2
こういうことで、Windows 上では DLL を使い、UNIX 上では GCC 4.0 以降でコンパイルした shared library を使うのが、名前の衝突が起こらず安心です。
注:
*1 mcpp V.2.7 以降では GCC V.4.0 で実装された #pragma GCC visibility * というディレクティブを使うようにしたためである。
*2 なお、GCC V.4.1 以降には -fstack-protector というオプションがあるが、これは #pragma GCC visibility hidden とは両立しないようである。 したがって、libmcpp のコンパイルでは使えない。
これらのヘッダファイルに記述されていることの意味は、たいていは読めばわかると思います。コメントも多く書き込んであります。さらに念のために以下に注釈を書いておきます。
設定の PART 1, PART 2 は noconfig.H (configed.H) にあり、PART 3 は system.H にあります。
まず、ターゲットシステム(mcpp を移植するシステム)とホストシステム(mcpp をコンパイルするシステム)を指定します。
#define HOST_SYSTEM SYSTEM #define HOST_COMPILER COMPILERとしておきます。
SYSTEM, COMPILER の命名には一定の規則がありますが、ソースを見たほうがわかりやすいでしょう。いささか仰々しい形になっていますが、SYSTEM はインクルードファイルのパスリストの形式やOS標準のインクルードディレクトリ等を知るためにしか使われていないので、あまり考える必要はありません。
このほか、実行時オプションに応じて system.c で定義される事前定義マクロもあります。CPU-dependent なマクロなどです。GCC V.3.3 以降では大量の事前定義マクロがあるので、これらの設定とは別に mcpp_g*.h という名前の4本の専用のヘッダファイルが mcpp のインストール時に自動的に生成されます。
以上の設定で事前定義されたマクロはすべて実行時には -N オプションで無効となります。
#line 123 "fname"というCのソースの形式がデフォルトとなっています。その他の形式を使う処理系では、この "#line " の部分を置き換える sequence を文字列リテラルで書いておきます。
# 123 "fname"という形式なら "# " と定義し、どちらでもない独自の形式ならそれに合わせて定義します(場合によっては main.c の sharp() 等に書き足さなければならないかもしれない)。
MBCHAR というマクロは multi-byte character の encoding を指定するものです。mcpp では下記の数種の encoding がすべて同時に実装されます。MBCHAR はデフォルトの encoding を指定するだけで、実行時に encoding を環境変数・オプション・#pragma で変更することができます(使い方については mcpp-manual.html#2.3, mcpp-manual#2.8, mcpp-manual.#3.4 を参照)。
初めの5つはいずれも shift-states を持たない、1文字が2バイトを占める encoding です。なお、multi-byte character, wide character の encoding が2バイトであるにもかかわらず、wchar_t が4バイトの型になっている処理系もありますが、プリプロセッサは wchar_t の型には関知しません。ソース上では multi-byte character や wide character が2バイトを占めているので、それに従って処理をします。
EUC_JP 日本の extended UNIX code (UJIS) SJIS 日本の shift-JIS (MS-Kanji) GB2312 中国の EUC-like な GB-2312(簡体字) BIGFIVE 台湾の Big5(繁体字) KSC5601 韓国の EUC-like な KSC-5601 (KSX 1001) ISO2022_JP ISO-2022-JP1 という国際規格の日本語 UTF8 unicode の encoding の1種である UTF-8
なお、multi-byte character に関するコンパイラの動作は実行する時の環境によって変わる場合があります。自分の使う環境に合わせて設定してください。これについては、mcpp-manual.html#2.8 も参照してください。
次の2つは便宜上、PART 2 に書いてありますが、 ターゲット処理系とホスト処理系の双方が指定の型を持つ場合に TRUE とし、そうでない場合は FALSE とします。
noconfig.H, configed.H ではターゲット処理系とホスト処理系とが同一であると仮定していますが、異なる場合は PART 2 を書き直す必要があります。
PART 1 にもホストとターゲットが同一と仮定している部分があるので、必要ならそれを書き換えます。例えば次のようにホスト処理系の事前定義マクロを使っている行です。
#if _MSC_VER >= 1200
system.H では mcpp の動作仕様を指定するマクロが定義されています。
mcpp には mcpp_mode という変数があり、これがマクロの展開方法、使えるディレクティブ、使える predefined マクロ等、プリプロセッサの根幹となる動作の仕様を決めています。mcpp_mode の値には OLD_PREP, KR, STD, POST_STD の4種があります。
mcpp の動作モードは実行時オプションで指定されます。mcpp をコンパイルする時には、これらの4つのマクロについては何も設定することはありません。しかし、各種の設定を正しく行うためには4つの動作仕様の違いを理解することが必要です。
このほかに COMPAT モードもありますが、これは STD の変種です。
ここでは KR と OLD_PREP を合わせて pre-Standard モード、STD と POST_STD を合わせて Standard モードと呼ぶことにします。各モードの仕様の詳細については mcpp-manual.html#2.1 を参照してください。
注:
*1 UCN は C++, C99 の仕様で、Unicode の文字の値を \u または \U で始まる16進 escape sequence で表記するものである(mcpp-manual.html#3.7, cpp-test.html#2.8, cpp-test.html#4.6 参照)。
それぞれ大きい値にするほど仕様は上等になりますが、NWORK, NBUFF, NMACWORK, SBSIZE は大きいとそれだけ大きなメモリを食います。実際のメモリ消費はマクロ定義の量によってさらに増えてゆきます(それぞれのマクロ定義の長さによって必要メモリが決まる。マクロ定義の内部的な形式は internal.H の struct defbuf に書いてある)。
NMACWORK, NEXP, RESCAN_LIMIT はスタックを消費します。
他のものはメモリはさほど必要としませんが、system.H のデフォルトの値以上にしても実用上の意味はほとんどないでしょう。
C90, C99 の要求する translation limits の最低限度は system.H の最後のほうに書いてあります。C++98 の translation limits も書いてありますが、これはCと異なり、要求仕様ではありません。
主としてターゲット処理系に関するいくつかの設定を実装しています。
Standard C にはないライブラリ関数のうち、getopt() と stpcpy() のソースがここに書いてあります。 getopt() はリンクのトラブルを防ぐため、mcpp_getopt() と名前を変えてあります。 stpcpy() は HOST_HAVE_STPCPY が FALSE の時に使われます。
「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 のバグを意味します。
BSD_MALLOC, DB_MALLOC, MALLOC_DBG というマクロのどれかを 1 に定義して mcpp をコンパイルすると、私の malloc() とは別のそれぞれデバッグ機能を持った malloc() が使われます。いずれにしても、処理系付属のものではない malloc() を使うには、コンパイルする前にライブラリを作っておかなければなりません。これについては kmmalloc のドキュメントを見てください。
注:
*1 kmmalloc は次のところにある。
*2 CygWIN ではライブラリの組み立てが他の malloc() を使えないようになっているので、私の malloc() は使っていない。 Visual C 2005, 2008 でも同様である。
プリプロセスの Standard C 適合性を検証するための Validation Suite を mcpp といっしょに公開しています。Standard C のプリプロセスのすべての規定を検証できるものにしたつもりです。もちろん、mcpp はこれを使ってチェックしてあります。それも、上記のすべての処理系でコンパイルしてチェックしてあります。したがって、バグや誤仕様はほとんどないと思いますが、しかし、まだいくつか残っている恐れもあります。まだ移植されていない処理系に新しく移植した場合は、処理系のバグにひっかかる可能性もあります。
もし、不可解な動作が発見されたら、ぜひご報告ください。その際には、次の点のチェックをお願いします。
もし、"Bug: ..." という診断メッセージが出たら、それは間違いなく mcpp または処理系の(たぶん mcpp の)バグです。また、たとえむちゃくちゃな「ソース」でも、それを食わせることで mcpp が暴走するなら、それもバグです。
もちろん、Standard C モード以外のモードの mcpp は Validation Suite では「間違い」だらけの動作をしますが、それは仕様です(それでも暴走はしないはず)。どういう仕様かは 4.1.3 を見てください。
私が書いた kmmalloc という malloc() 等のライブラリがあります(4.extra 参照)。
もし、私のこの malloc() 等をリンクした mcpp で 120 から 124(処理系によっては 2120 から 2124)のエラー番号で途中で exit することがあれば、それは間違いなく mcpp または処理系の(たぶんライブラリ関数の)バグです。
また、テストに使うサンプルソースのどこかに
#pragma MCPP debug memory
と書いておくと、その個所および終了時にヒープメモリに関する情報が出力されますが、ここで Heap error: ... というメッセージが出ることがあれば、それも間違いなく mcpp または処理系のバグです。
これらのバグが発見されたら、サンプルソースの各部分を #if 0 と #endif ではさんでテストを繰り返し、バグを発生する部分を絞り込んでみてください。
バグ報告には次のようなデータを付けてくださるようお願いします。
mcpp はほとんどの処理系に比較的簡単に移植できるように書いてあるつもりです。
しかし、私が持っている処理系は少数です。他の処理系への移植ではソースの書き足しが必要なはずです。それらの処理系への移植の報告をお待ちしています。それをソースにフィードバックしていきたいと思います。
移植の報告は次のような形でお願いします。
正しく移植できたかどうかを確かめるには、compiler-specific-build では、まずプリプロセッサを入れ替えて、ヘッダファイルの "pre-preprocess" の機能を使って自分自身をリコンパイルしてみるのが手っ取り早いでしょう。
さらに Validation Suite で STD モードのチェックをします。ただ、これはファイルの数が多いので、デバッグを繰り返す時には手間がかかりすぎます。デバッグ中はまず、n_std.c をコンパイルして、正常にコンパイル・実行されるかどうかを見ます。処理系付属のコンパイラドライバでは mcpp に渡す方法のないオプションもありますが、それについては mcpp-manual.html#2.2 を見てください。先に mcpp を通してからコンパイルする手もあります。
もしこれがうまくいかない場合は、 n_std.t というサンプルを使って、どこが悪いのか、目でチェックします。これがうまくいったら、e_std.t, m_*.t, unspcs.t, warns.t, misc.t もチェックします。"post-Standard" モードでは n_post.t, e_post.t を使います。
これらを mcpp -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 は GCC の testsuite に対応しています。したがって、mcpp を GCC のどれかのバージョンに移植した場合は、GCC / testsuite がインストールされていれば、GCC のプリプロセッサを mcpp に置き換えると、mcpp の自動テストができます。これについては cpp-test.html#3.2.3, mcpp-manual.html#3.9.7 を見てください。
mcpp は UNIX 系システムでは configure スクリプトが使えます。 しかし、UNIX 系システムでの GCC 以外の処理系については私はまったく知らないので、compiler-specific-build の configure ではいくつかのオプションを指定してもらわなければなりません。
これらのオプションで指定する内容については、その処理系を使っている人は知っているか、または調べることができるはずです。おわかりの方はぜひ教えてください。Configure に取り込んでゆきたいと思います。
Configure については INSTALL をご覧ください。
移植がうまくいかない場合は、そのようすをお知らせください。
次のデータを付けてくれれば、移植したソースをお返しできるかもしれません。Configure の使える環境では、これらのデータのうちのかなりの部分を configure によって知ることができます。
なお、C90 (ANSI C) に対応していない処理系は、mcpp V.2.6 からは移植の対象から外しました。
/* t_line.c */ #include <stdio.h> #line 1000 error line; main( void) { return 0; }
ホスト処理系とターゲット処理系が違う場合はその双方について上記のデータがあれば、何とかなるでしょう。
こうして並べてみると、チェックすべきことがずいぶんたくさんありますね。しかし、多くの処理系では移植ずみの処理系と共通の特性が多いでしょうから、一応動作するだけの移植であればさほどの手間ではないはずです。比較的手間のかかるのは実行時オプションと #pragma、さらに規格外仕様の実装です。これは一応動作するようになってから、徐々にやってゆくこともできます。唯一面倒なのは、処理系のバグにひっかかった場合です。
私が持っている処理系のプリプロセッサを私の検証セットでテストした結果は、cpp-test.html#6 にまとめてあります。
その他の処理系についてテストした結果をお知らせください。項目が多いのでかなりの手間ですが。
cpp_test.c によるテストであれば手間はかからないので、これだけでもお願いします。GCC の場合は、検証セットによる自動テストができます。
バグ報告のほかにも、mcpp の使い勝手、診断メッセージ、mcpp のソース、Validation Suite、私の Standard C 解釈、ドキュメントの書き方、などについてご意見をお寄せください。
趣味で作ったプリプロセッサですが、V.2.0 までだけでも6年半もかけて凝りに凝った労作です。凝りついでにできるだけ良いものにしたいと思っています。Cプリプロセッサについては、私の持っていない処理系への移植とテスト以外は、やって意味のあることはほとんどすべてやったつもりです。多少とも問題が残っていれば、手を入れたいと思います。
Martin Minow のソースはとてもきれいな、クセのない、わかりやすいもので、これを読むだけでも私にとってはずいぶん勉強になりました。
こういうものに興味を持つ人はかなり限られていると思いますが、多くのコメントと情報をお待ちしています。
ご意見と情報は
の "Open Discussion Forum" またはメールでお願いします。
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 いじりに集中しなくなったせいもあります。しかし、それだけではありません。この間にやったのは次のようなことです。
中でも時間のかかったのはドキュメントでした。ことに後半の4年くらいはソースをいじった時間はほんの少しで、ドキュメント書きが作業の大半を占めていました。おかげで大変な分量になってしまいましたが、しかし、時間がかかったのは量が多いせいばかりではありません。ドキュメントを書いていると、仕様の不明確なところが次々と出てくるのです。そのたびに規格書を読み返し、ソースを少しずついじりました。ソースをいじった時間は少なくても、回数は少なくありません。規格書もプリプロセス規定だけではなく、全体を ANSI C の Rationale も含めてよく読んでみました。私はプリプロセッサを作ることを通して C90 の勉強をしたようなものです。さらにはこれを通して、C90 の規定の問題点も明確に把握することができました。
テストプログラムは初めは簡単なサンプルを何本か書いただけでした。ところが、書いて cpp をテストするたびに意外なバグが見つかるのです。そこで、C90 プリプロセスの全規定をテストする Validation Suite を書くことにしました。そして、Valadation Suite を書くことを通して、C90 の問題点がさらに明らかになってきました。C90 の不規則な部分に対応するのは、自分にとってはわずらわしいばかりであまり意味のないことでしたが、それよりも意味のある部分のほうがはるかに多かったことは確かです。
この作業を通して私が学んだのは、次のようなことです。
この考え方は完全主義的なものです。
世の中のことは完全主義ではうまくゆかないものが多く、プログラムも例外ではありませんが、中には完全主義が重要な意味を持つ分野もあります。言語処理系はその一つでしょう。
趣味だから何年もかけて徹底的にやることができたとも言えます。それにしても6年半は長すぎました。こんなに時間をかけて完全なプログラムを作って、いったいだれが使うのだろうという疑問がずっと続いていました。趣味で作るプログラムとしては、このくらいが規模の限度なのでしょう。
しかし、mcpp はもう作ってしまったので、今後もメンテナンスをしていくつもりです。せっかくですから皆さん、コメント、報告、移植をお願いします。
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 は時間だけでなく、手間も比較的かかっています。GCC V.2.9x に実装してみたところ、GCC / cpp との互換性確保のためにかなり手を加えなければならないことがわかったからです。多くのオプションを追加し、拡張仕様を実装しました。また、一部のエラーをウォーニングに格下げしたり、頻発するウォーニングをデフォルトのウォーニングクラスからはずしたりして、規格による制限を緩和しています。
こうした変更の多くは後向きのものであり、楽しいものではありませんでした。ことに C90 以前の "traditional" な仕様の一部を C99 の仕様と両立させなければならないというのは、はなはだ不本意なことでした。しかし、これが現在のオープンソース界の実情であれば、それにある程度合わせるのはやむをえません。
規格による制約を緩和したことで、他の処理系用の版も、処理系付属のプリプロセッサと置き換えて使うためには使いやすくなったと思います。
V.2.3 への update の途中で、mcpp および Validation Suite は情報処理振興事業協会 (IPA) の平成14年度「未踏ソフトウェア創造事業」というものに採択されました。たまたまこの事業のことを知ったので応募してみたところ、新部 裕・プロジェクトマネージャが採択してくれたのです。こうして 2002/07 から 2003/02 までは IPA の資金援助と新部PMの助言のもとに、開発が進められることになりました。ドキュメントの英訳も、ハイウェルが引き受けてくれることになりました
比較的小さいソフトウェアながらも、これだけ時間をかけ、私のライフワークのようになってしまったものです。その完成度には自信がありましたが、世に出る機会がなく、残念な思いをしてきました。その機会がついに与えられたのです。私はこのプロジェクトを遂行するため、仕事を週3日に減らしました。
私がこのプロジェクトでやることとして考えたのは、次のようなことでした。
さらに新部 PM から次の提案がありました。
私自身もこれらはぜひやりたいことであるので、喜んで計画に追加しました。
ところが、私の計画は遅延に遅延を重ねました。
まず、ディスククラッシュに見舞われました。また、何か新しいことをするたびに使ったことのないソフトウェアを使って、そのたびに時間がかかりました。GCC をソースからコンパイルしたのも初めてですが、これはいくつかのトラブルに見舞われました。大量のドキュメントの更新と大量の英訳のチェックと修正にも、かなりの時間がかかりました。その上、母の入院という事態まで発生しました。プロジェクトは市販の処理系への対応等、計画の一部を断念する結果となりました。
私はこれまで一つの穴を深く掘ってゆくようなことしかしてこなかったので、穴を少し広げようとするとひどく手間がかかってしまうのです。アマチュアプログラマが何かを掘り下げるには、こういうやり方をしなければできることではありません。しかし、その成果を世に出すためには、穴をいくらか広げなければならないのでした。
穴を広げる過程で、私は新部 PM の助言と励ましを得て、いくつかの未経験のソフトウェアを習得し、開発の前線というものに触れることができました。自分の文章がこなれた英文に翻訳されて戻ってくるのも、大変うれしいことでした。時間に追われるのは苦しいことですが、内容はどれも新鮮で楽しいことでした。
「未踏ソフトウェア創造事業」はこれでおしまいではありませんでした。平成15年度にも、伊知地 宏・プロジェクトマネージャが mcpp を継続プロジェクトとして採択してくれたのです。こうして、前年度の積み残しの課題を初めとして、私にとっては未経験の領域のいくつかの課題に取り組むこととなりました。
今回も私の6年前のパソコンにトラブルが発生し、ハードウェアと OS を upgrade する過程でさらにいくつかのトラブルに見舞われました。未経験のソフトウェアの習得にも時間を要し、開発はやはり遅れ気味でした。いったん退院して比較的元気になっていた母の容態が、プロジェクトが大詰めに近づくのと並行して以前にも増して悪くなってきたことも、心配の種でした(母は 2004 年 2 月に死去した)。しかし、伊知地PMが目標を無理のないところに設定してくれたおかげで、あわてずにじっくりと課題に取り組むことができました。
Visual C++ への移植、configure スクリプトの作成、多様な multi-byte character encoding への対応等の課題を達成することができました。また、ソースコードの整理という目立たないながらも作者としてはこだわりのある課題にも取り組みました。日本語版と英語版のドキュメントの更新という手間のかかる作業も、ハイウェルの協力を得て達成することができました。
この成果によって、私は伊知地PMから何と「スーパークリエータ」という評価を受けることができました。私の実力にとっては過分の評価ですが、長年にわたる mcpp の積み重ねを認めていただいたものと思い、大変喜んでいます。
2年近くにわたる「未踏ソフトウェア」のプロジェクトによって、mcpp は世界一高品質な C/C++ プリプロセッサに仕上がったつもりです。熟年のアマチュアプログラマとして非力ながらもよくやったと自分では納得しています。
未踏ソフトウェアのプロジェクトが終わってからも、mcpp の改良作業は続けられています。まだいくつかの課題が残っています。これらの課題を達成し、mcpp を普及させるために、今後も着実に取り組みを続けてゆくつもりです。