预处理

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 文件启用。

转到下一节或返回索引