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 :

Singleton et Template


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2020
    Messages
    12
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vienne (Poitou Charente)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2020
    Messages : 12
    Par défaut Singleton et Template
    Bonjour à tous !


    Je viens vers vous, car j'ai un souci sur une classe singleton que je souhaite créer. Je sais que le singleton crée toujours un débat, mais mon poste ne concerne pas ce débat, mais plutôt un souhait de ma part de l'utiliser pour mon amusement à moi et surtout pour connaître sa création dans un contexte du petit projet que je me construis pour améliorer mes connaissances globales.


    Je souhaite aussi dire que je ne suis pas expert en création du post sur les forums (pas pour l'instant) donc si vous avez des conseils, que vous trouver que j'ai mal créé mon poste ou mes questions, je suis preneur de tous vos conseils. (je suis débutant, merci de votre gentillesse.)

    J'attaque le sujet.

    Le contexte de ma classe est assez simple, c'est un container que j'utilise comme un cache qui me permet de stocker et d'utiliser les valeurs voulues en les identifiants par des clés bien précises (ma classe utilise la lib boost)

    Je vous copie ici ma classe de base qui fonctionne pour avoir une idée.

    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
     
     
    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/sequenced_index.hpp>
    #include <boost/multi_index/hashed_index.hpp>
    #include <boost/multi_index/key.hpp>
     
    template<typename Tkey, typename Tval>
    class CacheWeb{
      private:
      using value_type = std::pair<Tkey, Tval>;
     
      unsigned int capacity;
      boost::multi_index_container<
        value_type,
        boost::multi_index::indexed_by<
          boost::multi_index::sequenced<>,
          boost::multi_index::hashed_unique<boost::multi_index::key<&value_type::first>>
        >
    > container; 
     
      CacheWeb(const CacheWeb&) = delete;
      CacheWeb& operator=(const CacheWeb&) = delete;
     
      int capacityOut(){
          if( capacity == 0 || container.size() < capacity ) {
            return 0;
          }
          int cnt = 0;
          while(container.size() > capacity) {
            container.pop_back();
            ++cnt;
          }
          return cnt;      
      };
     
      public:
      CacheWeb(int icapacity) : capacity(icapacity){};
      virtual ~CacheWeb() = default;
     
     
      int size(){
        return container.size();
      };
      bool empty(){
        return container.empty(); 
      };
      void clear(){
        container.clear(); 
      };
     
      bool contains(const Tkey& key){
        const auto& lookup = container.template get<1>();
        return lookup.find(key) != container.template get<1>().end(); 
      };
     
      void remove(const Tkey& key){
        container.erase(key);
      };
     
      void put(const Tkey& key, const Tval& val){
        auto& lookup = container.template get<1>();
        auto it = lookup.find(key);
        if( it != lookup.end() ) {
          lookup.modify(it,[&](value_type& x){ x.second = val; });
        }
        else{
          it=lookup.emplace(key, val).first;
        }
        container.relocate(container.begin(),container.template project<0>(it));
        capacityOut();
      };
     
      std::list<std::pair<Tkey, Tval>>getItems(){
        return {container.begin(), container.end()};
      };
     
      const Tval& get(const Tkey& key){
        const auto& lookup = container.template get<1>();
        const auto it = lookup.find(key);
        if( it == lookup.end() ) {
          throw std::invalid_argument("Key does not exist");
        }
        return it->second;
      }
    };
     
    #include <iostream>
     
    int main()
    {
      CacheWeb<int,int> c(10);
      for(int i=0;i<11;++i)c.put(i,i);
     
      for(const auto& x:c.getItems()){
        std::cout<<"("<<x.first<<","<<x.second<<")";
      }
      std::cout<<"\n";
     
      for(int i=1;i<11;++i){
        std::cout<<i<<"->"<<c.get(i)<<" ";
      }
      std::cout<<"\n";
    }
    Par la suite j'ai voulu utilise le pattern singleton, donc j'ai recherché et testé différentes idées (je pense même etre parti en hors sujet à certains moments ou avoir fait trop compliquer pour quelque chose de simple). Ce qui me donnait ceci

    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
     
    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/sequenced_index.hpp>
    #include <boost/multi_index/hashed_index.hpp>
    #include <boost/multi_index/identity.hpp>
    #include <boost/multi_index/member.hpp>
    #include <boost/config.hpp>
    #include <iostream>
    #include <stdexcept>
    #include <list>
     
    template<typename KEY_T, typename VAL_T>
    class MultiContainer{
     
      private:
     
      using value_type = std::pair<KEY_T, VAL_T>;
     
      static MultiContainer<KEY_T, VAL_T>* _singletonCacheServer;
      unsigned int _capacity;
      boost::multi_index_container<value_type,boost::multi_index::indexed_by<boost::multi_index::sequenced<>,  boost::multi_index::hashed_unique<boost::multi_index::member<value_type,KEY_T,&value_type::first>>>> _container;
     
      MultiContainer(const MultiContainer&) = delete;
      MultiContainer& operator=(const MultiContainer&) = delete;
     
      int capacityOut(){
          if( _capacity == 0 || _container.size() < _capacity ) {
            return 0;
          }
          int cnt = 0;
          while(_container.size() > _capacity) {
            _container.pop_back();
            ++cnt;
          }
          return cnt; 
      };
     
      public:
     
      MultiContainer(int icapacity) : _capacity(icapacity){};
      virtual ~MultiContainer() = default;
      MultiContainer();
     
      static MultiContainer<KEY_T, VAL_T>& getInstance() {
        if (!_singletonCacheServer) {
            _singletonCacheServer = new MultiContainer;
        }else{
            delete _singletonCacheServer;
            _singletonCacheServer = nullptr;
        }
        return _singletonCacheServer;
      }
     
      int size(){
        return _container.size();
      };
      bool empty(){
        return _container.empty(); 
      };
      void clear(){
        _container.clear(); 
      };
     
      bool contains(const KEY_T& key){
        const auto& lookup = _container.template get<1>();
        return lookup.find(key) != _container.template get<1>().end(); 
      };
     
      void remove_key(const KEY_T& key){
        auto& lookup = _container.template get<1>();
        auto it = lookup.find(key);
        if(it != lookup.end()){
          lookup.erase(it);
        }
      }
     
      void put(const KEY_T& key, const VAL_T& val){
        auto& lookup = _container.template get<1>();
        auto it = lookup.find(key);
        if( it != lookup.end() ) {
          lookup.modify(it,[&](value_type& x){ x.second = val; });
        }
        else{
          it=lookup.emplace(key, val).first;
        }
        _container.relocate(_container.begin(),_container.template project<0>(it));
        capacityOut();
      };
     
      std::list<std::pair<KEY_T, VAL_T>>getItems(){
        return {_container.begin(), _container.end()};
      };
     
      const VAL_T& get(const KEY_T& key){
        const auto& lookup = _container.template get<1>();
        const auto it = lookup.find(key);
        if( it == lookup.end() ) {
          throw std::invalid_argument("Key does not exist");
        }
        return it->second;
      }
     
    };
     
     
     
    int main()
    {
     
      MultiContainer<std::string, std::string>* a1 = MultiContainer<std::string, std::string>::getInstance();
      //auto cache  = MultiContainer<std::string, std::string>::getInstance();
     
    }
    Le souci c'est qu'en testant, j'ai obtenu une erreur celle-ci: a nonstatic member reference must be relative to a specific object


    À cause de cette erreur je me suis dit que j'ai mal construit mon singleton car s'il me demande un objet cela voudrait dire que le principe de singleton ne fonctionne pas.

    Quelles sont les erreurs que j'ai pu faire ? pour pouvoir enfin réussir à faire fonctionner ma classe

    Voilà j'ai peur d'avoir pas assez expliqué, donc merci si vous me lisez et je ferais le nécessaire pour corriger, améliorer mon poste. Bonne Année à vous



    ps: j'aurais une deuxième question par la suite "dans quel fichier utilisé le singleton" mais cela viendra après

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 153
    Billets dans le blog
    4
    Par défaut
    Le singleton magique qui se crée tout seul quand tu l'appelles c'est une hérésie, mais celui qui en plus se détruit et se recrée tous les 2 appels pour le récupérer, retournant au passage une référence vers un null une fois sur deux, c'est carrément débile et un suicide software.
    Si tu veux un singleton, arrange-toi pour qu'une unique instance soit créée, puis crée-la quelquepart.
    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
     
    class A
    {
      static A* Instance;
    public:
      A()
      {
        assert(Instance == nullptr);
        Instance = this;
      }
      ~A()
      {
        assert(Instance == this);
        Instance = nullptr;
      }
     static A* Get() { return Instance; }
    };
    A* A::Instance = nullptr;
     
    int main()
    {
      A a;
      ...
      A::Get()->...;
    }
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Bonjour,

    il manque une * ligne 51. Et dans main() ligne 110, ça n'est pas une * qu'il faut mais un &.

    D'autre part ton singleton est curieux. C'est un clignotant! A la 1ère demande il retourne une instance, à la seconde elle est détruite et ça retourne n'importe quoi, à la troisième il recrée un nouvelle instance... Il faut supprimer les lignes 47 à 49.

    Mais la variable MultiContainer<,>::_singletonCacheServer doit être créée quelque part. Le problème c'est qu'elle est template ce qui complique les choses.
    Il est plus simple et plus sûr de d'avoir cette définition dans l'entête :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template<class  KEY_T,class VAL_T>
    inline MultiContainer<KEY_T,VAL_T>&  MultiContainer<KEY_T,VAL_T>::getInstance() {
        static MultiContainer<KEY_T,VAL_T>  container;  // va créer le container lors du 1er appel
        return  container;                              // retourne le singleton
    }

  4. #4
    Membre Expert
    Femme Profil pro
    ..
    Inscrit en
    Décembre 2019
    Messages
    676
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 95
    Localisation : Autre

    Informations professionnelles :
    Activité : ..

    Informations forums :
    Inscription : Décembre 2019
    Messages : 676
    Par défaut
    Et sans oublier de privatiser le constructeur.

  5. #5
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 771
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 771
    Par défaut
    Effectivement pour 1 singleton simple, le constructeur, le constructeur par recopie, le destructeur et l'opérateur = sont privés.

    1 exemple de singleton en C++98/ C++03. En C++ moderne, on peut delete (mais pas trop sûr du code )
    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
    30
    31
    32
    template <class T>
    class Singleton {
    public:
     
        static T& Instance();
     
     
    private:
     
    //  version 1 - definition
        static T m_inst;
     
        Singleton() {}
        Singleton(const Singleton<T>&) {}
        ~Singleton() {}
     
        T& operator= (const T&) {}
    };
     
     
    // version 1 - initialisation
    template<class T>
    T Singleton<T>::m_inst = T();
     
     
    template<class T>
    T& Singleton<T>::Instance() {
    //  version 2 - definition + lazy initialisation
    //  static T m_inst = T();
     
        return m_inst;
    }

  6. #6
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2020
    Messages
    12
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vienne (Poitou Charente)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2020
    Messages : 12
    Par défaut
    Merci à tous de vos réponses.


    Je vais juste revenir sur ta réponse dalfad.

    J'ai fait les corrections que tu as proposées. Je suis toujours. en test pour bien comprendre l'utilisation du inline que tu me propose

    Vu que je suis toujours dans mon apprentissage. Je vais te dire ce que j'ai compris des inline deja.

    Le mot-clé inline est utilisé en C++ et s'applique à une fonction ou à une méthode. Il indique au compilateur que chaque appel à la fonction inline doit être remplacé par le corps de cette fonction.

    Pour le coup, ça devrait dire que je supprime ma premier fonction getInstance d'avant, est qu'elle sera remplacée par la inline ?

    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
     
    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/sequenced_index.hpp>
    #include <boost/multi_index/hashed_index.hpp>
    #include <boost/multi_index/identity.hpp>
    #include <boost/multi_index/member.hpp>
    #include <boost/config.hpp>
    #include <iostream>
    #include <stdexcept>
    #include <list>
     
     
     
    template<typename KEY_T, typename VAL_T>
    inline MultiContainer<KEY_T,VAL_T>& MultiContainer<KEY_T,VAL_T>::getInstance() {
        static MultiContainer<KEY_T,VAL_T>  container;  // va créer le container lors du 1er appel
        return container;                              // retourne le singleton
    }
     
     
    template<typename KEY_T, typename VAL_T>
    class MultiContainer{
     
      private:
     
      using value_type = std::pair<KEY_T, VAL_T>;
     
      static MultiContainer<KEY_T, VAL_T>* _singletonCacheServer;
      unsigned int _capacity;
      boost::multi_index_container<value_type,boost::multi_index::indexed_by<boost::multi_index::sequenced<>,  boost::multi_index::hashed_unique<boost::multi_index::member<value_type,KEY_T,&value_type::first>>>> _container;
     
      MultiContainer(const MultiContainer&) = delete;
      MultiContainer& operator=(const MultiContainer&) = delete;
     
      int capacityOut(){
          if( _capacity == 0 || _container.size() < _capacity ) {
            return 0;
          }
          int cnt = 0;
          while(_container.size() > _capacity) {
            _container.pop_back();
            ++cnt;
          }
          return cnt; 
      };
     
      public:
     
      MultiContainer(int icapacity) : _capacity(icapacity){};
      virtual ~MultiContainer() = default;
      MultiContainer();
     
      /**
      static MultiContainer<KEY_T, VAL_T>& getInstance() {
        if (!_singletonCacheServer) {
            _singletonCacheServer = new MultiContainer;
        }
        return *_singletonCacheServer;
      }
      */
     
     
      int size(){
        return _container.size();
      };
      bool empty(){
        return _container.empty(); 
      };
      void clear(){
        _container.clear(); 
      };
     
      bool contains(const KEY_T& key){
        const auto& lookup = _container.template get<1>();
        return lookup.find(key) != _container.template get<1>().end(); 
      };
     
      void remove_key(const KEY_T& key){
        auto& lookup = _container.template get<1>();
        auto it = lookup.find(key);
        if(it != lookup.end()){
          lookup.erase(it);
        }
      }
     
      void put(const KEY_T& key, const VAL_T& val){
        auto& lookup = _container.template get<1>();
        auto it = lookup.find(key);
        if( it != lookup.end() ) {
          lookup.modify(it,[&](value_type& x){ x.second = val; });
        }
        else{
          it=lookup.emplace(key, val).first;
        }
        _container.relocate(_container.begin(),_container.template project<0>(it));
        capacityOut();
      };
     
      std::list<std::pair<KEY_T, VAL_T>>getItems(){
        return {_container.begin(), _container.end()};
      };
     
      const VAL_T& get(const KEY_T& key){
        const auto& lookup = _container.template get<1>();
        const auto it = lookup.find(key);
        if( it == lookup.end() ) {
          throw std::invalid_argument("Key does not exist");
        }
        return it->second;
      }
     
    };
     
     
    int main()
    {
     
      MultiContainer<std::string,std::string>& a1 = MultiContainer<std::string, std::string>::getInstance();
      //auto cache  = MultiContainer<std::string, std::string>::getInstance();
     
    }

  7. #7
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 771
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 771
    Par défaut
    Je n'avais pas fait vraiment attention à la solution de @dalfab, mais tu "inline" 1 variable static
    Est-ce que cela fonctionne ? ta variable va se retrouver partout dans le code.

    Citation Envoyé par nansty Voir le message
    Je vais te dire ce que j'ai compris des inline deja.
    La solution de @dalfab est la version 2 de mon pseudo-code
    Tu crées 1 méthode (ici getInstance()) et dans sa définition, tu utilises 1 variable locale statique.

    Moi ma méthode est statique, parce que je ne veux pas utiliser la classe autrement qu'en singleton.

    Mais ton singleton n'est pas bon tu as encore 2 constructeurs et 1 destructeur public:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
      public:
     
      MultiContainer(int icapacity) : _capacity(icapacity){};
      virtual ~MultiContainer() = default;
      MultiContainer();

    Le mot clef inline est juste 1 indication au compilateur pour que dans le résultat final (exécutable, bibliothèque, ...) l'appel (en assembleur c'est call) soit remplacé par le code de la méthode (en remplaçant au moins les paramètres et les return)
    La première chose, c'est 1 indication dans le sens le compilateur peut le faire ou pas. La longueur du code est/ était 1 paramètre important : si ta méthode est trop longue elle ne sera pas inlinée.

    La deuxième chose, c'est (qu'en théorie), toute méthode définie à l'intéreur de la définition de la classe est inlinée.
    D'ailleurs (à vérifier) le mot clef inline est facultatif et doit être mis que dans la définition de la classe.
    Il sert essentiellement pour sortir les définitions de tes méthodes inlinées à l'extérieur de la classe (code + "propre")

Discussions similaires

  1. [DesignPattern] Singleton et Template
    Par faust73 dans le forum Langage
    Réponses: 4
    Dernier message: 16/09/2014, 23h06
  2. [Résolu]Singleton avec template de fonction
    Par LiquidHuk dans le forum C++
    Réponses: 2
    Dernier message: 05/06/2014, 10h21
  3. Singleton template partagé entre Dll et Exe
    Par eltrex dans le forum Langage
    Réponses: 1
    Dernier message: 07/08/2008, 12h13
  4. Réponses: 3
    Dernier message: 22/11/2006, 21h10
  5. Réponses: 13
    Dernier message: 25/10/2006, 16h17

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