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_PREPROCESSINGをNO
に設定することで、すべてのプリプロセッシングを無効にできます。上記の場合、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
など)、通常のマクロ置換(PURE
やTHIS
など)、およびプレーンな定義(__cplusplus
など)を受け入れることに注意してください。
また、プリプロセッサによって通常自動的に定義されるプリプロセッサ定義(__cplusplus
など)は、Doxygenのパーサーで手動で定義する必要があることにも注意してください(これは、これらの定義がしばしばプラットフォーム/コンパイラ固有であるためです)。
場合によっては、マクロ名や関数を別のものに置換したいが、その結果をさらにマクロ置換に晒したくないことがあります。これは、=
演算子ではなく:=
演算子を使用することで実現できます。
例として、次のコード断片があるとします。
#define QList QListT class QListT { };
この場合、DoxygenにこれをQList
クラスのクラス定義として解釈させる唯一の方法は、以下を定義することです。
PREDEFINED = QListT:=QList
以下は、Valter MinuteとReyes Ponceが提供した例で、MicrosoftのATLおよびMFCライブラリのボイラープレートコードをDoxygenが処理するのに役立ちます。
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ファイルに対してプリプロセッシングが有効になります。