先日、酒を飲みながらgccだかなんだかのコンパイラの話をしていたときに、ふと、「そういえば昔のgccはプリプロセッサが#pragma
ディレクティブを見つけると、コンパイルを中止してゲームを起動するとかいうのがあったよな」と思い出した。
この元ネタは、もう絶版だけど、「エキスパートCプログラミング」という本に書いてある。

エキスパートCプログラミング―知られざるCの深層 (Ascii books)
- 作者: ピーターヴァン・デ・リンデン,Peter van der Linden,梅原系
- 出版社/メーカー: アスキー
- 発売日: 1996/03
- メディア: 単行本
- 購入: 17人 クリック: 404回
- この商品を含むブログ (78件) を見る
C/C++において、#pragma
は、ヘッダの二重インクルードを防止するために、伝統的な
#ifndef GUARD #define GUARD ... #endif
を使用したインクルードガードに変わって#pragma once
のように用いられることが多い。しかし、pragmaに続くキーワードやその動作は下記のページにもあるように、処理系ごとに定義が委ねられている。
当時のgccはどうも#pragma
をいけ好かないものとしていたようで、「処理系ごとに決められた動作」として、ゲームを起動するというようなことをやっていたらしい。
せっかくなので、gccの古いソースコードを掘り返してみる。 アーカイブへの直リンクは
http://ftp.tsukuba.wide.ad.jp/software/gcc/old-releases/gcc-1/
さて、まずは一番古そうなのをということで、verion 0.9 (gcc-0.9.tar.bz2, 1987/03/22リリース)を覗いてみる。 このなかの、cccp.cというファイルを見てみると、2401行目付近に、
/* * the behavior of the #pragma directive is implementation defined. * this implementation defines it as follows. */ do_pragma () { close (0); if (open ("/dev/tty", O_RDONLY) != 0) goto nope; close (1); if (open ("/dev/tty", O_WRONLY) != 1) goto nope; execl ("/usr/games/hack", "#pragma", 0); execl ("/usr/games/rogue", "#pragma", 0); execl ("/usr/new/emacs", "-f", "hanoi", "9", "-kill", 0); execl ("/usr/local/emacs", "-f", "hanoi", "9", "-kill", 0); nope: fatal ("You are in a maze of twisty compiler features, all different"); }
というように、このころのコードには確かにゲームを起動するコードが仕組まれている。ちなみに、私はhackもrogueも名前だけは知っていたが、起動して遊んだことはないので何するゲームなのかよく知らない*1。 なるほどexeclはそのプロセスをそのまま呼び出すコマンドで置き換えるから、より新しいものから起動を試してみて、見つからなかったらrogueというように順々に探していくわけだ。 ちなみに、コンパイルができなかったために、本当にpragmaを見つけるとゲームが起動するのか、手元で確かめてはいない。というのも、do_pragma()という関数自体が、関数ポインタを介して呼ばれているようで、必ず呼ばれるものか、いまいち確信は持てていないためだ。
つぎに、gcc-1.21(gcc-1.21.tar.bz2, 1988/05/01リリース)を見てみる。 すると、同じくcccp.cの2993行目付近
#if 0 /* This was a fun hack, but #pragma seems to start to be useful. By failing to recognize it, we pass it through unchanged to cc1. */ /* * the behavior of the #pragma directive is implementation defined. * this implementation defines it as follows. */ do_pragma () { close (0); if (open ("/dev/tty", O_RDONLY) != 0) goto nope; close (1); if (open ("/dev/tty", O_WRONLY) != 1) goto nope; execl ("/usr/games/hack", "#pragma", 0); execl ("/usr/games/rogue", "#pragma", 0); execl ("/usr/new/emacs", "-f", "hanoi", "9", "-kill", 0); execl ("/usr/local/emacs", "-f", "hanoi", "9", "-kill", 0); nope: fatal ("You are in a maze of twisty compiler features, all different"); } #endif
というように、コメントアウトされてしまっている。しかも「なんだか使い道が見えてきた(意訳)」という言い訳つきである。上記のwikibooksによると、「処理系が認識できないプラグマは無視する」と書いてあるが、この段階ではコメントにもあるように、プリプロセッサでは無視しているようだ。
エキスパートCプログラミングという本自体、1996年に出版されていることを考えると、この本が「昔」というのだから「昔のさらに昔」ということで本当に初期の一瞬だけの話だけだったようだ。ちなみに、私は1990年生まれなので、生まれる前にはもうpragmaは「ちゃんと使われていた」ことを今更知った次第である。
なお、gcc-0.9のビルド周辺に関しては、下記のページでビルドの試行錯誤などがされている。
virtuallyfun.superglobalmegacorp.com
終わり