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

Langage C++ Discussion :

Arguments variables et types, comment uniformiser tout ça ?


Sujet :

Langage C++

  1. #1
    Membre averti
    Inscrit en
    Octobre 2006
    Messages
    21
    Détails du profil
    Informations forums :
    Inscription : Octobre 2006
    Messages : 21
    Par défaut Arguments variables et types, comment uniformiser tout ça ?
    (Questions générales :
    - Connaitre le TYPE des arguments multiples.
    - Afficher la VALEUR d'une variable depuis une MACRO.
    )

    Bonjour, j'ai un petit soucis (ou plutôt une question) sur un programme que je programme en C++.
    Mon objectif ici est d'écrire le moins de ligne de code possible.


    J'ai plusieurs méthodes, qui prennent toutes un nombre fixe d'arguments. Les méthodes en question font en revanche toujours la même chose : Stocker les arguments dans une (std::map<string,string>).

    Exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    	#define ARG_LIST_STR(...) getArgNameArray(#__VA_ARGS__)
    	#define MAP_VARGS(...) index_vargs(ARG_LIST_STR(__VA_ARGS__), ##__VA_ARGS__ )
     
    Objet* Classe::ma_fonction_1( int count,  float camembert, float max_id, bool hide ) {
    	Objet* obj = new Objet();
            obj->ajouterMap( MAP_VARGS(count,camembert,max_id,hide) );
            return obj;
    }
     
    Objet* Classe::ma_fonction_2( bool calories,  int roquefort, bool hide ) {....}
    La macro MAP_VARGS fait la chose suivante :
    1. Liste le NOM des paramètres de la fonction pour les stocker en tant que clés dans un std::map<string,string>
    2. Indexe les VALEURS des paramètres en tant que valeurs correspondantes dans le std::map<string,string>

    J'obtiens donc une std::map contenant :
    "count" -> valeur_count
    "camembert" -> valeur_camembert
    etc...

    Je bloque actuellement sur la partie 2, car j'utilise les fonctionnalités de <stdvargs>, qui permet de passer un nombre variable d'arguments à une fonction. Le soucis, c'est que <stdvargs> est limité : On ne peut pas connaître le type des arguments à l'avance (pour cause, ce n'est stocké nulle part pendant l'exécution du programme).
    J'écarte la solution de "caster" les arguments jusqu'à trouver le plus "convenable", car selon les compilateurs/architectures, je risquerais d'avoir des résultats différents. (tailles des types des variables)

    Ma fonction pour les v_args :
    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
     
    std::map<std::string,std::string> index_vargs( std::vector<std::string> keys, ... ) {
     
      std::map<std::string,std::string> parameters;
     
      va_list list;
      va_start(list,keys); 
     
      for( int i=0; i<keys.size(); i++) {
     
        int p = va_arg(list,int);
     
        if ( p != N_UN ) {
     
          parameters[keys.at(i)] = otos(p);
          // otos() est une fonction convertissant n'importe quel type CONNU en string.
     
        }
     
      }
     
      va_end(list); 
      return parameters;
     
    }

    Pour l'instant, je pense que le meilleur moyen serait de "convertir" tous les arguments en std::string ( avec otos() ) AVANT de les passer dans la fonction v_args, mais comme je ne sais pas quel nombre d'arguments convertir à l'avance, le serpent se mord la queue ... :/


    Une méthode qui marche :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    	#define ARG_LIST_STR(...) getArgNameArray(#__VA_ARGS__)
    	#define MAP_VARGS(...) index_vargs(ARG_LIST_STR(__VA_ARGS__), ##__VA_ARGS__ )
     
    Objet* Classe::ma_fonction_1( int count,  float camembert, float max_id, bool hide ) {
    	Objet* obj = new Objet();
            obj->ajouterMap( index_vargs(ARG_LIST_STR(count,camembert,max_id,hide)), otos(count),otos(camember),otos(max_id),otos(hide));
            return obj;
    }
    --> En gros je fais la même chose, mais en passant chaque argument manuellement, après les avoir convertis en string.
    Le problème, c'est que mon objectif est d'éviter ce genre de répétitions inutiles. (il y aura environ 50 fonctions du type "ma_fonction_1"). Je suis sûr qu'il existe des MACROS portables qui permettent plus ou moins d'éviter ça, mais malgré mes recherches, je n'ai rien trouvé.
    J'en appelle donc à votre aide, pour savoir s'il existe des "hacks" (portables), ou si quelquechose m'a échappé.
    ++ A noter que je ne cherche pas de moyen "dynamique" de résoudre mon problème, mais bien un moyen de "découvrir" le type des variables PENDANT LA COMPILATION.

    (l'autre solution serait de générer mon code à partir d'un autre langage, vu que chaque fonction doit se comporter quasiment de la même façon)


    Merci d'avance pour votre aide. Je suis à votre dispo' pour toute explication/éclaircissement.

  2. #2
    Membre Expert

    Avatar de germinolegrand
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Octobre 2010
    Messages
    738
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Octobre 2010
    Messages : 738
    Par défaut
    Macros, macros... ça y'en a mauvais et moins puissant que C++11.

    Donc si je comprends bien tu veux stocker de tout et n'importe quoi dans une seule map ?

    Avoir une centaine de ma_fonction_1 est-il nécessaire ou est-ce seulement un intermédiaire et en avoir une seule qui fasse le boulot de façon générique serait plus agréable ?

  3. #3
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    "Je veux des trucs qui font la même chose, enfin presque", c'est pas synonyme de "par ici les templates"?

    Plutot que otos, pourquoi ne pas utiliser les cast ( operator std::string() ) ou mieux encore, std::ostream?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <typename T>
    std::string toString(const T& s){
    	std::ostringstream oss;
    	oss << s;
    	return oss.str();
    }

  4. #4
    Membre averti
    Inscrit en
    Octobre 2006
    Messages
    21
    Détails du profil
    Informations forums :
    Inscription : Octobre 2006
    Messages : 21
    Par défaut
    Merci pour vos réponses.

    Citation Envoyé par germinolegrand Voir le message
    Macros, macros... ça y'en a mauvais et moins puissant que C++11.

    Donc si je comprends bien tu veux stocker de tout et n'importe quoi dans une seule map ?

    Avoir une centaine de ma_fonction_1 est-il nécessaire ou est-ce seulement un intermédiaire et en avoir une seule qui fasse le boulot de façon générique serait plus agréable ?
    Oui, je dois stocker tous les arguments dans une map. Là où je "triche" (utilisation des macros forcée), c'est que j'utilise le nom des variables comme clés dans ma Map.

    La centaine de ma_fonction_X est nécessaire car ces fonctions seront utilisées par d'autres développeurs. En revanche, elles ont toutes le même comportement (stocker les éléments dans une Map), donc utiliseront la même sous-fonction.

    ------------------------------------------------------------------------

    Citation Envoyé par leternel Voir le message
    "Je veux des trucs qui font la même chose, enfin presque", c'est pas synonyme de "par ici les templates"?

    Plutot que otos, pourquoi ne pas utiliser les cast ( operator std::string() ) ou mieux encore, std::ostream?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <typename T>
    std::string toString(const T& s){
    	std::ostringstream oss;
    	oss << s;
    	return oss.str();
    }

    La fonction "toString" que tu proposes est à la ligne près la même que ma "otos()" (je me suis mal exprimé, ce n'est pas une fonction native).



    J'ai pour le moment trouvé une solution à mon problème, mais elle m'oblige à renseigner pour chaque fonction le nombre d'arguments à "Mapper", ce qui est un peu "dégueulasse".

  5. #5
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 394
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 394
    Par défaut
    L'autre option est d'utiliser le même mécanisme que pour les flux.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  6. #6
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut
    Est-ce que ce qui suit te conviendrait ?

    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
    #include <map>
    #include <string>
    #include <sstream>
    #include <iostream>
     
    #define ADD_TO_MAP(map, ...) add_to_map_impl::add(map, #__VA_ARGS__, __VA_ARGS__)
     
    namespace add_to_map_impl {
        typedef std::map<std::string, std::string> map_type;
     
        template<typename T>
        std::string to_string(const T& t) {
            std::ostringstream ss;
            ss << t;
            return ss.str();
        }
     
        void trim(std::string& name) {
            while (name[0] == ' ' || name[0] == '\t')
                name.erase(0,1);
            while (name[name.size()-1] == ' ' || name[name.size()-1] == '\t')
                name.erase(name.size()-1,1);
        }
     
        template<typename T>
        void add(map_type& m, std::string name, const T& value) {
            trim(name);
            m.insert(std::make_pair(name, to_string(value)));
        }
     
        template<typename T, typename ... Values>
        void add(map_type& m, std::string names, const T& value, Values... values) {
            // La macro fourni le nom des variables en une seule chaine "titi, toto"
            // On coupe donc celle-ci à la première virgule
            // (ok : les noms de variable ne peuvent pas en contenir)
            size_t next_pos = names.find(",");
            std::string name = names.substr(0, next_pos);
            // On élimine les espaces autour du nom de la variable
            trim(name);
            // On supprime le tout de la liste de noms
            names.erase(0, next_pos+1);
            // On ajoute le couple nom/valeur à la map
            m.insert(std::make_pair(name, to_string(value)));
            // On continue avec les autres
            add(m, names, values...);
        }
    }
     
    int main() {
        int bob = 5;
        double alice = 5.2;
     
        std::map<std::string, std::string> mymap;
     
        ADD_TO_MAP(mymap, bob, alice);
     
        for (auto& p : mymap)
            std::cout << p.first << " : " << p.second << std::endl;
     
        // Affiche :
        // alice : 5.2
        // bob : 5
     
        return 0;
    }
    Question performances, on doit traiter la chaine retournée par #__VA_ARGS__ (en séparant à la main les noms des différents arguments). C'est un peu dommage, mais je ne connais pas assez les macro pour savoir comment sérialiser chacun des arguments du __VA_ARGS__ séparément. C'est sans doute possible avec boost, mais bon...

    Sinon, si tu sais que tu n'auras jamais plus de X arguments à stocker en même temps, il est possible d'écrire X macros pour développer le __VA_ARGS__, mais c'est pénible sauf si X est raisonnablement petit, par exemple 4 dans l'exemple qui suit (je me suis fortement inspiré de cet article sur stackoverflow) :
    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
    #include <map>
    #include <string>
    #include <sstream>
    #include <iostream>
     
    #define SERIALIZE_1(arg) #arg, arg
    #define SERIALIZE_2(arg, ...) #arg, arg, SERIALIZE_1(__VA_ARGS__)
    #define SERIALIZE_3(arg, ...) #arg, arg, SERIALIZE_2(__VA_ARGS__)
    #define SERIALIZE_4(arg, ...) #arg, arg, SERIALIZE_3(__VA_ARGS__)
     
    #define COUNT_ARGS_(_1, _2, _3, _4, N, ...) N
    #define COUNT_ARGS(...) COUNT_ARGS_(__VA_ARGS__, 4, 3, 2, 1, 0)
     
    #define CONCATENATE(arg1, arg2) arg1##arg2
    #define SERIALIZE_(N, ...) CONCATENATE(SERIALIZE_, N)(__VA_ARGS__)
    #define SERIALIZE(...) SERIALIZE_(COUNT_ARGS(__VA_ARGS__), __VA_ARGS__)
     
    #define ADD_TO_MAP(map, ...) add_to_map_impl::add(map, SERIALIZE(__VA_ARGS__))
     
    namespace add_to_map_impl {
        typedef std::map<std::string, std::string> map_type;
     
        template<typename T>
        std::string to_string(const T& t) {
            std::ostringstream ss;
            ss << t;
            return ss.str();
        }
     
        template<typename T>
        void add(map_type& m, const std::string& name, const T& value) {
            m.insert(std::make_pair(name, to_string(value)));
        }
     
        template<typename T, typename ... Values>
        void add(map_type& m, const std::string& name, const T& value, Values... values) {
            m.insert(std::make_pair(name, to_string(value)));
            add(m, values...);
        }
    }
     
    int main() {
        int bob = 5;
        double alice = 5.2;
     
        std::map<std::string, std::string> mymap;
     
        ADD_TO_MAP(mymap, bob, alice);
     
        for (auto& p : mymap)
            std::cout << p.first << " : " << p.second << std::endl;
     
        // Affiche :
        // alice : 5.2
        // bob : 5
     
        return 0;
    }
    Le problème de cette version (mis à part le fait qu'elle ne fonctionnera pas pour plus de 4 arguments) est qu'elle nécessite bien plus de macros que la première, ce qui pollue d'avantage le code.

    Quoi qu'il en soit, ces deux versions utilisent les templates variadiques, fonctionnalité introduite par la nouvelle norme C++11 (disponible à partir de gcc4.3 avec l'option "-std=c++0x", je ne sais pas pour VisualC++).

Discussions similaires

  1. Réponses: 4
    Dernier message: 25/01/2013, 08h38
  2. Réponses: 7
    Dernier message: 20/04/2007, 16h42
  3. Réponses: 2
    Dernier message: 08/12/2006, 11h09
  4. Comment effacer toutes les variables de sessions en une fois
    Par dessinateurttuyen dans le forum Langage
    Réponses: 4
    Dernier message: 03/08/2006, 09h20
  5. comment declarer une variable de type date ?
    Par MAJIK_ENIS dans le forum JDBC
    Réponses: 1
    Dernier message: 26/05/2006, 00h02

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