2010-08-01-01-コンパイル時点での構成体サイズチェック - project-enigma

2010-08-01-01-コンパイル時点での構成体サイズチェック

>> Site top >> weblog >> 月別アーカイブ >> 2010年08月のlog >> 2010-08-01-01-コンパイル時点での構成体サイズチェック

最終更新日付:2014/01/02 00:00:00


コンパイル時点での構成体サイズチェック

2010 年 08 月 01 日

仕事をしていると、プライベートな開発ではあまりやらないことをやる。これもそのひとつだ。ある決まった長さのベタ詰めの文字列が何処かから飛んできて、それをさらに決まった部分文字列に分けて構造体のように扱う必要がある。大抵の場合、char 型配列のメンバがずらりと並んだ構造体を用意して、あたえられた文字列の先頭ポインタをキャストして使うことになるだろう。以下のように。

  struct Record {
      char item1[16];
      char item2[8];
      char item3[24];
      char item4[8];
  };
    
  void foo( const char* pData ) {
      const Record* pRecord = (const Record*)pData;
      // :
      // :
      // :
  }

ここで問題になるのは、「何処かから飛んでくる」データの長さだ。上の Record 構造体のサイズはそれに合わせておく必要があるが、こういうのは変動したりするし、そうでなくても人間の目でチェックするのは面倒だしミスの元にもなる。機械的なチェックがしたいわけだ。

最初に考えつくのは、assert を使用したものだ。ベタ詰めの文字列の長さが 56 と決まっているのであれば、例えば以下のように。

 assert( sizeof(Record) == 56 );

ちなみに、文字列長を strlen で調べて構造体サイズとの一致を assert でチェックするのもあるが、これはどちらかというと文字列長のチェックだ(pDataがNULL終端しているという前提)。

 void foo( const char* pData ) {
     assert( sizeof(Record) == strlen( pData ) );
     const Record* pRecord = (const Record*)pData;
 }

そして、これらは実行時チェックだ。そこが問題である。ランタイムに依存しないチェックが実行時まで持ち越されるのはおかしい。というわけで次に思いついたのは以下のような間違った(実際には動作しない)チェック。

 #if sizeof(Record) != 56
   #error "size mismatch!"
 #endif

これはコンパイラではなくプリプロセッサが処理するものだから、そもそも sizeof 演算子が使えるわけがない。ここに至って、コンパイル時点でのチェックを C 言語でやる方法を自分は知らないことに気付く。

そこで、C++ 前提でテンプレートを使う方法を考えてみた。まず、記述は以下のようになる。コンパイラがこのインスタンス化に失敗すればコンパイルエラーにできる。

 SizeChecker<Record, 56> recordSizeCheck;

で、SizeChecker クラステンプレートは以下のようにする。同様にクラステンプレートである SizeCheckerImp からの派生とし、与えられた構成体の実サイズと想定サイズの差をテンプレートパラメータとして与えているわけだ。

 template <typename STRUCT, int SIZE>
 class SizeChecker : public SizeCheckerImp<sizeof(STRUCT)-SIZE> {
 };

これで、SizeCheckerImp<0> からの派生の場合のみコンパイルに成功すれば良い。そこで、以下のようにした。

 template <int N> class SizeCheckerImp {
 private:
 	SizeCheckerImp( ) { };
 };
 template <> class SizeCheckerImp<0> { };

SizeCheckerImp の本体はコンストラクタを private にし、特殊化でテンプレートパラメータが0の場合だけ別扱いとする。こうすれば、SizeChecker テンプレートに与えた構造体のサイズが想定サイズでない場合にのみコンパイルエラーになる。

ここではクラステンプレートを2つ使ったが、できれば1つにしたいし、そもそも C言語で簡単にやれる方が良いが今のところ思いつかない。とりあえずは自分の目的は達成できたので、ひとまず良しとしよう。

 

コメント

このページにコメントする

 

このページのタグ

Page tag : 開発

 

 


Copyright(C) 2005-2017 project-enigma.
Generated by CL-PREFAB.