用作 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
以停用任何其他輸出)。
請注意,並非所有語言都會執行預處理。預處理適用於使用「C」掃描器(「java」、「d」和「php」除外)的檔案、Fortran 檔案(僅在副檔名包含至少一個大寫字元的情況下)和 vhdl 檔案。