PHPをC++の前処理に使う際に#line指令を埋め込む。
高木です。こんにちは。
今回は久々に技術ネタ、それもかなりマニアックなネタを書いてみたいと思います。
話の前提として、以前書いた記事を一読しておいてください。
プラグインがなかったりしてコードの部分の表示がいまいちですが、読めないことはないでしょう。
元のネタからして相当マニアックですね。
今回はこのネタをさらに深掘りしていきます。
プログラミングをしていると、当然のことながら記述ミスその他の理由でコンパイルエラーが発生します。
エラーメッセージには、エラーが見つかったソースファイル名と行番号が含まれますが、それはあくまでもPHPによる前処理後のものです。
コーディングしている者にしてみれば前処理前の行番号が欲しいはずです。
けれども、PHPを処理した結果、行番号は無残にもずれてしまいます。
これではいけません。
この問題を解決するには、前処理後のC++のコードに#line指令を埋め込んでやる必要があります。
#line指令なんかほとんどのC++プログラマーは使ったことがないと思いますが、こういうときに使うんですよ!
#line指令の埋め込み方ですが、安易に考えれば、”?>”を”?>\n#line 行番号\n”に置換してやれば済む気がしますね。
ただ、その方法だと、PHPの文字列中に?>が含まれている場合にも置換してしまうので、うまくいかないケースが出てきます。
この問題は、PHPのtoken_get_all関数を使うことで解決できそうです。
以下は、ソースコードをいったんtoken_get_allで分解してから#line指令を埋め込んで再結合し、PHPとして評価してから標準出力に書き出すサンプルです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php $tokens = token_get_all(file_get_contents($argv[1])); $source = "#line 1 \"" . __FILE__ . "\"\n"; foreach ($tokens as $token) { if (!is_array($token)) { $source .= $token; } else { $source .= $token[1]; if ($token[0] === T_CLOSE_TAG) $source .= "\n#line " . $token[2] . "\n"; } } eval("?>\n$source"); |
標準出力として吐き出した結果をパイプラインで直接C++コンパイラに入力することを想定して、先頭行にも#line指令をファイル名付きで出力しています。
いったんファイルに書き込んでからコンパイルする場合でも、前処理の前後でファイル名が変わるはずですから、この方法は有効です。
このように、PHPで前処理するにしても、直接PHPで評価する前にさらに前処理をする必要が出てきます。
実は少なくとももうひとつ前処理を行う必要があるのですが、それはまた別の機会に取っておくことにします。