Pour l'instant j'ai ce message d'erreur à la ligne 5 du .cpp : "P:\_PROGRAMMATION\Projets\Animation\src\TextureManager.cpp|5|multiple definition of `textureTantumStanding'|".
Ce message d'erreur se répète sur chaque ligne qui suit aussi.
Les externc'est à mettre dans le fichier .h (on dit que ça existe quelque part). Sans les externc'est dans le fichier .cpp (on défini une unique fois les données)
Mais le mieux est de mettre les textures dans le TextureManager, même si un Singleton n'est pas parfait, on évite d'avoir des objets globaux. D'où dans le fichier .h :
Dans le .cpp, il faut juste définir la fonction void TextureManager::loadAllStruture().
Et les textures s'utilisent par TextureManager::Walking(), ... L'initialisation des textures sera faite automatiquement au moment de la première utilisation de l'une de ces fonctions.
Bon, visiblement, il va y avoir de la place pour le progres... Essentiellement, en ce qui concerne la manière de travailler du compilateur.
Commençons donc par le commencement:
Ce que l'on appelle la "compilation" (la génération d'un programme exécutable) est en réalité composé de deux étapes majeurs:
La compilation proprement dite, par laquelle le compilateur va utiliser le code que tu as écrit pour générer des "fichiers objets" contenant le code binaire exécutable et
l'édition de liens, par laquelle tous les fichiers objets seront regroupés en un seul fichier et qui permettra aussi de faire coincider l'appel aux différentes fonctions avec l'adresse à laquelle ces fonctions se trouvent dans le programme final
(je simplifie, mais le principe est globalement correct )
Ce quil faut garder en mémoire au sujet de ces deux étapes majeures, c'est
qu'elles sont toutes les deux exécutées par des outils différents (le compilateur d'une part et l'éditeur de liens d'autre part) et
que les différents outils, n'étant jamais que des programmes eux aussi, n'ont qu'une tolérance très limitée faces aux différentes erreurs qui sont commises.
Poursuivons donc en nous intéressant à la compilation:
Lors de la compilation, le compilateur va travailler sur la base de ce que l'on appelle une "unité de compilation". Pour faire simple, il s'agit du code du fichier d'implémentation (*.cpp) ainsi que du code ajouté par tous les fichiers d'en-têtes (*.h / *.hpp) qui ont été inclus de manière directe ou indirecte.
qui se trouve dans un fichier d'en-tête va donc se retrouver dans ... l'ensemble des unités de compilation correspondant au fichiers d'implémentation dans lequel ton fichier d'en-tête est inclus.
Pour que tu puisse comprendre plus clairement, si texturemanager.h est inclus dans les fichier a.cpp, b.cpp et c.cpp, ces six lignes de code vont -- forcément -- se retrouver dans les fichiers objets correspondant (a.o, b.o et c.o / a.obj, b.obj et c.obj en fonction du compilateur).
Or, ces six lignes correspondent chacune à la déclaration d'une variable globale et, comme le compilateur oublie de manière systématique ce qu'il a pu faire lors du traitement d'une unité de compilation au moment de commencer à en traiter une autre, hé bien, il va -- forcément -- créer le code définissant ces six variables dans ... les trois unités de compilation correspondant aux trois fichier d'implémentation dans lesquels ton fichier d'en-tête est inclus.
Et, bien sur, après avoir généré les trois fichiers objets, le compilateur va donner la main à l'éditeur de liens. Et les problèmes vont commencer...
Car l'éditeur de liens va, comme je te l'ai dit plus haut, se contenter de "regrouper" le contenu des différents objets, sans essayer de les modifier d'une quelconque manière.
En gros, c'est comme s'il suivait une logique proche de
je prend le contenu de a.o(bj)
je cherche le dernier caractère de ce fichier
j'ajoute le contenu de b.o(bj) après le dernier caractère de a.o(bj)
je cherche le dernier caractère de ce fichier
j'ajoute le contenu de c.o(bj) après le dernier caractère de b.o(bj)
Et ce n'est qu'une fois qu'il a mis tous ces fichiers ensemble qu'il va passer à la deuxième étape de son travail. En gros, et pour faire simple, il va chercher chaque accès à une variable ou à une fonction, et remplacer le nom de la variable ou de la fonction par ... "l'offet"( AKA l'adresse) à laquelle la variable ou la fonction se trouve dans son "mega fichier".
Bien sur, dans la situation que je viens de décrire, il va avoir un gros problème: s'il cherche l'une des six textures que ton code définis, il va systématiquement trouver ... trois adresses qui y correspondent.
Et du coup, il ne saura pas quelle adresse choisir. Il n'aura donc pas d'autre choix que de t'afficher un message d'erreur (celui que tu lis dans ton EDI) avant ... d'abandonner le travail.
C'est là que le mot clé extern entre en jeu. Ce mot clé nous permet de dire quelque chose comme
Ne perd pas ton temps à générer le code correspondant à cette variable, car je peux te garantir qu'elle existe "quelque par ailleurs"
au compilateur.
Et le compilateur, en brave petit soldat, va suivre l'ordre à la lettre.
Seulement, pour que ce mot clé fonctionne, il faut qu'il soit placé ... dans le fichier qui sera inclus, de manière directe ou indirecte, dans l'ensemble des unités de compilation, alors que la "vraie" déclaration des variable, celle qui incitera le compilateur à effectivement générer le code pour les différentes variables globales, ne peut apparaitre que dans une et une seule unité de compilation.
Pour que ton code puisse être correct, il faut donc que le mot clé extern apparaisse dans le fichier d'en-tête sous une forme proche de
PS: Les variables globales posent, bien souvent beaucoup plus de problèmes qu'elles ne permettent d'en résoudre, pout toute une série de raisons que la longueur de cette intervention m'incite à ne pas citer ici.
AMHA, tu devrais vraiment envisager de revoir très sérieusement ta conception, et ce, d'autant plus que les textures n'auront -- a priori -- du sens qu'au niveau de l'affichage des différents éléments de ton programme
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
extern devant une variable est une déclaration de variable, sinon c'est une définition.
Cette déclaration est l'équivalent d'une déclaration de fonction (on peut ajouter extern devant une déclaration de fonction si ça nous chante, mais c'est implicite en quelque sorte).
Et où met on ses déclarations (fonction, classe ou variable) avec un linkage externe? Dans un header. Une déclaration n'est qu'une promesse faite au compilateur que la définition se trouvera quelque part au moment de l'édition des liens: dans une librairie partagée, ou un fichier objet ou dans la même unité de compilation, mais une et une seule fois dans la globalité de tout ce fourbi.
On peut aussi parfois mettre extern dans un cpp si on veut à partir d'une unité de compilation aller taper un symbole privé d'une librairie qui n'a pas mis cette déclaration dans les headers publics.
Où met on ses définitions? Dans un cpp. Sinon les risques de violation ODR (One Definition rule) sont grandes.
En recherche d'emploi.