Partage
  • Partager sur Facebook
  • Partager sur Twitter

retour de méthode virtuelle pure de plusieurs type

Sujet résolu
Anonyme
    18 mai 2020 à 21:49:56

    Salut,

    J'ai pour mini projet de faire un objet (enfin plus un ensemble d'objets) qui permettent de lire un fichier de config. J'ai donc un objet créer comme ceci :

    Loader_Config config("filepath", [
       Config_Parameter_int("nom", valeur_par_defaut),
       ... //etc
    ]);

    J'ai donc un objet Config_Parameter qui est un classe abstraite dû à la méthode getValue qu'elle possède, et là est le problème, quel est le type de retour de cette méthode ? Pour les classes fille c'est simplement leur type mais pour celle-ci ?!

    Je ne suis même pas sûr que je peux changer le type de retour entre 2 classes filles :/

    • Partager sur Facebook
    • Partager sur Twitter
      18 mai 2020 à 22:19:49

      Tu peux reformuler ta question ! Je n'y comprend rien !

      BrigitteLPB a écrit:

      J'ai donc un objet Config_Parameter qui est un classe abstraite ...

      Pas possible ! Un objet ne peut pas être une classe et encore moins une classe abstraite. On ne peut même pas créer d'objet avec une classe abstraite !

      BrigitteLPB a écrit:

      quel est le type de retour de cette méthode ?

      De la méthode getValue ? Si oui, elle n'a peut-être rien à faire dans la classe de base !

      -
      Edité par rouloude 18 mai 2020 à 22:28:09

      • Partager sur Facebook
      • Partager sur Twitter
        18 mai 2020 à 22:36:34

        La méthode getValue doit avoir pour type de retour le type des objets qu'elle est censée retourner.

        • Partager sur Facebook
        • Partager sur Twitter
          19 mai 2020 à 2:02:09

          Salut ! Euh... C'est un singleton ?? D'après ce que tu nous dis, ton objet est construit via une fonction membre d'une classe (getValue), donc celle ci retourne une référence ou un pointeur sur un autre objet. Enfin, c'est ce que j'ai essayé de comprendre.

          Pour connaître le type de retour d'une fonction membre dont hérite une classe fille, bah y'a pas besoin de chercher loin, tu regardes la signature de la fonction. Je sais pas si ça te parle:

          #include <iostream>
          #include <string>
          #include <type_traits>
          
          class Object {
            private:
              static Object _S_instance;
          
            public:
              Object& getValue() const noexcept {
                return Object::_S_instance;
              }
          };
          
          Object Object::_S_instance;
          
          class Derived : public Object {
          };
          
          int main() {
            Derived test;
            std::string getValue_type {
              (std::is_same<decltype( test.getValue() ), Object&>()) ? "Object&" : "Other type"
            };
          
            std::cout << "getValue return type: " << getValue_type << std::endl;
          }

          La classe Derived hérite de la fonction getValue qui retourne une référence sur un instance statique de la classe mère.

          @michelbillaud, Hum... Tu aurais du approfondir la remarque. Je trouve que ça n'est pas assez explicite à lire... Tu aurais du ajouter le fait que le type de retour d'une fonction est le type retourné par la fonction. xDD

          -
          Edité par Daimyo_ 19 mai 2020 à 2:04:51

          • Partager sur Facebook
          • Partager sur Twitter
            19 mai 2020 à 3:54:48

            > Salut ! Euh... C'est un singleton ??

            J'espère pas pour elle. Mélanger singleton et héritage, c'est compliqué et casse gueule. De ce que je lis, ce n'est pas le cas. L'héritage est sur les paramètres, pas sur les types de fichiers

            BTW, je ne suis pas sûr que ce pattern de singleton soit thread-safe.

            Dernière remarque, on a toujours droit au retour covariant pour changer des détails. Mais sur ces problématiques, je doute que l'on y gagne.

            Au mieux on peut imaginer ce type de patterns si la liste des options est connue à l'avance et figée à tout jamais -- ça ne peut pas aller si on a des sortes de plugins dynamiques qui enregistrent leurs paramètres à la volée.
            NB: code non vérifié, noms catastrophiques

            struct IParam : boost::noncopyable {
                virtual ~IParam() = default;
                virtual bool decode(std::string_view text) = 0;
                // + helpstring, etc
            };
            
            
            template <typename T> struct Param : IParam {
                Param(T & actual) : m_actual(actual) {}
                bool decode(std::string_view text) override {
                    std::istringstream iss(text);
                    return iss >> m_actual;
                }
                T & m_actual;
            };
            
            // plus enfant éventuel pour les valeurs par défaut??
            
            ....
            
            struct DeclaredParameters { // nom très mauvais
                using params_t = std::unordered_map<std::string, std::unique_ptr<IParam>>;
                params_t m_params;
                
                template <typename T> void register_param(std::string const& name, T & actual) {
                    m_params.insert({name, std::make_unique<Param<T>{actual}});
                }
            
                void read(std::string const& filename) {
                    pourtouteslesentrees(name,value)dufichier(filename) {
                        m_params[name]->decode(value);
                        // TODO: gérer le cas pas de paramètre de nom "name" déclaré pour ce programme
                    }
                }
            };
            ....
            
            // liste de tous les paramètres possibles à prévoir de stocker quelque part
            int parameter_i = 42;
            float parameter_f; // ou alors... prévoir la gestion des optionals?
            std::string param_s;
            
            DeclaredParameters dp;
            dp.register_param("i", parameter_i );
            dp.register_param("mean", parameter_f);
            dp.register_param("name", parameter_s);
            
            dp.read("fichierconf.whatever"); // le résultat sera directement dans les variables parameter_*


            C'est une approche que j'aime bien, mais donc très limitée pour les paramètres dynamiques. Et elle demande encore de l'huile de coude et des spécialisations pour les paramètres qui sont des types composés (listes, tuples, structures...)

            Mais in-fine, la gestion des fichiers de conf, c'est toujours des grosses bidouilles dégueulasses à coup de cast retardés ou de variant/any.

            -
            Edité par lmghs 19 mai 2020 à 3:59:03

            • Partager sur Facebook
            • Partager sur Twitter
            C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
            Anonyme
              19 mai 2020 à 9:59:42

              salut,

              Merci pour vos retour ^^ mais je vois pas trop comment trop reformuler :/

              Je souhaite créer un objet Loader_Config qui va aller lire le fichier structuré qu'on lui à passer pour y lire les valeurs des variables au début (c'est un fichier de configuration en soit). Pour que Loader_Config sache quoi lire il reçoit donc un tableau (vector ?) de référence sur des "objets" Config_Parameter qui est une classe abstraite, c'est pour créer une collection hétérogène composer de classe héritant de Config_Parameter (chaque classe fille représente un type que l'on peut charger, ex Config_Parameter_int ou alors Config_Parameter_char).

              Donc pour que Loader_Config puisse manipuler sa collection hétérogène, Config_Parameter doit posséder des méthodes virtuelles pures pour obtenir la valeur du paramètre (voir un set mais lui ne sera pas dure). Tout le problème se pose dans le type de retour de getValue, car on ne le connait pas à l'avance :/ (et un void * serait une solution mais le pointeur n'est pas vraiment pratique du côté utilisation car impose de sauvegarder l'adresse pour ensuite accéder à son emplacement)

              Si vous pensez que ma façon de faire est mauvaise merci de m'indiquer un autre chemin car j'ai que celui là pour l'instant ^^

              • Partager sur Facebook
              • Partager sur Twitter
                19 mai 2020 à 11:21:06

                Ce n'est pas possible. Ce n'est pas pour rien que dans le code que je propose au dessus je mets le point de variation dynamique à la lecture du fichier et non au fetch de l'option.

                Soit tu retournes un std::variant<int,std::string,...> (de tous les types possibles), soit tu retournes un std::any, soit tu as un getOption<T> qui caste à la volée.

                • Partager sur Facebook
                • Partager sur Twitter
                C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                  19 mai 2020 à 12:50:42

                  Faut séparer en deux niveaux.

                  Le niveau bas lit des trucs dans un fichier, par exemple au format JSON, ou XML, ou ... et construit un objet qui contient la représentation de ce qui est dans le fichier.

                  A partir de là, du côté application, on va piocher les infos dans cet objet.

                  • Partager sur Facebook
                  • Partager sur Twitter
                  Anonyme
                    19 mai 2020 à 14:13:12

                    michelbillaud a écrit:

                    Faut séparer en deux niveaux.

                    Le niveau bas lit des trucs dans un fichier, par exemple au format JSON, ou XML, ou ... et construit un objet qui contient la représentation de ce qui est dans le fichier.

                    A partir de là, du côté application, on va piocher les infos dans cet objet.

                    c'est exactement le but de ce mini projet, caché ses 2 étapes dans un objet afin de faciliter énormément son utilisation :3 La classe Loader_Config est là pour faire l'interface et caché cette double étape
                    • Partager sur Facebook
                    • Partager sur Twitter
                      19 mai 2020 à 14:31:52

                      Faut savoir ce qu'on veut:

                      Une bibliothèque générale, réutilisable dans plusieurs applis, elle ne peut rien savoir sur les bousins spécifiques qu'on veut configurer dans les applis.

                      Donc, deux niveaux, le commun et le spécifique.

                      -
                      Edité par michelbillaud 19 mai 2020 à 16:01:20

                      • Partager sur Facebook
                      • Partager sur Twitter
                      Anonyme
                        20 mai 2020 à 11:33:06

                        salut,

                        Bon je vais pas plus essayer d'aller dans des choses que je ne maîtrise pas du tout :/ Je vais juste retourner un string et ça sera à l'utilisateur de convertir ses valeurs dans les types de son choix :/

                        Merci quand même :3

                        • Partager sur Facebook
                        • Partager sur Twitter
                          20 mai 2020 à 13:33:40

                          Cela n'empêche pas de fournir des outils à l'utilisateur...

                          #include <sstream>
                          #include <stdexcept>
                          #include <unordered_map>
                          #include <string>
                          #include <iostream>
                          
                          template <typename T>
                          struct expected{
                              static char const* name() { return "SHOULDNOTBEUSED"; }
                          };
                          
                          template <> struct expected<std::string> {
                              static char const* name() { return "a string";}
                          };
                          
                          template <> struct expected<int> {
                              static char const* name() { return "an int";}
                          };
                          
                          template <> struct expected<double> {
                              static char const* name() { return "a double";}
                          };
                          // ... + les autres selon les besoins (floats, long, unsigned...)
                          
                          template <typename T> struct decoder {
                              static T decode(std::string const& text, std::string const& name) {
                                  std::istringstream iss(text);
                                  if (T value ; (iss >> value).eof()) {
                                      // eof() => vérifier que le flux chaine est entièrement consommé
                                      // bizarre: je n'ai pas souvenir que c'est comme ça que eof
                                      // fonctionne usuellement
                                      return value;
                                  }
                                  throw std::runtime_error("Cannot extract parameter "+name+
                                          " as "+expected<T>::name()+" from \""+text+"\"");
                              }
                          };
                          
                          template <> struct decoder<std::string> {
                              static std::string const& decode(std::string const& text, std::string const& name) {
                                  return text;
                              }
                          };
                          
                          class Parameters
                          {
                          public:
                              Parameters(std::istream& is) {
                                  std::string line;
                                  while (std::getline(is, line)) {
                                      std::istringstream iss(line);
                                      if (std::string key, text;  std::getline(iss >> key >> std::ws, text)) {
                                          m_params.insert({key, text});
                                      }
                                  }
                              }
                          
                              template <typename T> T get(std::string const& name, T def = T{}) const {
                                  auto const wh = m_params.find(name);
                                  if (wh == end(m_params)) {
                                      return def;
                                  }
                                  // std::cout << "{'"<<name<<"': "<<text<<"} -- " << expected<T>::name() << "\n";
                                  return decoder<T>::decode(wh->second, name);
                              }
                          private:
                              std::unordered_map<std::string, std::string> m_params;
                          };
                          
                          int main()
                          {
                              std::string config =
                                  "v1 12.5\n"
                                  "v2 12\n"
                                  "v3 une chaine\n";
                          
                              std::istringstream iss(config);
                              Parameters p(iss);
                              try
                              {
                                  std::cout << p.get<double>("v1") << "\n";
                                  std::cout << p.get<double>("v2") << "\n";
                                  std::cout << p.get<std::string>("v3") << "\n";
                          
                                  // Les 2 erreurs...
                                  std::cout << p.get<int>("v1") << "\n";
                                  std::cout << p.get<double>("v3") << "\n";
                                  return EXIT_SUCCESS;
                              }
                              catch (std::exception const& e)
                              {
                                  std::cerr << "Error: " << e.what() << "\n";
                              }
                              return EXIT_FAILURE;
                          }
                          // Vim: let $CXXFLAGS='-std=c++17 -Wall -Werror'

                          ce qui donne

                          12.5
                          12
                          une chaine
                          Error: Cannot extract parameter v1 as an int from "12.5"
                          // ou
                          Error: Cannot extract parameter v3 as a double from "une chaine"
                          




                          -
                          Edité par lmghs 20 mai 2020 à 13:36:25

                          • Partager sur Facebook
                          • Partager sur Twitter
                          C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                            20 mai 2020 à 14:00:16

                            Clairement, les types de son choix, ils sont du côté de l'application, pas de la bibliothèque "générique".

                            Ca ne veut pas dire qu'on ne peut rien faire, maintenant qu'on a des lambdas, on peut injecter des traitements à appliquer quand on reconnait certains trucs dans la configuration.

                            -
                            Edité par michelbillaud 20 mai 2020 à 14:01:35

                            • Partager sur Facebook
                            • Partager sur Twitter

                            retour de méthode virtuelle pure de plusieurs type

                            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                            × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                            • Editeur
                            • Markdown