Transmettre la déclaration d’instances de structure C statiques en C ++

J’écris un générateur de code, eh bien, en fait un générateur de données qui produira des structures de données de cette forme (évidemment, les structures de données réelles sont beaucoup plus élaborées):

typedef struct Foo { int a; struct Foo* foo; } Foo; extern Foo f1; extern Foo f2; Foo f1 = {1, &f2}; Foo f2 = {2, &f1}; 

C’est portable pour tous les compilateurs C et C ++ que j’ai essayés.

Je voudrais transmettre déclarer ces instances struct comme statiques afin de ne pas polluer l’espace variable global, comme dans:

 typedef struct Foo { int a; struct Foo* foo; } Foo; static Foo f1; static Foo f2; static Foo f1 = {1, &f2}; static Foo f2 = {2, &f1}; 

Bien que cela fonctionne avec gcc et probablement tous les compilateurs C, le code ci-dessus ne fonctionne pas avec les compilateurs C ++ et entraîne une erreur de compilation:

 error: redefinition of 'Foo f1' error: 'Foo f1' previously declared 

Je comprends pourquoi cela se passe en C ++. Existe-t-il une solution de contournement simple n’impliquant pas l’utilisation de code au moment de l’exécution pour obtenir le même effet que celui transférable à tous les compilateurs C ++ sans recourir à un compilateur C pour comstackr certains fichiers?

Cela devrait comstackr avec C ou C ++ et vous donner le même nom pour accéder à la même chose dans les deux compilateurs.

 #ifdef __cplusplus namespace // use anonymous namespace to avoid poluting namespace. { struct StaticFoos { static Foo f1; static Foo f2; }; Foo StaticFoos::f1 = {1, &StaticFoos::f2}; Foo StaticFoos::f2 = {2, &StaticFoos::f1}; } static const &Foo f1 = StaticFoos::f1; static const &Foo f2 = StaticFoos::f2; #else static Foo f1 = {1, &f2_}; static Foo f2 = {1, &f1_}; #endif 

Maintenant en C et C ++, vous pouvez accéder aux fonctions f1 et f2 .

Cela semble avoir un effet similaire à la réponse de Josh, mais avec moins de complexité:

 #ifdef __cplusplus namespace { extern Foo f1; extern Foo f2; Foo f1 = {1, &f2}; Foo f2 = {2, &f1}; } #else static Foo f1; static Foo f2; Foo f1 = {1, &f2}; Foo f2 = {2, &f1}; #endif 

Lorsqu’elles sont compilées pour C ++, les définitions externes de f1 et f2 sont exposées dans le fichier object avec un symbole pouvant être lié de manière externe. Toutefois, comme ils se trouvent dans un espace de noms anonyme, les symboles sont mutilés de manière à ne pas entrer en conflit avec les symboles d’une autre unité de traduction.

Avec la magie macro, vous pouvez configurer les choses de manière à ce qu’il n’y ait qu’un seul endroit où les f1 et f2 sont déclarés et définis, mais s’ils sont générés de manière mécanique, il n’ya probablement aucune raison de le faire.

Quelque chose comme:

 #ifdef __cplusplus #define START_PRIVATES namespace { #define END_PRIVATES } #define PRIVATE extern #else #define START_PRIVATES #define END_PRIVATES #define PRIVATE static #endif START_PRIVATES PRIVATE Foo f1; PRIVATE Foo f2; Foo f1 = {1, &f2}; Foo f2 = {2, &f1}; END_PRIVATES 

Ce que vous essayez d’éviter est appelé fiasco d’ordre d’initialisation statique . Vous aurez tout intérêt à utiliser des fonctions et à initialiser les objects individuels avec des valeurs par défaut, puis à remettre en place les pointeurs de membre.

Vos exemples de code signifient des choses radicalement différentes. Vous devrez relancer. Le premier réussit parce que vous avez la définition d’un object et la déclaration d’un autre. Cela fonctionnera à la fois en C et en C ++.

 extern Foo f1; 

Ceci est une déclaration et une définition provisoire.

 static Foo f1; 

C’est la déclaration et la définition d’un object f1 de type struct Foo .

 static Foo f2; 

Idem.

 static Foo f1 = {1, &f2}; 

Ceci est une redéfinition. Vous avez enfreint la règle de définition unique qui stipule qu’il doit exister une définition exacte du symbole dans une unité de traduction. En dehors de cela, vous êtes autorisé à avoir plusieurs définitions, mais bien sûr, chaque occurrence doit avoir la même syntaxe et la même sémantique.

 static Foo f2 = {2, &f1}; 

Idem.

 extern Foo fn; /* some code */ extern Foo fn; /* some more ... */ Foo fn; /* finally a definition */ 

Cela convient car il est correct d’avoir plusieurs déclarations provisoires.

Vous ne pouvez pas transférer des objects de déclaration, mais uniquement des types. La solution externe est la solution correcte. Ou si vous devez réellement éviter la pollution globale des espaces de noms, rendez-les statiques et initialisez-les à l’aide d’une fonction que vous appelez avant toutes les autres.

EDIT: Michael Burr a mentionné la raison dans un commentaire, je pensais que je l’appendais à un post:

@dirkgently: il est valide en C parce que le standard C dit: “Dans une unité de traduction, chaque déclaration d’un identifiant avec une liaison interne dénote le même object ou la même fonction”.

C ++ n’a pas de telle règle.

MODIFIER:

Comme indiqué dans un autre post. Vous pouvez également utiliser un espace de noms anonyme pour limiter la scope de la variable. Enveloppez simplement les éléments d’espace de noms dans un #ifdef __cplusplus et vous devriez être #ifdef __cplusplus à partir.

J’ai rencontré ce problème. La ressortingction est frustrante et je ne vois aucune raison pour laquelle C ++ aurait cette incompatibilité gratuite avec C.

Ma solution consiste à utiliser des fonctions statiques – que vous pouvez transmettre déclarer – qui renvoient simplement les fonctions f1 et f2:

 typedef struct Foo { int a; struct Foo* foo; } Foo; static Foo* link_f1(); static Foo* link_f2(); static Foo f1 = {1, link_f2()}; static Foo f2 = {2, link_f1()}; static Foo* link_f1() { return &f1; } static Foo* link_f2() { return &f2; } 

Malheureusement, ce n’est pas valide en C, vous aurez donc toujours besoin d’un code différent pour C et C ++.

Je créerais deux fichiers (.cpp et .h):

code.h:

 typedef struct Foo { Foo() {} Foo(int aa, struct Foo* ff) : a(aa), foo(ff) {} int a; struct Foo* foo; } Foo; static Foo f1; static Foo f2; 

code.cpp:

 void myfun() { f1 = Foo(1, &f2); f2 = Foo(2, &f1); } 

Je préférerais également placer toutes les variables telles que f1, f2 … dans un type d’object “stockage” (de ma propre classe ou, par exemple, un conteneur STL). Ensuite, je définirais cet object comme un object statique.

Voici ce que j’ai fait dans mon projet. Au lieu d’essayer de résoudre ce problème avec un espace de nom anonyme, j’ai utilisé un espace de nom nommé.

[Et ensuite, grâce aux commentaires utiles de Matt McNabb, il s’avère qu’un espace de noms anonyme conviendrait pour une solution très soignée avec moins de macros ne générant aucune pollution de nom externe. ]

Cela me permet d’avoir deux régions distinctes de texte de programme, avec une scope de fichier normale entre les deux, pour une solution ordonnée:

Tout est caché derrière ces macros:

 #ifdef __cplusplus #define static_forward(decl) namespace { extern decl; } #define static_def(def) namespace { def; } #else #define static_forward(decl) static decl; #define static_def(def) static def; #endif 

Et alors on peut faire:

 static_forward(struct foo foo_instance) void some_function(void) { do_something_with(&foo_instance); } static_def(struct foo foo_instance = { 1, 2, 3 }) 

L’extension C est simple et ressemble à ceci:

 static struct foo foo_instance; void some_function(void) { do_something_with(&foo_instance); } static struct foo foo_instance = { 1, 2, 3 }; 

L’extension C ++ ressemble à ceci:

 namespace { extern struct foo foo_instance; } void some_function(void) { do_something_with(&foo_instance); } namespace { struct foo foo_instance = { 1, 2, 3 }; } 

En résumé, grâce aux espaces de noms anonymes, C ++ n’a pas de problème de renvoi direct statique, mais seulement le problème de son implémentation de manière incompatible avec le langage C, qui est corrigeable à l’aide de macros.

Plusieurs régions d’espaces de noms anonymes dans la même unité de traduction constituent le même espace de noms et la scope du fichier environnant en dehors de l’espace de noms est visible.