Pragma once
Dans les langages de programmation C et C++, #pragma once
est une directive de préprocesseur non standard mais largement prise en charge, conçue pour que le fichier source actuel ne soit inclus qu'une seule fois dans une seule compilation[1]. Ainsi, #pragma once
a le même objectif que include guards, mais avec plusieurs avantages, notamment : moins de code, évitement des conflits de noms et parfois une amélioration de la vitesse de compilation[2]. D'autre part, #pragma once
n'est pas nécessairement disponible dans tous les compilateurs et son implémentation est délicate et peut ne pas toujours être fiable.
Exemple
- Fichier "grandparent.h"
#pragma once
struct foo
{
int membre;
};
- Fichier "parent.h"
#include "grandparent.h"
- Fichier "enfant.c"
#include "grandparent.h"
#include "parent.h"
Dans cet exemple, l'inclusion de grandparent.h
à la fois dans parent.h
et enfant.c
provoquerait normalement une erreur de compilation, car une structure avec un nom donné ne peut être définie qu'une seule fois dans une compilation donnée. La directive #pragma once
sert à éviter cela en ignorant les inclusions ultérieures de grandparent.h
.
Avantages
L'utilisation de #pragma once
permet au préprocesseur C d'inclure un fichier d'en-tête lorsque cela est nécessaire et d'ignorer une directive #include
dans le cas contraire. Cela a pour effet de modifier le comportement du préprocesseur C lui-même et permet aux programmeurs d'exprimer les dépendances de fichiers de manière simple, évitant ainsi le besoin d'une gestion manuelle.
L'alternative la plus courante à #pragma once
consiste à utiliser #define
pour définir une macro de protection #include, dont le nom est choisi par le programmeur pour être unique dans ce fichier. Par exemple,
#ifndef GRANDPARENT_H
#define GRANDPARENT_H
... contenu de grandparent.h
#endif /* !GRANDPARENT_H */
Cette approche garantit au minimum que le contenu du fichier inclus n'est pas vu plus d'une fois. Ceci est plus verbeux, nécessite une plus grande intervention manuelle et est sujet aux erreurs du programmeur car il n'y a pas de mécanismes disponibles pour le compilateur pour empêcher l'utilisation accidentelle du même nom de macro dans plus d'un fichier, ce qui entraînerait un seul des fichiers étant inclus. Il est peu probable que de telles erreurs passent inaperçues, mais elles peuvent compliquer l'interprétation d'un rapport d'erreurs du compilateur. Étant donné que le pré-processeur lui-même est responsable de la gestion de #pragma once
, le programmeur ne peut pas faire d'erreurs qui provoquent des conflits de noms.
En l'absence de protections #include autour des directives #include
, l'utilisation de #pragma once
améliorera la vitesse de compilation pour certains compilateurs puisqu'il s'agit d'un mécanisme de niveau supérieur ; le compilateur lui-même peut comparer des noms de fichiers ou des inodes sans avoir à invoquer le préprocesseur C pour analyser l'en-tête à la recherche de #ifndef
et #endif
. Pourtant, étant donné que les gardes d'inclusion apparaissent très souvent et que la surcharge d'ouverture des fichiers est importante, il est courant que les compilateurs optimisent la gestion des gardes d'inclusion, les rendant aussi rapides que #pragma once
[3] - [4] - [5].
Mises en garde
L'identification du même fichier sur un système de fichiers n'est pas une tâche triviale. Les liens symboliques et surtout les liens physiques peuvent faire en sorte qu'un même fichier se retrouve sous des noms différents dans des répertoires différents. Les compilateurs peuvent utiliser une heuristique qui compare la taille du fichier, l'heure de modification et le contenu[6]. De plus, #pragma once
peut faire quelque chose de mal si le même fichier est intentionnellement copié dans plusieurs parties d'un projet, par exemple lors de la préparation de la construction. Alors que les gardes d'inclusion protégeraient toujours des doubles définitions, #pragma once
peut ou non les traiter comme le même fichier d'une manière dépendante du compilateur. Ces difficultés, ainsi que les difficultés liées à la définition de ce qui constitue un même fichier en présence de liens physiques, de systèmes de fichiers en réseau, etc. ont jusqu'à présent empêché la normalisation de #pragma once
.
L'utilisation de macros #include guard permet au code dépendant de reconnaître et de répondre à de légères différences de sémantique ou d'interfaces d'alternatives concurrentes. Par exemple,
#include TLS_API_MACRO /* défini sur la ligne de commande */
...
#if defined TLS_A_H
... utiliser une API connue
#elif defined TLS_B_H
... utiliser une autre API connue
#else
#error "API TLS non reconnue"
#endif
Dans ce cas, la détermination directe pour laquelle l'API est disponible utiliserait le fait que le fichier inclus s'est annoncé lui-même avec sa macro #include guard .
La directive #include
est définie pour représenter l'intention d'un programmeur d'inclure réellement le texte d'un fichier au point de la directive. Cela peut se produire plusieurs fois dans une seule unité de compilation et est utile pour évaluer plusieurs fois le contenu contenant une macro par rapport aux définitions changeantes de la macro.
L'utilisation de #pragma once
, comme l'utilisation des macros de protection #include dans un fichier d'inclusion place la responsabilité sur ses auteurs afin de se protéger contre les inclusions multiples indésirables. Une dépendance excessive à l'un ou l'autre mécanisme de la part des programmeurs par l'utilisation directe et non protégée des directives #include
sans leur propre garde #include entraînera un échec lors de l'utilisation d'un fichier d'inclusion qui ne s'est pas protégé avec l'un ou l'autre des mécanismes.
Références
- « once », Microsoft Docs, (consulté le )
- « Games from Within: Even More Experiments with Includes » [archive du ], (consulté le )
- « The C Preprocessor: 1. The C Preprocessor », Gcc.gnu.org, (consulté le )
- « "Clang" CFE Internals Manual — Clang 3.4 documentation », Clang.llvm.org (consulté le )
- « clang: File manipulation routines », Clang.llvm.org (consulté le )
- « should_stack_file() function in GCC source code »