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++

  1. #1
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2020
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vienne (Poitou Charente)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2020
    Messages : 11
    Points : 12
    Points
    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 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    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 éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    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 565
    Points : 7 648
    Points
    7 648
    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 éprouvé
    Femme Profil pro
    ..
    Inscrit en
    Décembre 2019
    Messages
    562
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 94
    Localisation : Autre

    Informations professionnelles :
    Activité : ..

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

  5. #5
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 631
    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 631
    Points : 10 559
    Points
    10 559
    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 à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2020
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vienne (Poitou Charente)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2020
    Messages : 11
    Points : 12
    Points
    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 éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 631
    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 631
    Points : 10 559
    Points
    10 559
    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")

  8. #8
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2020
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vienne (Poitou Charente)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2020
    Messages : 11
    Points : 12
    Points
    12
    Par défaut
    J'ai modifié le constructeur et le de deconstructeur etc. en priver. Mais pour la méthode getInstance(), ça revient à garder ce que j'ai déjà fait, je crée ma fonction getInstance() dans ma classe et retourner ma variable statice (enfin sa valeur). Ou c'est autre chose ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
      static MultiContainer<KEY_T, VAL_T>& getInstance() {
        if (!_singletonCacheServer) {
            _singletonCacheServer = new MultiContainer;
        }
        return *_singletonCacheServer;
      }

  9. #9
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    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 565
    Points : 7 648
    Points
    7 648
    Par défaut
    Citation Envoyé par foetus Voir le message
    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.
    Une variable static est unique dans le code même si elle est dans une fonction inline.
    Comme ici la fonction est template, chaque cas avec des paramètres distincts de template aura sa variable statique unique associée.
    Le mot inline ici peut être omis (car aussi bien les fonctions template que les inline peuvent être redéfinies plusieurs fois).
    Ce mot aujourd'hui s'est éloigné de sa signification initiale. Il indique que la définition pourra être vue plusieurs fois mais désigne toujours la même fonction (ou la même variable car désormais les variables peuvent être elles-aussi inline). Et finalement la fonction sera ou pas inline. C'est aujourd'hui le compilateur qui décide de mettre en inline ou pas une fonction, et c'est indépendant du mot inline.
    Citation Envoyé par foetus Voir le message
    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")
    Oui inline est facultatif ici. On déclare la fonction dans la classe par:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
            static MultiContainer  getInstance();
    On définit la fonction après la classe par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template<class  KEY_T,class VAL_T>                  // inline est facultatif ici car c'est une template
    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
    }
    Ici l'unicité est garantie par le système y compris dans le cas où plusieurs threads tentent de créer simultanément le singleton (à gérer soit même dans ce cas est loin d'être simple, un simple test de variable même si elle est atomic n'est pas suffisant!)

  10. #10
    Membre éprouvé
    Femme Profil pro
    ..
    Inscrit en
    Décembre 2019
    Messages
    562
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 94
    Localisation : Autre

    Informations professionnelles :
    Activité : ..

    Informations forums :
    Inscription : Décembre 2019
    Messages : 562
    Points : 1 253
    Points
    1 253
    Par défaut
    Citation Envoyé par foetus Voir le message
    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.
    C'est d'ailleurs pour ça que j'ai plussoiyé son message
    1: fonction membre static template dans un en-tête => gestion complète par le compilo.
    2: inline => ODR complaisant (le compilateur n'a pas le choix).
    3: variable locale statique => un seul et même objet pour tout le processus.
    4: initialisation thread safe, merci C++11

  11. #11
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 631
    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 631
    Points : 10 559
    Points
    10 559
    Par défaut
    Citation Envoyé par dalfab Voir le message
    Ce mot aujourd'hui s'est éloigné de sa signification initiale.
    Ah ouais depuis (peut-être) C++14 , inline a 1 autre fonction inline specifier, lien cppreference.com en anglais
    En gros, le comité a décidé que le compilateur pouvait se débrouiller tout seul, et maintenant, inline veut dire "il y a plusieurs définitions dans différentes unités de compilation" (<- en très très gros)
    Donc le inline static fait sens.

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

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2020
    Messages : 11
    Points : 12
    Points
    12
    Par défaut
    Merci de votre réponse j'ai appris pas mal de chose, je pense approcher de la fin mais j'ai encore quelques question (toujours liée au poste)


    1er Question:

    Pour le coup, sachant que ma classe est un template, toute mon initialisation se fait dans mon .hpp. Donc j'implémente la solution que tu m'as donnée. Voici la classe finale:

    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
     
    #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:
     
      ~MultiContainer();
      MultiContainer();
      MultiContainer(const MultiContainer<typename KEY_T, typename VAL_T>&) = delete;
      MultiContainer& operator=(const MultiContainer&);
     
      using value_type = std::pair<KEY_T, VAL_T>;
     
      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;
     
     
     
      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){};
     
      static MultiContainer getInstance();
     
      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;
      }
     
    };
    Mais j'ai une erreur: "déclaration is incompatible with "MultiContainer<KEY_T, VAL_T> MultiContainer<KEY_T, VAL_T>: : getInstance [with KEY_T=<error-type>, VAL_T=<error-type>]" (declared at line 45)"

    Ce que je comprends avec cette erreur, c'est que je dois regarder du coter de cette ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      static MultiContainer getInstance();
    . La seule chose qui me fait penser à une erreur viendrait de cette ligne, j'ai tort ou pas ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    template<class KEY_T,class VAL_T>

    2e Question:


    Dans mon exemple, j'ai introduit une main pour faire mon test.

    L'appel de mon singleton se faisait par cette ligne;

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    MultiContainer<std::string,std::string>& a1 = MultiContainer<std::string, std::string>::getInstance();
    Mais mon but est bien sur de l'utiliser ailleurs. N'étant pas habitué avec les inline. Ma première pensée est d'utiliser directement le code ci-dessous dans d'autre classes, directement dans le fichier sources.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    template<class KEY_T,class VAL_T>                  // inline est facultatif ici car c'est une template
    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
    }

    Par exemple:

    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
     
     
    //Fichier Header
     
    class B{
     private:
      string value;
      string text;
     public:
      void setValue();
      string getResult();
    };
     
     
    //Fichier Source
     
    template<class KEY_T,class VAL_T>                  // inline est facultatif ici car c'est une template
    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
    }
     
    void setValue(){
     //Code
     MultiContainer<std::string,std::string>& a1 = MultiContainer<std::string, std::string>::getInstance();
    };
     
    string getResult(){
     //Code
    };

    ps: merci aussi a vous tous de partager vos expériences. j'espère aussi ne pas trop poser des questions qui pour vous paraissent peut-être logiques et simples

  13. #13
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 631
    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 631
    Points : 10 559
    Points
    10 559
    Par défaut
    public:

    MultiContainer(int icapacity) : _capacity(icapacity){};

    static MultiContainer getInstance();
    En gros, ta méthode getInstance n'est pas statique et il te reste 1 constructeur en public.

    Je pense que tu te prends la tête : code ta classe MultiContainer normalement, et utilise 1 classe Singleton (comme mon singleton/ pseudo code) pour faire Singleton<MultiContainer>::Instance();

  14. #14
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2020
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vienne (Poitou Charente)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2020
    Messages : 11
    Points : 12
    Points
    12
    Par défaut
    Merci foetus pour ta réponse. Oui tu as raison, je me prends surement la tête pour un sujet simple : D

    J'aurais plusieurs questions tout de même.


    Déjà le code corriger:

    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
     
    #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 MultiContainer{
      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; 
     
      ~MultiContainer();
      MultiContainer();
      MultiContainer(const MultiContainer<typename Tkey, typename Tval>&) = delete;
      MultiContainer& operator=(const MultiContainer&);
     
      int capacityOut(){
          if( capacity == 0 || container.size() < capacity ) {
            return 0;
          }
          int cnt = 0;
          while(container.size() > capacity) {
            container.pop_back();
            ++cnt;
          }
          return cnt;      
      }
     
      public:
     
      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;
      }
    };

    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
     
    template <class T>
    class Singleton {
    public:
        static T& getInstance();
    private:
        //version 1 - definition
        //static T m_inst;
        Singleton() {};
        Singleton(const Singleton<T>&) {};
        ~Singleton() {};
        T& operator= (const T&) {};
     
    };
     
     
    template<class T>
    T& Singleton<T>::getInstance() {
        static T m_inst = T();
        return m_inst;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    #include "multiContainer.hpp"
    #include "singleton.hpp"
     
    int main()
    {
      auto const& t = Singleton<MultiContainer<std::string,std::string>>::getInstance(); 
     
    }

    1er: Je comprends le principe d’empêcher la construction d'une nouvelle instance singleton, donc de mettre tous les constructeurs, déconstructeur, copie etc. en priver

    Mais par la suite quand j'initialise le singleton, je ne peux pas le construire. Erreur qui suit:

    "calling a private constructor of class 'MultiContainer<std::basic_string<char>, std::basic_string<char>>'" ou
    "variable of type 'MultiContainer<std::basic_string<char>, std::basic_string<char>>' has private destructor"


    2e: J'ai peur de faire une erreur entre "MultiContainer(const MultiContainer&);" et "MultiContainer(const MultiContainer<typename Tkey, typename Tval>&);"

    Dans ma première classe, j'avais declarer "MultiContainer(const MultiContainer&);" et je n'avais aucune erreur. Mais je me suis dit que j'ai fait un oubli
    car je n'avais pas précisé le template donc après modification j'ai rajouté <typename Tkey, typename Tval>.

    Est-ce que ma pensée était fausse ? ou c'est bien comme ça qu'il faut le déclarer

    3e: "in instantiation of member function 'Singleton<MultiContainer<std::basic_string<char>, std::basic_string<char>>>::getInstance' requested here"

    Cette erreur proviendrait peut-être de ma première erreur qui empêche sa création

  15. #15
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 631
    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 631
    Points : 10 559
    Points
    10 559
    Par défaut
    Ton problème est simple : maintenant que tu as codé 2 classes (ta classe principale MultiContainer et 1 classe Singleton), ta classe principale MultiContainer n'a plus besoin d'avoir ces constructeurs et destructeurs privés.

    Par contre, ton singleton va appeler le constructeur par défaut ( T() ). Il faut réfléchir si tu veux paramétrer ton container ? passer par des mutateurs ("setters") ? utiliser des paramètres ayant 1 valeur, mais il faut trouver le premier appel ?

  16. #16
    Membre éprouvé
    Femme Profil pro
    ..
    Inscrit en
    Décembre 2019
    Messages
    562
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 94
    Localisation : Autre

    Informations professionnelles :
    Activité : ..

    Informations forums :
    Inscription : Décembre 2019
    Messages : 562
    Points : 1 253
    Points
    1 253
    Par défaut
    Pour info, boost fournit déjà une classe/enveloppe (wrapper) singleton

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