Prétraitement
Les fichiers sources utilisés comme entrée dans Doxygen peuvent être analysés par le préprocesseur C intégré de Doxygen.
Par défaut, Doxygen n'effectue qu'un prétraitement partiel. Autrement dit, il évalue les instructions de compilation conditionnelle (comme #if) et évalue les définitions de macro, mais il n'effectue pas d'expansion de macro.
Donc, si vous avez le fragment de code suivant :
#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
alors, par défaut, Doxygen transmettra les éléments suivants à son analyseur :
#define VERSION
#define CONST_STRING
static CONST_STRING version = "2.xx";
Vous pouvez désactiver tout prétraitement en définissant ENABLE_PREPROCESSING sur NO dans le fichier de configuration. Dans le cas ci-dessus, Doxygen lira alors les deux instructions, c'est-à-dire :
static CONST_STRING version = "2.xx";
static CONST_STRING version = "1.xx";
Si vous souhaitez étendre la macro CONST_STRING, vous devez définir la balise MACRO_EXPANSION dans le fichier de configuration sur YES. Le résultat après le prétraitement devient alors :
#define VERSION
#define CONST_STRING
static const char * version = "2.xx";
Notez que Doxygen va maintenant étendre toutes les définitions de macro (de manière récursive si nécessaire). C'est souvent trop. Par conséquent, Doxygen vous permet également d'étendre uniquement les définitions que vous spécifiez explicitement. Pour cela, vous devez définir la balise EXPAND_ONLY_PREDEF sur YES et spécifier les définitions de macro après la balise PREDEFINED ou EXPAND_AS_DEFINED.
Un exemple typique où l'aide du préprocesseur est nécessaire est lorsqu'il s'agit de l'extension de langage de Microsoft : __declspec. Il en va de même pour l'extension __attribute__ de GNU. Voici un exemple de fonction.
extern "C" void __declspec(dllexport) ErrorMsg( String aMessage,...);
Si rien n'est fait, Doxygen sera confus et verra __declspec comme une sorte de fonction. Pour aider Doxygen, on utilise généralement les paramètres de préprocesseur suivants :
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
PREDEFINED = __declspec(x)=
Cela permettra de s'assurer que __declspec(dllexport) est supprimé avant que Doxygen n'analyse le code source.
Des paramètres similaires peuvent être utilisés pour supprimer les expressions __attribute__ de l'entrée :
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
PREDEFINED = __attribute__(x)=
Pour un exemple plus complexe, supposons que vous ayez le fragment de code obscurci suivant d'une classe de base abstraite appelée IUnknown :
/*! Une référence à un IID */
#ifdef __cplusplus
#define REFIID const IID &
#else
#define REFIID const IID *
#endif
/*! L'interface IUnknown */
DECLARE_INTERFACE(IUnknown)
{
STDMETHOD(HRESULT,QueryInterface) (THIS_ REFIID iid, void **ppv) PURE;
STDMETHOD(ULONG,AddRef) (THIS) PURE;
STDMETHOD(ULONG,Release) (THIS) PURE;
};
sans extension de macro, Doxygen sera confus, mais nous ne voudrons peut-être pas étendre la macro REFIID, car elle est documentée et l'utilisateur qui lit la documentation doit l'utiliser lors de l'implémentation de l'interface.
En définissant ce qui suit dans le fichier de configuration :
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
nous pouvons nous assurer que le résultat approprié est transmis à l'analyseur de Doxygen :
/*! Une référence à un IID */
#define REFIID
/*! L'interface IUnknown */
class IUnknown
{
virtual HRESULT QueryInterface ( REFIID iid, void **ppv) = 0;
virtual ULONG AddRef () = 0;
virtual ULONG Release () = 0;
};
Notez que la balise PREDEFINED accepte des définitions de macros de type fonction (comme DECLARE_INTERFACE ), des substitutions de macros normales (comme PURE et THIS) et des définitions simples (comme __cplusplus).
Notez également que les définitions de préprocesseur qui sont normalement définies automatiquement par le préprocesseur (comme __cplusplus), doivent être définies à la main avec l'analyseur de Doxygen (ceci est fait parce que ces définitions sont souvent spécifiques à la plate-forme/au compilateur).
Dans certains cas, vous souhaiterez peut-être remplacer un nom de macro ou une fonction par autre chose sans exposer le résultat à une substitution de macro supplémentaire. Vous pouvez le faire mais en utilisant l'opérateur := au lieu de =.
A titre d'exemple, supposons que nous ayons le morceau de code suivant :
#define QList QListT
class QListT
{
};
La seule façon pour que Doxygen interprète ceci comme une définition de classe pour la classe QList est de définir :
PREDEFINED = QListT:=QList
Voici un exemple fourni par Valter Minute et Reyes Ponce qui aide Doxygen à parcourir le code standard des bibliothèques ATL et MFC de Microsoft :
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(classe)= " \
"IMPLEMENT_DYNAMIC(classe1, classe2)= " \
"DECLARE_DYNCREATE(classe)= " \
"IMPLEMENT_DYNCREATE(classe1, classe2)= " \
"IMPLEMENT_SERIAL(classe1, classe2, classe3)= " \
"DECLARE_MESSAGE_MAP()= " \
TRY=essayer \
"CATCH_ALL(e)= catch(...)" \
END_CATCH_ALL= \
"THROW_LAST()= throw" \
"RUNTIME_CLASS(classe)=classe" \
"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(classe)= " \
"BEGIN_DISPATCH_MAP(classe1, classe2)= " \
"BEGIN_INTERFACE_MAP(classe1, classe2)= " \
"INTERFACE_PART(classe, id, nom)= " \
"END_INTERFACE_MAP()=" \
"DISP_FUNCTION(classe, nom, fonction, résultat, id)=" \
"END_DISPATCH_MAP()=" \
"IMPLEMENT_OLECREATE2(classe, nom, id1, id2, id3, id4,\
id5, id6, id7, id8, id9, id10, id11)="
Comme vous pouvez le voir, le préprocesseur de Doxygen est assez puissant, mais si vous voulez encore plus de flexibilité, vous pouvez toujours écrire un filtre d'entrée et le spécifier après la balise INPUT_FILTER ou la balise FILTER_PATTERNS (ou la balise FILTER_SOURCE_PATTERNS).
Si vous n'êtes pas sûr de l'effet du filtre, vous pouvez exécuter Doxygen comme suit : doxygen -d filteroutput.
Si vous n'êtes pas sûr de l'effet du prétraitement de Doxygen, vous pouvez exécuter Doxygen comme suit :
doxygen -d Preprocessor
ou lorsque les numéros de ligne ne sont pas souhaités :
doxygen -d Preprocessor -d NoLineno
Cela demandera à Doxygen de vider les sources d'entrée sur la sortie standard une fois le prétraitement effectué (Astuce : définissez QUIET = YES et WARNINGS = NO dans le fichier de configuration pour désactiver toute autre sortie).
Notez que le prétraitement n'est pas effectué pour tous les langages. Le prétraitement est activé pour les fichiers qui utilisent l'analyseur « C » (à l'exception de « java », « d» et « php »), les fichiers Fortran (uniquement si l'extension contient au moins un caractère majuscule) et les fichiers vhdl.