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

Boost C++ Discussion :

Problème avec boost::function et les constructeurs


Sujet :

Boost C++

  1. #1
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut Problème avec boost::function et les constructeurs
    Hello,

    J'ai besoin de créer un map de foncteurs, afin de me créer un objet en fonction d'une chaîne de caractères envoyée.
    Ci-dessous figure un code avec, pour simplifier, des clés de type entier.

    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
    template <typename T>
    T* create()
    {
    	return new T();
    }
     
    int main(int argc, char **argv)
    {
    	const int PARENT=1;
    	const int CHILD=2;
    	using namespace std;
     
    	typedef boost::function< Parent* () > Functor;
     
    	map< int, Functor > map;
     
    	map[PARENT] = &create<Parent>;
    	map[CHILD] = &create<Child>;
     
    	delete map[PARENT]();
    	delete map[CHILD]();
    }
    Tout fonctionne très bien, mais je n'arrive pas à faire une version dont le constructeur des arguments.

    J'ai essayé

    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
    template <typename T>
    T* create(const char* toto)
    {
    	return new T(toto);
    }
     
    int main(int argc, char **argv)
    {
    	const int PARENT=1;
    	const int CHILD=2;
    	using namespace std;
     
    	typedef boost::function< Parent* (const char*) > Functor;
     
    	map< int, Functor > map;
     
    	map[PARENT] = &create<Parent>;
    	map[CHILD] = &create<Child>;
     
    	delete map[PARENT]();
    	delete map[CHILD]();
    }
    mais lorsque j'entre CHILD dans le map, j'obtiens l'erreur suivante :

    error C2440: '='*: impossible de convertir de 'overloaded-function' en 'boost::function<Signature>'

    Merci pour votre aide !

  2. #2
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    C'est parce que ta fonction est surchargée, et qu'il n'est pas possible de déterminer de quelle surcharge il s'agit.

    Écris par exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    	map[PARENT] = (Parent*(*)(const char*))&create<Parent>;
    	map[CHILD] = (Child*(*)(const char*))&create<Child>;
    Il faut aussi bien sûr changer ton delete pour que ça compile.

    À part ça, ton code est nul puisqu'il suit pas le RAII, mais c'est un autre problème.

    Autre chose : fais un exemple minimal qui compile et qui reproduit le problème la prochaine fois, parce que là le problème ne vient pas du code que tu as donné...
    Boost ftw

  3. #3
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Citation Envoyé par loufoque Voir le message
    C'est parce que ta fonction est surchargée, et qu'il n'est pas possible de déterminer de quelle surcharge il s'agit.
    Surchargée ? Comment ça ?
    Tu parles du type de retour ?
    Parce que pour les arguments, je suis supposé avoir toujours la même signature. Après, peut-être que mon code modifié ne correspond pas à cela, mais c'était en tout cas mon intention.

    Écris par exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    	map[PARENT] = (Parent*(*)(const char*))&create<Parent>;
    	map[CHILD] = (Child*(*)(const char*))&create<Child>;
    Il faut aussi bien sûr changer ton delete pour que ça compile.
    J'obtiens alors l'erreur :
    error C2440: 'cast de type'*: impossible de convertir de 'overloaded-function' en 'AmStandard *(__cdecl *)(const char *)'

    À part ça, ton code est nul puisqu'il suit pas le RAII, mais c'est un autre problème.
    Ben je voulais éviter de surcharger encore plus les écritures pour poster ici.

    Autre chose : fais un exemple minimal qui compile et qui reproduit le problème la prochaine fois, parce que là le problème ne vient pas du code que tu as donné...
    Ci-dessous un code qui compile, avec une hiérarchie de classe qui ne prend pas d'argument en constructeur. Je cherche à faire en sorte de pouvoir utiliser une hiérarchie de classe qui prend un nombre quelconque d'arguments.
    Un float, par exemple. Je m'arrangerai après avec mes besoins.

    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
    #include <boost/function.hpp>
    #include <map>
     
    class Parent
    {
    public:
    	Parent();
    	virtual ~Parent();
    };
     
    Parent::Parent(  ) { cout << "CTor Parent" << endl;}
    Parent::~Parent() { cout << "DTor Parent" << endl;}
     
    class Child: public Parent
    {
    public:
    	Child();
    	virtual ~Child();
    };
     
    Child::Child() { cout << "CTor Child" << endl; }
    Child::~Child() { cout << "DTor Child" << endl;}
     
    template <typename T>
    T* create()
    {
    	return new T();
    }
     
    int main(int argc, char **argv)
    {
    	const int PARENT=1;
    	const int CHILD=2;
    	using namespace std;
     
    	typedef boost::function< Parent* (const char*) > Functor;
     
    	map<int, Functor> map;
     
    	map[PARENT] = &create<Parent>;
    	map[CHILD] = &create<Child>;
     
    	delete map[PARENT]();
    	delete map[CHILD]();
    }

  4. #4
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Ton code ne compile pas. La signature de boost::function n'est pas bonne, cela devrait être Parent* () et non Parent* (const char*).

    Code simplifié et converti pour utiliser un argument dans le constructeur.
    Tu ne vois que ça pose aucun problème.
    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
    #include <boost/function.hpp>
    #include <map>
     
    struct Parent
    {
    	Parent(const char*) {}
    };
     
    struct Child : Parent
    {
    	Child(const char*) : Parent(0) {}
    };
     
    template <typename T>
    T* create(const char* toto)
    {
    	return new T(toto);
    }
     
    int main(int argc, char **argv)
    {
    	const int PARENT=1;
    	const int CHILD=2;
    	using namespace std;
     
    	typedef boost::function< Parent* (const char*) > Functor;
     
    	map<int, Functor> map;
     
    	map[PARENT] = &create<Parent>;
    	map[CHILD] = &create<Child>;
     
    	delete map[PARENT]("foo");
    	delete map[CHILD]("foo");
    }
    Le problème, comme je l'ai dit, apparaît quand create est surchargée, ce qu'indique d'ailleurs le message d'erreur.

    Si tu ne sais pas ce qu'est la surcharge (l'overloading en anglais), je te conseille de lire un cours sur le C++.
    Boost ftw

  5. #5
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Merci, cela fonctionne, effectivement.

    Cela dit, j'essaye d'utiliter auto_ptr dans la fonction create, et le compilateur râle quand je veux créer autre chose qu'un Parent :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    template <typename T>
    std::auto_ptr<T> create(const char* toto)
    {
    	return std::auto_ptr<T>(new T(toto));
    }
    main
    {
       ...
       typedef boost::function<std::auto_ptr<Parent> (int)> Functor;
       ...
       map[PARENT] = &create<Parent>;   // OK
       map[CHILD] = &create<Child>;       // warning C4927
    }
    warning C4927: conversion non conforme*; plusieurs conversions définies par l'utilisateur ont été appliquées implicitement lors de l'appel du constructeur 'std::auto_ptr<_Ty>::auto_ptr(std::auto_ptr_ref<_Ty>) throw()'

    Je dois complexifier les ajouts au map :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    	map[PARENT] = (Parent* (*)(int))&create<Parent>;
    	map[CHILD] = (Child*  (*)(int))&create<Child>;

  6. #6
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Bon, c'est bouclé, mais ça n'a pas été sans mal.

  7. #7
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Pour info, l'auto_ptr lève une exception à l'exécution.

  8. #8
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Points : 20 970
    Points
    20 970
    Par défaut
    Le problème de l'auto_ptr, c'est qu'il transfert la responsabilité. C'est donc très risqué d'utiliser un truc comme ça dans ton programme.

  9. #9
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Ce serait tellement plus simple si tu mettais les types des arguments de ton constructeur dans les paramètres template de create...
    Bref faire en sorte que create ait une syntaxe similaire à celle de function.
    Boost ftw

  10. #10
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Bon, j'ai laissé tomber les smart pointers pour l'instant.

    Avant de gérer les fuites de mémoire, j'aimerais comprendre pourquoi une exception est levée quand j'essaye d'utiliser ma tambouille.

    Je remplis tout d'abord mon map :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    map["pouet"] = (classePouet* (*)(const char *))(&create<classePouet>);
    Aui moment de l'utiliser, je fais un find pour vérifier qu'il existe bien un "pouet" dans mon map, puis j'appelle l'instruction (je ne l'affecte à rien, je sais : je veux juste que ça tourne sans planter, pour l'instant) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    map["pouet"]("dudule");   // --> c'est là que l'exception est levée
    Je rappelle le corps de la fonction create :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename T>
    static T* createShader(const char* s)
    {
    	return new T(s);
    }
    classePouet a bien évidemment un constructeur prenant un const char*.

  11. #11
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    J'ai fait une version non templatisée de la fonction correspondant à la chaîne donnée :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    classePouet* createPouet(const char* param1)
    {
    	return new classePouet(param1);
    }
    et je l'ai ajoutée au map comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    map["pouet"] = &createPouet;
    Et ça foire tout pareil.

  12. #12
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Points : 20 970
    Points
    20 970
    Par défaut
    Et sans le '&' ?

  13. #13
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Le précédent test fonctionne en laissant le &, mais en rajoutant
    au début du .h.

    En revanche, dès que je reviens à la version templatisée, ça foire.

  14. #14
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Points : 20 970
    Points
    20 970
    Par défaut
    Tu fais bien connaître au fichier remplissant la map le type de tes classes ? Si l'ajout fait marcher ton code, c'est que ce n'est pas le cas. Or, pour compiler du code, il faut que le compilateur connaisse ce dont il se sert !

  15. #15
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Je fais bien connaître le type ajouté à mon code.

    Mais le problème est bien plus pervers que ça.

    Je récupère la chaîne donnée comme clé via une API qui a une classe MString.
    Cette classe MString a une fonction asChar(), dont le prototype est : const char* asChar() const

    Mon instance de MString s'appelle typeName.

    Soit le morceau de code suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    const char* nomType = typeName.asChar();
    const char* nomType2 = "pouet";
     
    if (strcmp("pouet",nomType) == 0)
        map[nomType]("dudule");   // -> exception
    Telle quelle, la dernière instruction lève une exception.
    Si dans l'utilisation du map je remplace nomType par monType2 -> OK

    Etrange, non ???

    La doc de l'API dit, pour asChar() :
    NOTE: The string should be duplicated if it will be re-used.

    Comment interprétez-vous tout ça ?

  16. #16
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Points : 20 970
    Points
    20 970
    Par défaut
    La durée de vie de la chaîne de caractères n'est pas garantie, donc tu dois la copier avant. Pourquoi ne pas utiliser une string pour ça ?

  17. #17
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Le problème, c'est que tu ne fournis pas des exemples minimaux réduits, mais des bouts de code que tu penses responsables de tes problèmes, alors que bien évidemment ils ne le sont pas.

    C'est quand même pas compliqué : si tu veux une vraie aide, il faut fournir un code complet mais minimal qui reproduit le problème.
    Boost ftw

  18. #18
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Si je remplace MString.asChar() par std::string.c_str(), même comportement...

    Je ne comprend rien.

  19. #19
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Points : 20 970
    Points
    20 970
    Par défaut
    Même combat avec c_str(), la chaîen de caractères n'est pas valide pendant l'éternité. Je te parle de conserver string comme clé de la map.

  20. #20
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    En fait, c'était le même problème.
    A présent, ça marche au poil.

    Je marque le fichier comme "Résolu".

    Merci encore !

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

Discussions similaires

  1. [boost] Problème avec boost::function
    Par Bakura dans le forum Boost
    Réponses: 3
    Dernier message: 22/03/2007, 20h08
  2. Réponses: 2
    Dernier message: 02/08/2006, 16h46
  3. [Tableaux] Problème avec un array et les pseudo frame
    Par azerty53 dans le forum Langage
    Réponses: 6
    Dernier message: 10/05/2006, 14h57
  4. Problème avec l'unicode et les exceptions
    Par Rafy dans le forum C++
    Réponses: 5
    Dernier message: 07/02/2006, 00h52
  5. problème avec strtok pour récupérer les vides
    Par manikou dans le forum MFC
    Réponses: 4
    Dernier message: 02/06/2005, 20h08

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