[実験] PCP: C Preprocessor

バカバカしいことも大まじめに取り組めば、ひとつぐらい実のなるものもあるかもしれないということで、いろいろ実験を行っています。今回もそのひとつで、PHPをCのプリプロセッサに使うことでメタプログラミングをやろうという話です。この話題は、当サイトでは以前から何度か出ていましたが、いくつかの問題点を解消した結果を今回はまとめておくことにします。

そもそもPHPをCのプロプロセッサに使うというのは、どのようなことなのかからおさらいをしておきます。例えば、バイナリファイルの内容をそのまま使って配列を初期化するようなケースを考えてください。当然、Cの文法だけでは実現できませんので、別途変換ツールを作って対応することになります。しかし、PHPをプリプロセッサに使えば、Cのソースコードにバイナリファイルから配列への変換処理を埋め込むことができます。

const unsigned char data[] = {
<?php
$handle = fopen("data.bin", "r");
for ($i = 0; !feof($handle); $i++)
{
    $c = ord(fgetc($handle));
    printf("0x%02x, ", $c);
    if ($i % 16 == 0) echo "\n";
}
fclose($handle);
?>
};

上記のようなイメージです(このコードはテストしていません)。あらかじめ、こうした処理を関数化しておけば、次のように非常に簡単な記述で済ますことも可能になります。

const unsigned char data[] = {
<?php binaryfile_to_initializer("data.bin"); ?>
};

このコードを記述したソースファイルが foo.c の場合、次のようにすればコンパイルすることができます(GCCの場合)。

php foo.c | gcc -c -x c -

こうした方法には大きな問題がひとつあり、先頭行に前処理指令を書くと、コメント扱いになってしまいます。Makefileで記述すれば良いとはいえ、コンパイルのコマンドが面倒なことも確かです。こうした問題を解決するためのPHPスクリプトを書いてみました(添付ファイル)。そして、記述上のルールを整理してみました。

まず、PHPの正式名が、PHP: Hypertext Preprocessorですので、それにちなんで、このスクリプトはPCP: C Preprocessorと呼ぶことにしました。スクリプト名もそれにあわせて pcp としています。 PCPのソースファイルは .pcp というサフィックス(拡張子)を付けることにします。ソースファイル foo.pcp をコンパイルするには次のようにします。

pcp foo.c

pcpコマンドは、指定したPCPソースファイルをテンポラリファイルからインクルードするようにしています。これにより、先頭行に前処理指令を記述しても、問題なく処理することができます。さらに、ソースファイルに先立って、pcp_header.php というスクリプトがあれば、それを読み込むようになっています(php_header.php)。

PCPソースファイル中では、PHPのコードを <? ... ?> で囲むことにします。コードを echo させるには <?= ... ?> とします。PHPでは必ずしも推奨されない記法ですが、PCPではこのように記述することにします。

今後の展開としては、Cの処理系定義の値をPHP側で知ることができるように、<limits.h> や <float.h> で定義される値を事前にスクリプトとして用意しておくとか、PHPのコードからCコンパイラを呼び出して、コード片のコンパイル結果を得るとか、高速化のためにそれをキャッシュするとか、そういったことを考えています。

この記事のトラックバックURL:

http://www.kijineko.co.jp/trackback/750
添付サイズ
pcp1.17 KB