4 Preprocessor

MkDoc relies on its own C preprocessor to keep track of each file inclusion, macro expansion and conditional directive.

4.1 Preprocessor context  

Declaration locations  

The preprocessor does not process all the input before feeding the parser but instead gives piece of preprocessed text acknowledged by the parser. Each piece of text fed into the parser comes with a complete preprocessing contexts stack which specify files, line numbers and macros currently being expanded, if any.

You can choose to show the declaration location in member sections by using the decl_location_sentence insert token in the document template. For instance, the following declaration:

/** A template macro */
#define TEMPLATE(t) \
\
/** Deserialisation function for @tt t type */ \
t * t##_deserialize(const char *); \
/** Serialisation function for @tt t type */ \
char * t##_serialize(const t *);

TEMPLATE(int)

generates the following member documentation for the int_serialize function:

char * int_serialize(const int *)  

This function is declared in TEMPLATE function like macro expansion, line 6 in doc/examples.h source file, line 185.

Serialisation function for int type

Macros expansion  

Documentation comments are stored in the macro contents and expanded with other content. This allows putting template documentation inside macros.

MkDoc also keeps track of which macros were expanded to produce declaration text, this allows to expose a list of involved macros in the documentation by using the decl_involved_macros_sentence insert token in the document template.

For instance, the following declaration:

/** A prototype macro */
#define PROTOTYPE(n) void (n)(const int *)
/** The TYPEDEF macro */
#define TYPEDEF typedef
/** A function typedef */
TYPEDEF PROTOTYPE(func_t);

generates the following member documentation for the func_t type:

typedef void (func_t)(const int *)   

This typedef is declared in doc/examples.h source file, line 194.

This declaration involves expansion of the TYPEDEF and PROTOTYPE macros.

A function typedef

4.2 Conditional directives  

Conditional context  

MkDoc doesn't rely on conditional directives to select the code to feed to the declaration parser. It is sometime useful to explore all conditional branches; this allows to expose alternates declarations for different branches in the documentation. The multiple_symbol_def and multiple_macro_def configuration options can be used to allow multiple declarations with the same identifier to appear in the documentation.

The decl_cpp_condition_sentence insert token can then be used to expose the preprocessor condition associated with each declaration in the documentation, as shown in the example below. Only conditions which involve visible macros are shown (see show_undoc_macros option).

For instance, these declarations:

/** A configuration macro */
#define CONFIG
/** A configuration macro */
#undef CONFIG_USE_INT

#ifdef CONFIG
# ifdef CONFIG_USE_INT
/** @alias config_number1
An integer */

int config_number;
# else
/** @alias config_number2
A short integer */

short config_number;
# endif
#endif

generate the following member documentation:

int config_number  

This variable is declared in doc/examples.h source file, line 167.

Alternate declarations with same identifier: [1], [2].

Preprocessor condition: defined( CONFIG ) and defined( CONFIG_USE_INT )

An integer

short config_number  

This variable is declared in doc/examples.h source file, line 171.

Alternate declarations with same identifier: [1], [2].

Preprocessor condition: defined( CONFIG ) and not defined( CONFIG_USE_INT )

A short integer

Branches exploration  

Two parallel stacks are used to track the current preprocessor conditional state while reading header files:

  • A first stack is used to compute conditional state as a regular C preprocessor would do. This is used to keep track of declarations conditional state and flag macro definition operations (#define and #undef) with current conditional state so that later preprocessor conditionals can perform accurate macro definition tests. Only definition tests are affected, macro definition for later expansions is not affected (see second point).

    The __MKDOC__ macro definition is always true when tested using the #ifdef and #ifndef directives.

  • A second stack is used to keep track of the explored conditional branches to take declarations and macro definitions into account. The default policy is to explore all conditional branches. This allows exposition of multiple declarations of the same identifier in the documentation.

    During exploration of the branches, the last encountered definition of a macro is used for expansion; this can be changed with the @noexpand tag.

It is possible to specify a different policy on a per-directive basis to instruct the preprocessor to feed the parser with only some portions of a header file. This is done by passing specific instructions on the conditional directive line inside comments. Available instructions are:

  • take : This is the default and instructs the preprocessor to feed this branch to the declaration parser.

  • skip : This instructs the preprocessor not to feed this branch to the parser.

  • eval : This instructs the preprocessor to use condition evaluation results to decide if this branch must be taken or ignored.

  • eval_top : Same as eval but considers evaluation result of this directive only and ignores result of nested conditionals.

  • force : This instructs the preprocessor to consider this branch even if currently inside an inactive branch.

The syntax is as shown in the following example:

#if MACRO == 42 /* mkdoc:skip */
...
#endif

The cpp_cond_policy configuration option is available to change the default policy globally.

The cpp_eval_const and cpp_eval_hidden options can be used to rely on evaluation of conditional expressions involving respectively constant expressions (ei. #if 0) and undocumented/hidden macros.

Valid XHTML 1.0 StrictGenerated by diaxen on Sun Jan 8 22:43:00 2012 using MkDoc