プリプロセッシング

Doxygenへの入力として使用されるソースファイルは、Doxygenに組み込まれたCプリプロセッサによって解析できます。

デフォルトでは、Doxygenは部分的なプリプロセッシングのみを行います。つまり、条件付きコンパイル文(#ifなど)を評価し、マクロ定義を評価しますが、マクロ展開は行いません。

したがって、次のコードフラグメントがある場合

#define VERSION 200
#define CONST_STRING const char *

#if VERSION >= 200
  static CONST_STRING version = "2.xx";
#else
  static CONST_STRING version = "1.xx";
#endif

デフォルトでは、Doxygenは以下の内容をパーサに送ります

#define VERSION
#define CONST_STRING

  static CONST_STRING version = "2.xx";

設定ファイルでENABLE_PREPROCESSINGNOに設定することで、すべてのプリプロセッシングを無効にできます。上記の場合、Doxygenは両方のステートメント、つまり以下を読み取ります。

  static CONST_STRING version = "2.xx";
  static CONST_STRING version = "1.xx";

CONST_STRINGマクロを展開したい場合は、設定ファイルでMACRO_EXPANSIONタグをYESに設定する必要があります。すると、プリプロセッシング後の結果は次のようになります。

#define VERSION
#define CONST_STRING

  static const char * version = "2.xx";

Doxygenはすべてのマクロ定義を(必要に応じて再帰的に)展開することに注意してください。これは多くの場合、やりすぎです。したがって、Doxygenは明示的に指定した定義のみを展開することもできます。このためには、EXPAND_ONLY_PREDEFタグをYESに設定し、PREDEFINEDタグまたはEXPAND_AS_DEFINEDタグの後にマクロ定義を指定する必要があります。

プリプロセッサからの何らかの助けが必要となる典型的な例は、Microsoftの言語拡張である__declspecを扱う場合です。GNUの__attribute__拡張機能も同様です。以下に例関数を示します。

extern "C" void __declspec(dllexport) ErrorMsg( String aMessage,...);

何も設定しないと、Doxygenは混乱し、__declspecを何らかの関数と見なします。Doxygenを助けるためには、通常、以下のプリプロセッサ設定を使用します。

ENABLE_PREPROCESSING   = YES
MACRO_EXPANSION        = YES
EXPAND_ONLY_PREDEF     = YES
PREDEFINED             = __declspec(x)=

これにより、Doxygenがソースコードを解析する前に__declspec(dllexport)が削除されるようにします。

同様の設定は、入力から__attribute__式を削除するために使用できます。

ENABLE_PREPROCESSING   = YES
MACRO_EXPANSION        = YES
EXPAND_ONLY_PREDEF     = YES
PREDEFINED             = __attribute__(x)=

より複雑な例として、IUnknownと呼ばれる抽象基底クラスの次の難読化されたコードフラグメントがあるとします。

/*! A reference to an IID */
#ifdef __cplusplus
#define REFIID const IID &
#else
#define REFIID const IID *
#endif


/*! The IUnknown interface */
DECLARE_INTERFACE(IUnknown)
{
  STDMETHOD(HRESULT,QueryInterface) (THIS_ REFIID iid, void **ppv) PURE;
  STDMETHOD(ULONG,AddRef) (THIS) PURE;
  STDMETHOD(ULONG,Release) (THIS) PURE;
};

マクロ展開なしではDoxygenは混乱しますが、REFIIDマクロはドキュメント化されており、ドキュメントを読むユーザーはインターフェースを実装する際にそれを使用する必要があるため、展開したくない場合があります。

設定ファイルに以下を設定することで

ENABLE_PREPROCESSING = YES
MACRO_EXPANSION      = YES
EXPAND_ONLY_PREDEF   = YES
PREDEFINED           = "DECLARE_INTERFACE(name)=class name" \
                       "STDMETHOD(result,name)=virtual result name" \
                       "PURE= = 0" \
                       THIS_= \
                       THIS= \
               __cplusplus

Doxygenのパーサーに適切な結果が送られるようにできます。

/*! A reference to an IID */
#define REFIID

/*! The IUnknown interface */
class  IUnknown
{
  virtual  HRESULT   QueryInterface ( REFIID iid, void **ppv) = 0;
  virtual  ULONG   AddRef () = 0;
  virtual  ULONG   Release () = 0;
};

PREDEFINEDタグは、関数のようなマクロ定義(DECLARE_INTERFACEなど)、通常のマクロ置換(PURETHISなど)、および単純な定義(__cplusplusなど)を受け入れることに注意してください。

また、通常プリプロセッサによって自動的に定義されるプリプロセッサ定義(__cplusplusなど)は、Doxygenのパーサーによって手動で定義する必要があることにも注意してください(これは、これらの定義がしばしばプラットフォーム/コンパイラ固有であるためです)。

場合によっては、マクロ名や関数を別のものに置換したいが、その結果をさらにマクロ置換に公開したくないことがあります。これは、=の代わりに:=演算子を使用することで実現できます。

例として、次のコードがあるとします。

#define QList QListT
class QListT
{
};

DoxygenがこれをクラスQListのクラス定義として解釈する唯一の方法は、次のように定義することです。

PREDEFINED = QListT:=QList

Valter MinuteとReyes Ponceが提供した例を以下に示します。これは、DoxygenがMicrosoftのATL & MFCライブラリの定型コードを処理するのに役立ちます。

PREDEFINED           = "DECLARE_INTERFACE(name)=class name" \
                       "STDMETHOD(result,name)=virtual result name" \
                       "PURE= = 0" \
                       THIS_= \
                       THIS= \
                       DECLARE_REGISTRY_RESOURCEID=// \
                       DECLARE_PROTECT_FINAL_CONSTRUCT=// \
                       "DECLARE_AGGREGATABLE(Class)= " \
                       "DECLARE_REGISTRY_RESOURCEID(Id)= " \
                       DECLARE_MESSAGE_MAP= \
                       BEGIN_MESSAGE_MAP=/* \
                       END_MESSAGE_MAP=*/// \
                       BEGIN_COM_MAP=/* \
                       END_COM_MAP=*/// \
                       BEGIN_PROP_MAP=/* \
                       END_PROP_MAP=*/// \
                       BEGIN_MSG_MAP=/* \
                       END_MSG_MAP=*/// \
                       BEGIN_PROPERTY_MAP=/* \
                       END_PROPERTY_MAP=*/// \
                       BEGIN_OBJECT_MAP=/* \
                       END_OBJECT_MAP()=*/// \
                       DECLARE_VIEW_STATUS=// \
                       "STDMETHOD(a)=HRESULT a" \
                       "ATL_NO_VTABLE= " \
                       "__declspec(a)= " \
                       BEGIN_CONNECTION_POINT_MAP=/* \
                       END_CONNECTION_POINT_MAP=*/// \
                       "DECLARE_DYNAMIC(class)= " \
                       "IMPLEMENT_DYNAMIC(class1, class2)= " \
                       "DECLARE_DYNCREATE(class)= " \
                       "IMPLEMENT_DYNCREATE(class1, class2)= " \
                       "IMPLEMENT_SERIAL(class1, class2, class3)= " \
                       "DECLARE_MESSAGE_MAP()= " \
                       TRY=try \
                       "CATCH_ALL(e)= catch(...)" \
                       END_CATCH_ALL= \
                       "THROW_LAST()= throw"\
                       "RUNTIME_CLASS(class)=class" \
                       "MAKEINTRESOURCE(nId)=nId" \
                       "IMPLEMENT_REGISTER(v, w, x, y, z)= " \
                       "ASSERT(x)=assert(x)" \
                       "ASSERT_VALID(x)=assert(x)" \
                       "TRACE0(x)=printf(x)" \
                       "OS_ERR(A,B)={ #A, B }" \
                       __cplusplus \
                       "DECLARE_OLECREATE(class)= " \
                       "BEGIN_DISPATCH_MAP(class1, class2)= " \
                       "BEGIN_INTERFACE_MAP(class1, class2)= " \
                       "INTERFACE_PART(class, id, name)= " \
                       "END_INTERFACE_MAP()=" \
                       "DISP_FUNCTION(class, name, function, result, id)=" \
                       "END_DISPATCH_MAP()=" \
                       "IMPLEMENT_OLECREATE2(class, name, id1, id2, id3, id4,\
                        id5, id6, id7, id8, id9, id10, id11)="

ご覧のとおり、Doxygenのプリプロセッサは非常に強力ですが、さらに柔軟性が必要な場合は、いつでも入力フィルターを作成し、INPUT_FILTERタグまたはFILTER_PATTERNSタグ(またはFILTER_SOURCE_PATTERNSタグ)の後に指定できます。
フィルターの効果が不明な場合は、次のようにDoxygenを実行できます。doxygen -d filteroutput

Doxygenのプリプロセッシングの効果が不明な場合は、次のようにDoxygenを実行できます。

  doxygen -d Preprocessor

または行番号が必要ない場合

  doxygen -d Preprocessor -d NoLineno

これにより、Doxygenはプリプロセッシングが完了した後、入力ソースを標準出力にダンプするように指示されます(ヒント:他の出力を無効にするには、設定ファイルでQUIET = YESおよびWARNINGS = NOを設定してください)。

プリプロセッシングはすべての言語に対して行われるわけではないことに注意してください。プリプロセッシングは、「C」スキャナーを使用するファイル(「java」、「d」、「php」を除く)、Fortranファイル(拡張子に少なくとも1つの大文字が含まれる場合のみ)、およびvhdlファイルに対して有効です。

次のセクションに進むか、インデックスに戻ります。