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によって提供された例で、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ファイルに対して有効になっています。