2005-12-18
そろそろネタが尽きてきたので更新も遅れがちですが、今回の記事は、
OKWave の
このQ&A から引用したいと思います。
簡単に解説すると、単に型やオブジェクトのサイズを静的に調べたいだけであれば、sizeof 演算子を使えば十分です。sizeof の評価結果はそのままプログラムの中で使うことができますが、その値をプログラマが知ろうとすると、コンパイル結果を調べるか、一旦外部装置に出力するしかありません。あるいはデバッガで調べるかです。
このうち、コンパイル結果を調べる以外の方法では、結局のところ実行時にならないと型やオブジェクトのサイズを調べることができません。しかし、実際の開発では、正確なサイズを知る必要が出てくる場合が少なからずあります。そんなとき、今回紹介する方法が役に立ちます。
OKWave の回答では、C++ では次のように書くことで型のサイズを知ることができるとされています。
template <typename T, int> struct type_;
#define PRINT_SIZEOF(type) inline void size_of_(type_<type, sizeof(type)>) {}
PRINT_SIZEOF(foo)
つまり、foo 型を PRINT_SIZEOF マクロの実引数として渡せば、sizeof(foo) の値を含んだエラーメッセージが出力されるというものです。
ファイル名: In function `void size_of_(type_<foo, 123>)':
ファイル名:行番号: error: `<anonymous>' has incomplete type
ファイル名:行番号: error: declaration of `struct type_<foo, 123>'
エラーメッセージは GCC のものです。
この方法の弱点は、型のサイズを調べることはできても、オブジェクトのサイズを調べることはできない点です。確かに、type_ クラステンプレートの第一テンプレート引数を廃止して、type_<Size> のようにすればオブジェクトでも使えますが、オブジェクト名を含むことができないので、良質なメッセージ出力が得られなくなります。
一方、OKWave の Q&A にはもう一つの解法が示されている。そして、こちらは C++ だけでなく C でも使うことができます。しかし、処理系に依存してしまう方法でもあります。
#define PRINT_SIZEOF(type) PRINT_SIZEOF_(type, __LINE__)
#define PRINT_SIZEOF_(type, line) \
void print_sizeof_##line() { \
asm volatile("size of " #type " is %0" :: "g"(sizeof(type))); \
}
PRINT_SIZEOF(struct foo)
やはり GCC の場合ですが、次のような良質なエラーメッセージが得られます。
Error: no such instruction: `size of struct foo is $123'
なお、このエラーメッセージは、コンパイルの段階ではなくアセンブルの段階で発生します。つまり、インラインアセンブラを使って、エラーメッセージのための文字列を生成しているわけですが、インラインアセンブラの書式が完全に GCC に依存してしまっています。しかも、GCC であっても、ターゲットによってはうまく働かない可能性があります。
結論としては、どんな場合にも汎用的に使える方法はないものの、使用している処理系に合わせた PRINT_SIZEOF マクロさえ用意しておけば、そのマクロ定義を置き換えるだけで何とかなりそうです。また、PRINT_SIZEOF マクロは故意にエラーを発生させてしまうので、最終的には、このマクロを無効化する仕組みも用意しておく必要があります。
Comment
おかげでちょっぴり悩んじまったじゃねぇか。ヽ(`Д´)ノ
これまでにも何度か失敗して、気付くたびに直していたのですが、今回は特に酷かったですね。
今後は、HTMLへの変換プログラムを作るなどして、対応するようにします。