型宣言ヘッダ - ヘッダファイル間の読み込みを減らす

C言語でヘッダファイル間の読み込みを減らすためのテクニックとして、型宣言ヘッダを紹介します。

型宣言ヘッダとはtypedef文を使って、構造体の別名を作成したヘッダファイルのことです。

このファイル名は「spvm_typedecl.h」とします。

型宣言ヘッダ

型宣言ヘッダで行っていることはtypedef文を使って「struct spvm_field」という構造体に「SPVM_FIELD」という型の別名をつけていることと、「struct spvm_package」という構造体に「SPVM_PACKAGE」という型の別名をつけていることです。ついでに、幅が保証された整数型を使える用にするために、「stdint.h」を読み込んでいます。NULLを使えるようにするために、stddef.hを読み込んでいます。

spvm_typedecl.h

#ifndef SPVM_TYPEDECL_H
#define SPVM_TYPEDECL_H

#include <stdint.h>
#include <stddef.h>

// spvm_field.h
struct spvm_field;
typedef struct spvm_field SPVM_FIELD;

// spvm_package.h
struct spvm_package;
typedef struct spvm_package SPVM_PACKAGE;
#endif

構造体の定義は、一度しか定義してはいけませんが、構造体の名前は、単独で宣言でき、後で、構造体の定義を書くことができます。

// 構造体の名前は先に宣言してOK
struct spvm_field;

// 構造体定義を行う前に型の別名をつける
typedef struct spvm_package SPVM_PACKAGE;

// 構造体定義
struct spvm_field {
  int32_t id;
  const char* name;
  SPVM_PACKAGE* package;
};

このことを理解しておくと、構造体の定義を行う前に、構造体の名前を宣言して、typedef文で、別名をつけることができます。

以下の部分は、インクルードガードです。

#ifndef SPVM_TYPEDECL_H
#define SPVM_TYPEDECL_H

#endif

次に「spvm_filed.h」と「spvm_package.h」という個別のヘッダファイルを見てみましょう。

個別のヘッダファイル

spvm_filed.h

「spvm_filed.h」の中では、SPVM_PACKAGEという型を参照しています。

#ifndef SPVM_FIELD_H
#define SPVM_FIELD_H

#include "spvm_typedecl.h"

struct spvm_field {
  int32_t id;
  const char* name;
  SPVM_PACKAGE* package;
};

#endif

「spvm_package.h」を取り込んでいるのではなく「spvm_typedecl.h」を取り込んでいるところに注目しましょう。

spvm_package.h

「spvm_package.h」の中では、SPVM_FIELDという型を参照しています。

#ifndef SPVM_PACKAGE_H
#define SPVM_PACKAGE_H

#include "spvm_typedecl.h"

struct spvm_package {
  int32_t id;
  const char* name;
  SPVM_FIELD* fields;
};

#endif

「spvm_field.h」を取り込んでいるのではなく「spvm_typedecl.h」を取り込んでいるところに注目しましょう。

このように、ヘッダファイルの取り込みあいをするのではなく、個別のヘッダファイルから型宣言ヘッダを読み込むことで、解決しています。

型宣言ヘッダをつかうと、コードの見通しが良くなり、循環参照の問題も気にする必要がなくなります。

関連情報