IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

extern template sur classes et fonctions


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Par défaut extern template sur classes et fonctions
    Bonjour à tous,

    Depuis 3 jours, j’essaie de comprendre comment fonctionne le mot clé extern compte tenu des différentes applications qu'il peut avoir. Malgré toute la documentation que je trouve sur internet, il me reste encore à pouvoir combiner ce mot clé avec les template et ce, pour de l'instanciation de classes mais aussi l'utilisation de fonctions templates.

    Ci-dessous, les 3 parties du code que j'essaie de faire compiler :

    * Le main.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include <iostream>
    #include "etst.hpp"
     
    testing::object_vector<int> myVect;
     
    int main()
    {
    	add<int>(3);	// undefined reference to "void add<int>(int const &)"
     
     
        std::cout<< "val A = " << "a" << std::endl;
        std::cout<< "val B = " << "b" << std::endl;
     
        return 0;
    };

    * le tst.hpp (contenant la classe principale) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #pragma once
     
    #include <vector>
    #include <cassert>
    #include <cstddef>
     
     
    namespace testing
    {
    	template<typename T>
    	class object_vector
    	{
    	private:
    		std::vector<T> vect_;
     
    	public:
    		object_vector() {}
     
    		void add(T const & obj){
    			vect_.push_back(obj);
    		}
     
    		T & get(size_t const & id){
    			assert(vect_.size() > id && "out of limits");
    			return vect_[id];
    		}
     
    	};
    }

    et enfin la partie en cours de développement, le etst.hpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #pragma once
    #include "tst.hpp"
     
     
    namespace testing
    {
    	template<typename T>
    	class object_vector;
    }
     
     
    template<typename T>
    extern testing::object_vector<T> myVect;
     
    template<typename T>
    void add(T const & obj)
    {
    	myVect<T>.add(obj);
    }
     
     
    template<typename T>
    T & ovget(size_t const & id)
    {
    	return myVect<T>.get(id);
    }

    Vous l'aurez compris, ceci n'est qu'un code rapide destiné à tester le principe. Ici, l'objectif est de "créer" à la volée des objets des la classe object_vector en fonction du type de donnée entrée, et d'ajouter des valeurs de ce type dans ce nouvel objet.
    Concrètement, je dois pouvoir réaliser des appels du type add<int>(12); ou add<float>(3.5f); etc...

    Aujourd'hui, sans l'utilisation du code présent dans "etst.hpp", et donc en appelant directement les méthodes de object_vector le code fonctionne. Par contre, lorsque je souhaite mettre en place le code présent dans "etst.hpp", selon les différentes configurations essayées, j'obtiens soit error: 'testing::object_vector<int> myVect' redeclared as different kind of symbol avec le code fourni ci-dessus, soit error: undefined reference to 'void add<int>(int const &)' (si je fais l'essai en adaptant "etst.hpp" en "etst.cpp").

    J'ai alors bien entendu essayé plusieurs autres adaptations de ce code, mais globalement, ce sont les erreurs que je retrouve régulièrement et dont je recherche en vain les explications...

    Quelqu'un saurait m'aiguiller sur une correction ?


    Merci d'avance.

  2. #2
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 760
    Par défaut
    Juste une question, dans quel but ? Parce que les templates externes ne sont je crois pas supportées par les compilateurs et cela se fait de toute manière sur une instanciation explicite: une template externe n'est pas template
    https://en.cppreference.com/w/cpp/la...class_template (Je te conseille de faire sans, c'est là surtout dans le but d'accélérer la compilation.)

  3. #3
    Membre éclairé Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Par défaut
    En fait, avec l'aide de ce post, je réalise un ECS, permettant donc de créer des tableaux d'objets accessibles via des identifiants (clés). Le schéma représenté ci-dessus retrace, selon moi, plutôt bien la forme du code nécessaire.

    Mon but est donc de rendre le code plus convivial en permettant de créer les objets de la classe "object_vector" automatiquement, et uniquement s'ils sont utilisés, et cela passe par les fonctions que l'on retrouve dans le header "etst.hpp" (add, get etc..).
    Dans le cas présenté ci-dessus, si je remplace le code template<typename T> extern testing::object_vector<T> myVect; du header "etst.hpp" par extern testing::object_vector<int> myVect;, alors, le code fonctionne... Pour un int. Si je veux maintenant le faire pour un float, je suis obliger de trouver une ruse.

    Le but de la manœuvre est donc de généraliser ce bout de code grâce à template<typename T> extern testing::object_vector<T> myVect; mais visiblement, je dois trouver autre chose...

  4. #4
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    En fait, je me suis trompé dans ce post...

    La solution la meilleure pour pouvoir récupérer le "gestionnaire" d'un type de composant particulier serait encore de passer par une fonction qui renvoie une référence sur une variable statique locale, sous une forme proche de

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename T>
    ComponentHolder <T> & getHolder(){
        static ComponentHolder<T> holder;
        return holder
    }
    pour avoir une fonction addComponent( par exemple) qui prendrait la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename C>
    void addComponent(size_t id, C const & value){
          Component<C> component{entity,value};
          holder.add(component);
    }
    Juste une question, dans quel but ? Parce que les templates externes ne sont je crois pas supportées par les compilateurs et cela se fait de toute manière sur une instanciation explicite: une template externe n'est pas template
    Cela se fait -- effectivement sur une instanciation explicite (ce que j'ai indiqué dans l'intervention en question), mais il y a tout à fait moyen de déclarer une instance particulière de classe extern.

    La meilleure preuve en est que les flux standard sont bel et bien déclaré comme extern (du moins, dans la bibliothèque standard telle que fournie par Gcc) sous la forme de :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      extern istream cin;		/// Linked to standard input
      extern ostream cout;		/// Linked to standard output
      extern ostream cerr;		/// Linked to standard error (unbuffered)
      extern ostream clog;		/// Linked to standard error (buffered)
    Par contre, j'admet que ce n'est peut être pas le plus efficace dans le cas présent, et qu'une fonction renvoyant une référence sur un holder classique serait sans doute plus adaptée.

    ATTENTION L'instanciation explicite (pour chaque composant créé) me semble -- malgré tout -- particulièrement importante, dans le sens où il faut pouvoir s'assurer que chaque holder est strictement unique en son genre
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 760
    Par défaut
    Attention, extern istream cout; n'est pas un template externe, mais une variable externe. Le premier instancie explicitement un template sans pour autant avoir de variable associée, le second indique qu'une variable existe dans une autre unité de compilation.

    Pour le getHolder(), je conseille de le mettre dans la fonction ComponentHolder et mettre tous les constructeurs privée pour éviter une construction isolée et des problèmes au niveau des ids.

  6. #6
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Sauf que, à mon sens, s'il a effectivement un problème, ce n'est pas à cause de la généricité, car il n'y a rien à faire std::istream n'est qu'un alias de type de
    template <typename CHAR_C /* , ...*/>
    std:: basic_istream
    et que le mot clé extern l'est là que pour indiquer que "il faut pas s'en faire, cette donnée existe bel et bien quelque part dans le code binaire".

    A part à cause du problème dont je vais parler tout de suite, il n'y aurait donc -- a priori -- absolument aucune raison (*) pour que le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <typename T>
    extern MaClasse<T> nomDeVariable;
    soit jugé invalide

    (*) Sauf que, bien sur, on va se heurter de plein fouet à la règle de la définition unique des variables, dans une portée donnée

    On ne peut utiliser nomDeVariable que comme ... nom de variable strictement unique dans une portée (un espace de noms ou autres) bien particulière.

    Je ne peux donc pas avoir la spécialisation
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <>
    MaClass<Position> nomDeVariable;
    qui traine "quelque part" dans le code binaire
    et la spécialisation
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <>
    MaClass<Vitesse> nomDeVariable;
    qui trainerait "ailleurs" dans le code binaire

    Et, de fait, il serait "dommage" de partir sur une approche générique si c'est pour ne pouvoir -- au final -- disposer que d'une seule donnée (d'un type bien particulier, qui plus est).

    C'est la raison pour laquelle je conseillerais plutôt une fonction template telle qu'indiquée dans mon intervention précédente, que je préférerais encore à l'utilisation de macros proches de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    #define HOLDER_NAME(type) type##Holder
    #define CREATE_HOLDER(type) template <> Holder<type> HOLDER_NAME(type)
    (même s'ils devaient pouvoir fonctionner -- aux inévitables corrections pour cause de non test près -- et qu'ils pourraient présenter quelques avantages )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Pointeur sur classe template
    Par SmOkEiSBaD dans le forum Langage
    Réponses: 6
    Dernier message: 23/04/2011, 12h29
  2. [Template] Pointeur sur classe template
    Par Kromagg dans le forum Langage
    Réponses: 2
    Dernier message: 29/10/2008, 16h01
  3. Réponses: 4
    Dernier message: 15/10/2008, 09h33
  4. Réponses: 3
    Dernier message: 08/07/2008, 15h06
  5. Fonction template et classe exportée
    Par xwindoo dans le forum Langage
    Réponses: 35
    Dernier message: 11/01/2008, 21h28

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo