预处理

用作 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 文件,启用预处理。

转到下一部分或返回索引