預處理

用作 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,並在 PREDEFINEDEXPAND_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 = YESWARNINGS = NO 以停用任何其他輸出)。

請注意,並非所有語言都會執行預處理。預處理適用於使用「C」掃描器(「java」、「d」和「php」除外)的檔案、Fortran 檔案(僅在副檔名包含至少一個大寫字元的情況下)和 vhdl 檔案。

前往下一節或返回索引