Wednesday, January 18, 2006

Programming::C++ - 二つのインクルードガード

C++だけでなく、当然ながらCでも使うインクルードガード。関数やクラスの定義を二回以上行われないようにするためのテクニックですね。例えば以下のようなコードでは関数が二回定義されてしまいますので、インクルードガードします。

// hoge.h

// ifndef, define, endifディレクティブでhogeが一回だけ定義されるようにする
#ifndef __INCLUDED_HOGE_H
#define __INCLUDED_HOGE_H

void hoge(int);

#endif // __INCLUDED_HOGE_H

// foo.h
#ifndef __INCLUDED_FOO_H
#define __INCLUDED_FOO_H

#include "hoge.h"
void foo(int); // 関数内でhoge()を呼び出す

#endif // __INCLUDED_FOO_H

// bar.c

#include "hoge.h"
#include "foo.h"

void bar();

void bar()
{
...
}

この手法の名前、正確には二重インクルードガードというのですね(http://www.02.246.ne.jp/~torutk/cxx/file/includeguard.html)。

ところで、大きなプログラムを作成しているとヘッダがモリモリ増えていきます。C++だとテンプレートを使ったクラスを作成しているとヘッダのサイズもバカになりません(さらに、doxygenでドキュメントを生成できるようにコメントを書いているので余計です)。二重インクルードガードだと複数回の定義は回避できますが、どのみちincludeディレクティブによりヘッダファイルの読み込みが発生します。この読み込みは明らかに冗長ですね。何せ必要ないヘッダを読み込んでいるわけですから。

この冗長なインクルードを回避するためには、上記コードを次のように改良します。


// hoge.h

#ifndef __INCLUDED_HOGE_H
#define __INCLUDED_HOGE_H

void hoge(int);

#endif // __INCLUDED_HOGE_H

// foo.h
#ifndef __INCLUDED_FOO_H
#define __INCLUDED_FOO_H

#ifndef __INCLUDED_HOGE_H // ここのifndefディレクティブを追加する
#include "hoge.h"
#endif // __INCLUDED_HOGE_H

void foo(int); // 関数内でhoge()を呼び出す

#endif // __INCLUDED_FOO_H

// bar.c

#include "hoge.h"
#include "foo.h"

void bar();

void bar()
{
...
}

この変更を行う前では、bar.cに置いてhoge.hが読み込まれ、foo.hが読み込まれ、さらにfoo.hの中でhoge.hをincludeしているため再びhoge.hが読み込まれます。二回hoge.hを読み込んでいますね。この変更により、hoge.h内の"__INCLUDE_HOGE_H"のおかげで、foo.h内の"#include "hoge.h""をスキップすることが出来ます。これを冗長インクルードガードというのですね。

冗長インクルードガードをしていなかったため、VCでのコンパイルでは時間が掛かって仕方有りませんでした。冗長インクルードガードを施すことで、コンパイル時間は改善されました。ちなみにgccはこの冗長インクルードガードの手法をこちらが使わなくても、勝手に同様のことをしてくれるようですね。

No comments: