プリプロセッシング

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を設定して、他の出力を無効にします)。

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

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