7
votes

Générer une erreur sur les définitions contradictoires avec la liaison en ligne

Je suis tombé sur un bug curieux aujourd'hui que j'avais en quelque sorte réussi à éviter jusqu'à maintenant.

file1.cpp: xxx

file2.cpp: xxx

main.cpp: xxx

sortie: xxx

parce que impression () a une liaison inline, cela ne produit aucune erreur de définition multiple (compilée avec g ++ -wall file1.cpple fichier2.cppp main.cpp ) et Le symbole dupliqué est effondré silencieusement. Le cas réel où j'ai vu cela était avec des méthodes de classe en ligne, pas des fonctions en ligne explicites, mais l'effet est identique.

Je me demande s'il y a une option de liaison ou similaire qui me permettra de produire un AVERTISSEMENT Lorsque ce type d'erreur est effectué?


1 commentaires

Je ne pense pas que cela imprimera print1 print1 . Il devrait imprimer print2 aussi.


4 Réponses :


0
votes

Je vais à l'impénie Copy-coller à partir d'un fil d'email J'ai trouvé Voici sur un sujet similaire ...

Ce n'est pas un problème de GCC.

Vous n'avez pas mentionné le système d'exploitation que vous utilisez. Je suppose que c'est Gnu / linux. Sur GNU / Linux, ou tout autre système basé sur elfe, il y a un Espace de noms globaux unique des variables globales et des fonctions. Quand deux Les bibliothèques partagées utilisent le même nom de variable global, ils se réfèrent à la même variable. Ceci est une fonctionnalité.

Si vous voulez quelque chose de différent, examinez la visibilité des symboles et scripts de version de liaison.

Il semblerait que vous puissiez pouvoir dire à la liaison / compilateur que Symbole X devrait être unique et se plaindre si ce n'est pas le cas.


0 commentaires

2
votes

It n'est pas nécessaire / mandaté qu'une erreur / avertissement soit généré, par la norme. Il s'agit d'une violation de C ++ 's une règle de définition , à cause de leur avoir Différents corps de fonction.

citant de: inline externe par défaut

Le sens de "extern inline" est que si appels vers une fonction ne sont pas généré en ligne, puis un compilateur devrait faire une seule copie de la Définition de la fonction, à partager sur tous les fichiers d'objet.

si ce qui précède se produit , le comportement de ce programme est considéré comme non défini selon la norme linguistique, mais que ni le compilateur ni La liaison est nécessaire pour donner un message de diagnostic. En pratique, cela signifie que en fonction de la façon dont la mise en œuvre fonctionne, le compilateur ou la liaison peut simplement choisir une des définitions à utiliser partout.

Le comportement est cohérent avec la définition de GCC de Lien vague ici.


1 commentaires

Merci. En regardant en arrière, j'aurais dû formuler la question mieux, mais pour clarifier - je réalise que le programme est en violation et que le comportement de la GCC est correct - je suis juste intéressé par des moyens (probablement spécifiques à une boîte à outils spécifique) de la détecter au moment de la compilation.



2
votes

Les fonctions ne sont pas une liaison interne ou dans un espace de noms anonyme, ils sont donc visiblement externes avec le même nom. Ils ont des corps différents afin que vous ayez clairement violé la règle d'une définition. À ce stade, toute spéculation sur ce qui se passera n'est pas utile car votre programme est mal formé.

Je suppose que vous avez compilé sans optimisation et que le compilateur a généré des appels de fonction au lieu de l'affranchissement réellement, et il a choisi l'un des organes à utiliser comme fonction à appeler (laissant l'autre orpheline). Maintenant, probablement si vous avez compilé avec optimisation, votre sortie attendue serait émise, mais votre programme serait toujours incorrect.

Modifier pour le commentaire: Malheureusement, il n'est pas nécessaire que les compilateurs diagnostiquent des violations de la règle d'une définition (et elles ne peuvent même pas être en mesure de détecter tous les cas). Cependant, il y a quelques points que vous pouvez faire:

  • Faites des fonctions source-privées toujours static ou dans un espace de noms anonyme (i préférez-vous l'espace de noms à des fins de regroupement logique, mais c'est bien).
  • Payer une attention particulière à inline (quel que soit l'emplacement) car ils disent explicitement le compilateur que toutes les versions seront identiques (pour des méthodes non intégrées, vous aurez probablement au moins un symbole en double. erreur de la liaison). L'approche la plus sûre ici consiste ici à éviter les fonctions en ligne des espaces de noms mondiaux (ou prendre des soins particuliers dans votre nommée). De plus, vous devez être vraiment prudent que modifier #define s ne change pas le corps de fonction (par exemple en utilisant affirmation dans une fonction inline où une utilisation a ndebug et l'autre ne pas).
  • Utilisez de manière logique des classes et des espaces de noms pour casser le code et aider à prévenir plusieurs définitions du même symbole.

1 commentaires

Oui, c'était sans optimisation. J'imagine que ce serait comme vous le décrivez avec optimisation activé (au moins avec ces petites fonctions). Je réalise que le programme est mal formé et le comportement de GCC est correct - juste intéressé par les moyens de détecter / empêcher cela.



-2
votes

Pour créer une fonction locale à un fichier .cpp (ou .c), vous devez faire cette fonction comme statique . Lorsque vous avez déclaré imprimé dans deux fichiers .CPP distincts, les deux où compilé à print_v , le deuxième symbole a été supprimé (ignoré) * par le compilateur.

Ceci est juste une hypothèse de la façon dont il a agi sur ma machine. J'ai essayé le même code sur un mac OS X Mountain Lion et j'ai eu le même résultat que vous, mais j'ai pu résoudre le problème en ajoutant statique aux deux fonctions.

* Si vous modifiez l'ordre des fichiers dans la compilation, le comportement changerait. Essayez g ++ file {2,1} .cppr main.cpp et la sortie doit être

print2

print2

sincèrement, Abdulrahman Alotaibi


0 commentaires