こんにちは、高木です。
いつものようにPHPでC言語の前処理をする話題です。ただし、今回はちょっと複雑です。何をやろうとしているのか、その手順を次に挙げます。
- PHPを使ってC言語のソースコードを自動生成する。
- 生成されたC言語のソースコードをコンパイル&実行して、PHPのソースコードを自動生成する。
- 生成されたPHPのソースコードをC言語の前処理に使う。
複雑ですよね。順を追って説明していきます。
最初に、最終的に何をやりたいのかをハッキリさせておくことにしましょう。前回の記事のように、前処理で使う「C言語の型の特性」をPHPで定義するのが今回の目的です。前回は配列定数C_LIMITSの内容について、一例としてchar型だけを取り上げましたが、今回はすべての実数型を対象にします。C言語の型はたくさんあるので、手作業でひとつひとつ定義していくのは大変です。そこでPHPの出番となります。
C言語の型の最大値、最小値や、サイズ、境界調整数を求めるのは、C言語のプログラムを使って行う方が簡単です。マイコン向けのクロスコンパイラなどでは手作業で行わないといけないとしても、セルフコンパイルできる環境ならC言語のプログラムを作って、C_LIMITSを自動生成させましょう。これが上記手順の2.にあたります。そして、2.で使うC言語のソースコードを生成するのが1.になります。今回は上記手順のうち、1.と2.を解説することにします。
とりあえず手順1.のPHPのコードを貼っておきます。ほとんど使い捨てのコードなので手抜きですが、まあこんな感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
<?php $integers = [ [ 'signed char', 'SCHAR' ], [ 'unsigned char', 'UCHAR' ], [ 'char', 'CHAR' ], [ 'short', 'SHRT' ], [ 'unsigned short', 'USHRT' ], [ 'int', 'INT' ], [ 'unsigned int', 'UINT' ], [ 'long', 'LONG' ], [ 'unsigned long', 'ULONG' ], [ 'long long', 'LLONG' ], [ 'unsigned long long', 'ULLONG' ], ]; $floats = [ [ 'float', 'FLT' ], [ 'double', 'DBL' ], [ 'long double', 'LDBL' ], ]; ?> #include <stdio.h> #include <limits.h> #include <float.h> #include <stddef.h> int main(void) { puts("<?php"); puts("define('C_LIMITS', ["); <?php // 整数型 foreach ($integers as $item) { $type = $item[0]; $prefix = $item[1]; $max = $prefix . '_MAX'; $min = $prefix . '_MIN'; echo <<<EOT puts("'$type' => ["); printf(" 'max' => %llu,\\n", (unsigned long long)$max); #ifdef $min printf(" 'min' => %lld,\\n", (long long)$min); printf(" 'signed' => %s,\\n", $min < 0 ? "true" : "false"); #else puts(" 'min' => '0',"); #endif printf(" 'size' => %zu,\\n", sizeof($type)); printf(" 'align' => %zu,\\n", offsetof(struct { char a; $type b; }, b)); puts(" ],"); EOT; } // 実浮動小数点型 foreach ($floats as $item) { $type = $item[0]; $prefix = $item[1]; $max = $prefix . '_MAX'; $min = $prefix . '_MIN'; $precision = $prefix . '_DIG'; echo <<<EOT puts("'$type' => ["); printf(" 'max' => %.*Le,\\n", $precision, (long double)$max); printf(" 'min' => %.*Le,\\n", $precision, (long double)$min); puts(" 'signed' => true,"); printf(" 'size' => %zu,\\n", sizeof($type)); printf(" 'align' => %zu,\\n", offsetof(struct { char a; $type b; }, b)); puts(" ],"); EOT; } ?> puts("]);"); puts("?>"); } |
このPHPのコードのうち、最初の部分(22行目まで)はC言語の型名と<limits.h>や<float.h>で定義されるマクロの接頭辞を配列にまとめています。
23行目からはいったんC言語のソースコードになります。
32行目から再度PHPのコードを埋め込んで、整数型と実浮動小数点型に分けて、配列C_LIMITSの各要素を出力するputsとprintfのお化けを自動生成しています。
最後、76行からはC言語のソースコードに戻っています。
なお、今回は使い捨てのコードということもあって、#line指令を埋め込むことは想定していません。
このPHPのコードを実行すると、次のようなC言語のソースコードが生成されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <stdio.h> #include <limits.h> #include <float.h> #include <stddef.h> int main(void) { puts("<?php"); puts("define('C_LIMITS', ["); puts("'signed char' => ["); printf(" 'max' => %llu,\n", (unsigned long long)SCHAR_MAX); #ifdef SCHAR_MIN printf(" 'min' => %lld,\n", (long long)SCHAR_MIN); printf(" 'signed' => %s,\n", SCHAR_MIN < 0 ? "true" : "false"); #else puts(" 'min' => '0',"); #endif printf(" 'size' => %zu,\n", sizeof(signed char)); printf(" 'align' => %zu,\n", offsetof(struct { char a; signed char b; }, b)); puts(" ],"); (中略) puts("'long double' => ["); printf(" 'max' => %.*Le,\n", LDBL_DIG, (long double)LDBL_MAX); printf(" 'min' => %.*Le,\n", LDBL_DIG, (long double)LDBL_MIN); puts(" 'signed' => true,"); printf(" 'size' => %zu,\n", sizeof(long double)); printf(" 'align' => %zu,\n", offsetof(struct { char a; long double b; }, b)); puts(" ],"); puts("]);"); puts("?>"); } |
非常に長いので途中を省略していますが、雰囲気はつかんでいただけるかと思います。
さらに、このC言語のソースコードをコンパイルして実行すると、次のようなPHPのソースコードが生成されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
<?php define('C_LIMITS', [ 'signed char' => [ 'max' => 127, 'min' => -128, 'signed' => true, 'size' => 1, 'align' => 1, ], 'unsigned char' => [ 'max' => 255, 'min' => '0', 'size' => 1, 'align' => 1, ], 'char' => [ 'max' => 127, 'min' => -128, 'signed' => true, 'size' => 1, 'align' => 1, ], 'short' => [ 'max' => 32767, 'min' => -32768, 'signed' => true, 'size' => 2, 'align' => 2, ], 'unsigned short' => [ 'max' => 65535, 'min' => '0', 'size' => 2, 'align' => 2, ], 'int' => [ 'max' => 2147483647, 'min' => -2147483648, 'signed' => true, 'size' => 4, 'align' => 4, ], 'unsigned int' => [ 'max' => 4294967295, 'min' => '0', 'size' => 4, 'align' => 4, ], 'long' => [ 'max' => 9223372036854775807, 'min' => -9223372036854775808, 'signed' => true, 'size' => 8, 'align' => 8, ], 'unsigned long' => [ 'max' => 18446744073709551615, 'min' => '0', 'size' => 8, 'align' => 8, ], 'long long' => [ 'max' => 9223372036854775807, 'min' => -9223372036854775808, 'signed' => true, 'size' => 8, 'align' => 8, ], 'unsigned long long' => [ 'max' => 18446744073709551615, 'min' => '0', 'size' => 8, 'align' => 8, ], 'float' => [ 'max' => 3.402823e+38, 'min' => 1.175494e-38, 'signed' => true, 'size' => 4, 'align' => 4, ], 'double' => [ 'max' => 1.797693134862316e+308, 'min' => 2.225073858507201e-308, 'signed' => true, 'size' => 8, 'align' => 8, ], 'long double' => [ 'max' => 1.189731495357231765e+4932, 'min' => 3.362103143112093506e-4932, 'signed' => true, 'size' => 16, 'align' => 16, ], ]); ?> |
このできあがったPHPのソースコードを、C言語の前処理に使うことになります。