82 votes

Pourquoi #pragma once n'est-il pas automatiquement assumé ?

Quel est l'intérêt de dire spécifiquement au compilateur d'inclure le fichier une seule fois ? Cela ne serait-il pas logique par défaut ? Y a-t-il même une raison d'inclure un seul fichier plusieurs fois ? Pourquoi ne pas simplement le supposer ? S'agit-il d'un matériel spécifique ?

24 votes

Y a-t-il une raison d'inclure un seul fichier plusieurs fois ? \=> Cela pourrait être le cas. Un fichier peut avoir une compilation conditionnelle #ifdefs en elle. On peut donc dire #define MODE_ONE 1 et ensuite #include "has-modes.h" et ensuite #undef MODE_ONE con #define MODE_TWO 1 y #include "has-modes.h" encore. Le préprocesseur est agnostique par rapport à ce genre de choses, et parfois elles peuvent avoir un sens.

66 votes

Il serait logique qu'il s'agisse de la valeur par défaut. Mais pas celui qu'ils ont choisi lorsque les programmeurs C montaient encore à cheval, portaient des armes et avaient 16 Ko de mémoire.

1 votes

@FrançoisAndrieux mais le duplicata semble demander la même chose sur les gardes d'inclusion en général pas seulement #pragma une fois.

86voto

Max Langhof Points 19174

Il y a plusieurs questions connexes ici :

  • Pourquoi est-ce que #pragma once ne sont pas automatiquement appliquées ?
    Parce qu'il y a des situations dans lesquelles vous voulez inclure des fichiers plus d'une fois.

  • Pourquoi voudriez-vous inclure un fichier plusieurs fois ?
    Plusieurs raisons ont été données dans d'autres réponses (Boost.Preprocessor, X-Macros, inclusion de fichiers de données). Je voudrais ajouter un exemple particulier de "éviter la duplication du code" : OpenFOAM encourage un style où #include L'intégration de bits et de pièces dans les fonctions est un concept commun. Voir par exemple este discussion.

  • D'accord, mais pourquoi n'est-ce pas le cas par défaut avec un opt-out ?
    Parce que ce n'est pas réellement spécifié par la norme. #pragma sont par définition des extensions spécifiques à l'implémentation.

  • Pourquoi l #pragma once n'est pas encore devenue une fonctionnalité standardisée (car elle est largement supportée) ?
    Parce que déterminer ce qu'est "le même fichier" d'une manière indépendante de la plate-forme est en fait étonnamment difficile. Voir cette réponse pour plus d'informations .

38voto

Jesper Juhl Points 14756

Vous pouvez utiliser #include partout dans un fichier, et pas seulement à l'échelle globale - comme à l'intérieur d'une fonction (et plusieurs fois si nécessaire). Bien sûr, c'est laid et ce n'est pas un bon style, mais c'est possible et parfois raisonnable (selon le fichier que vous incluez). Si #include n'était qu'une chose ponctuelle, alors ça ne marcherait pas. #include ne fait qu'une simple substitution de texte (couper-coller) après tout, et tout ce que vous incluez ne doit pas nécessairement être un fichier d'en-tête. Vous pouvez, par exemple #include un fichier contenant des données générées automatiquement et contenant les données brutes pour initialiser une std::vector . Comme

std::vector<int> data = {
#include "my_generated_data.txt"
}

Et que "mes_données_générées.txt" soit quelque chose généré par le système de construction pendant la compilation.

Ou peut-être que je suis paresseux/stupide et que je mets cela dans un fichier ( muy exemple artificiel) :

const noexcept;

et ensuite je fais

class foo {
    void f1()
    #include "stupid.file"
    int f2(int)
    #include "stupid.file"
};

Un autre exemple, un peu moins artificiel, serait un fichier source où de nombreuses fonctions doivent utiliser un grand nombre de types dans un espace de noms, mais vous ne voulez pas simplement dire using namespace foo; globalement, car cela polluerait l'espace de noms global avec un tas d'autres choses que vous ne voulez pas. Vous créez donc un fichier "foo" contenant

using std::vector;
using std::array;
using std::rotate;
... You get the idea ...

Et ensuite vous faites ceci dans votre fichier source

void f1() {
#include "foo" // needs "stuff"
}

void f2() {
    // Doesn't need "stuff"
}

void f3() {
#include "foo" // also needs "stuff"
}

Note : Je ne préconise pas de faire des choses comme ça. Mais c'est possible et fait dans certaines bases de code et je ne vois pas pourquoi cela ne devrait pas être autorisé. Il hace a son utilité.

Il se peut également que le fichier que vous incluez se comporte différemment selon la valeur de certaines macros ( #define s). Il se peut donc que vous souhaitiez inclure le fichier à plusieurs endroits, après avoir d'abord modifié une valeur, afin d'obtenir un comportement différent dans différentes parties de votre fichier source.

27voto

ThorX89 Points 967

L'inclusion de plusieurs fois est utilisable, par exemple, avec l'option X-macro technique :

data.inc :

X(ONE)
X(TWO)
X(THREE)

use_data_inc_twice.c

enum data_e { 
#define X(V) V,
   #include "data.inc"
#undef X
};
char const* data_e__strings[]={
#define X(V) [V]=#V,
   #include "data.inc"
#undef X
};

Je ne connais pas d'autre utilisation.

0 votes

Cela semble trop complexe. Y a-t-il une raison de ne pas inclure ces définitions dans le fichier dès le départ ?

2 votes

@JohnnyCache : L'exemple est une version simplifiée de la façon dont les macros X fonctionnent. Veuillez lire le lien ; elles sont extrêmement utiles dans certains cas pour effectuer des manipulations complexes de données tabulaires. Dans toute utilisation significative des macros X, il n'y aurait aucun moyen de simplement "inclure ces définitions dans le fichier".

2 votes

@Johnny - oui - une bonne raison est d'assurer la cohérence (difficile à faire à la main lorsque vous avez seulement quelques dizaines d'éléments, sans parler des centaines).

21voto

T.E.D. Points 26829

Vous semblez partir du principe que le but de la fonction "#include", même si elle existe dans le langage, est de fournir un support pour la décomposition des programmes en plusieurs unités de compilation. C'est faux.

Il peut remplir ce rôle, mais ce n'était pas son but premier. C était développé à l'origine comme langage de niveau légèrement supérieur à celui du PDP-11 Macro-11 Assemblage pour la réimplémentation d'Unix. Il avait un préprocesseur de macro parce que c'était une fonctionnalité de Macro-11. Il avait la capacité d'inclure textuellement des macros d'un autre fichier parce que c'était une fonctionnalité de Macro-11 que l'Unix existant qu'ils portaient vers leur nouveau compilateur C avait utilisé.

Il s'avère que "#include" est utile pour séparer le code en unités de compilation, comme étant (sans doute) un peu un hack. Cependant, le fait que ce hack existait signifie qu'il est devenu "The Way that is done in C". Le fait qu'un moyen existait signifie qu'aucune nouvelle méthode n'a jamais été utilisée. nécessaire à créer pour fournir cette fonctionnalité, de sorte que rien de plus sûr (par exemple : non vulnérable à l'inclusion multiple) n'a jamais été créé. Puisque c'était déjà dans le C, cela a été copié dans le C++ avec la plupart du reste de la syntaxe et des idiomes du C.

Il y a une proposition pour donner à C++ un système de modules approprié afin de pouvoir enfin se passer de ce vieux préprocesseur de 45 ans. Je ne sais pas si c'est vraiment imminent. J'ai entendu dire que c'était en préparation depuis plus de dix ans.

10voto

SergeyA Points 2159

Non, cela entraverait considérablement les options disponibles, par exemple, pour les auteurs de bibliothèques. Par exemple, Boost.Preprocessor permet d'utiliser des boucles de préprocesseur, et le seul moyen d'y parvenir est d'inclure plusieurs fois le même fichier.

Et Boost.Preprocessor est un bloc de construction pour de nombreuses bibliothèques très utiles.

1 votes

Cela n'empêcherait pas tout de cela. L'OP a demandé à propos d'un par défaut un comportement, pas un comportement immuable. Il serait tout à fait judicieux de modifier la valeur par défaut et de fournir à la place un drapeau de préprocesseur #pragma reentrant ou quelque chose de ce genre. Le recul est de 20/20.

0 votes

Cela l'entraverait dans le sens où cela obligerait les gens à mettre à jour leurs bibliothèques et leurs dépendances, @KonradRudolph. Ce n'est pas toujours un problème, mais cela pourrait causer des problèmes avec certains programmes existants. Idéalement, il y aurait également un commutateur de ligne de commande pour spécifier si le défaut est once o reentrant pour atténuer ce problème ou d'autres problèmes potentiels.

1 votes

@JustinTime Comme le dit mon commentaire, il est clair que ce n'est pas un changement rétrocompatible (et donc faisable). La question, cependant, était de savoir pourquoi c'était initialement conçu de cette façon, pas pourquoi il n'a pas été changé. Et la réponse à cette question est, sans ambiguïté, que la conception originale était une énorme erreur aux conséquences considérables.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X