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 :

Initialisation de tableau de pointeurs de fonction dans le constructeur


Sujet :

C++

  1. #1
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut Initialisation de tableau de pointeurs de fonction dans le constructeur
    Bonjour à tous,

    Alors voilà, je débute vraiment en C++, mais ayant touché un peu aux tableaux de pointeurs sur fonctions en C, je souhaiterais pouvoir faire de même en C++.

    Pour illustrer la chose, un exemple de ce que je fais (j'ai mixé le hpp et cpp) :

    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
     
     
    class test
    {
     
    private:
     
    	int m_var;
    	float m_vfl;
    	// etc... 
     
    public:
     
    	test();
    	test(int a, float b);
    	~test();
     
    	int(test::*ftab[6])(const float v);
     
    	void fonction_qqe(const int i, const float v);
     
    	int fonction1(const float v);
    	int fonction2(const float v);
    	// etc...
    	int fonction6(const float v);
     
    };
     
    // le constructeur : 
     
    test::test(): m_var(0), m_vfl(0.0f) // etc...
    {
    	// initialisation du tableau de pointeur sur fonction
     
    	int(test::*ftab[6])(const float v) = { &test::fonction1, &test::fonction2, /*etc...*/ &test::fonction6 };
     
    }
     
    // et maintenant l'utilisation dans un fonction membre : 
     
     
    void test::fonction_qqe(const int i, const float v)
    {
    	float a(0.0)
     
    	a = (this->*ftab[i])(v);
     
    	// suite de la fonction
    }

    A la vue de ce code, on sais déjà qu'il est mauvais car je l'utilise exactement comme on le ferait en C (à l'exception des namepaces).
    A la compilation, l'utilisation de -Wall me dit seulement que la variable "const float v" du pointeur de fonction n'est pas utilisé. Si j'enlève l'option -Wall, ça compile, mais me sort une belle erreur de segmentation...
    Petit aparté, mes options de compilation sont -W -Wall -Wextra -Werror -O3

    J'ai bien vérifié, il n'y a aucun cas pour lequel "i" (l'index du tableau de pointeur) serait supérieur à l'indice maxi (soit 5).

    J'imagine donc que mon code est foireux dès son initialisation dans le constructeur, mais si je trouve des exemples de code sur le net, je remarque qu'à chaque fois, ces pointeurs sont initialisés en dehors des fonctions membres. Ors, j'en ai besoin au sein de ma classe uniquement.


    Une âme charitable saurait mettre le doigt sur mon (mes) erreur(s) ?

    Merci d'avance !


    EDIT : bon.... Après avoir trituré mon code, finalement, sur un erreur, ça a marché. J'ai simplement supprimé la déclaration dans le *.hpp pour la déclarée et l'initialisée dans mon *.cpp (en dehors du constructeur), et ça marche impeccable. même le -Wall ne me sort aucune erreur.... Néanmoins je suis friand de savoir comment je pourrais mieux faire. car si je veux rajouter une fonction par la suite, cela m'oblige à en modifier une autre.... Aussi, je ne suis pas fan d'initialiser cette dernière en dehors de la classe... Une idée ? Merci d'avance !

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 629
    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 629
    Points : 10 554
    Points
    10 554
    Par défaut
    Et pourtant avec un code à la C (initialisation + déclaration d'un tableau + typedef), cela marche nickel

    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
    #include <iostream> 
     
     
    class Test
    {
    private:
     
        int m_var;
        float m_vfl;
    //  etc... 
     
    public:
     
        Test();
    //  Test(int a, float b);
    //  ~Test();
     
    private:
     
        typedef int(Test::*type_func)(const float v);
     
        const static unsigned int NB_FUNCS = 6;
     
        type_func ftab[6];
     
    public:
     
        void fonction_qqe(const unsigned int i, const float v);
     
        int fonction1(const float v) { return 25; }
        int fonction2(const float v) { return 26; }
        int fonction3(const float v) { return 27; }
        int fonction4(const float v) { return 28; }
        int fonction5(const float v) { return 29; }
        int fonction6(const float v) { return 30; }
    };
     
     
    Test::Test(): m_var(0), m_vfl(0.0f) /* etc... */ {
        ftab[0] = Test::fonction1;
        ftab[1] = Test::fonction2;
        ftab[2] = Test::fonction3;
        ftab[3] = Test::fonction4;
        ftab[4] = Test::fonction5;
        ftab[5] = Test::fonction6;
    }
     
     
    void Test::fonction_qqe(const unsigned int i, const float v) {
        if (i >= Test::NB_FUNCS) { return; }
     
        float a(0.0);
     
        a = (this->*ftab[i])(v);
     
    //  suite de la fonction
     
        std::cout <<  "Test::fonction_qqe - " << a << std::endl;
    }
     
     
    int main()
    {
        Test a;
     
        for(unsigned char func = 0; func < Test::NB_FUNCS; ++func) {
            a.fonction_qqe(func, 7.25);
        }
     
        return 0;
    }

  3. #3
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Bonjour,

    je te remercie pour ce retour. Cependant, je n'utilise pas de typedef dans mon code en C. Pour moi, c'est directement int(*fcptr[6])(float)...

    Je vais néanmoins tester le type d'implémentation que tu proposes.


    Merci !

  4. #4
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Ton problème se situe sur la ligne 35 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int(test::*ftab[6])(const float v) = { &test::fonction1, &test::fonction2, /*etc...*/ &test::fonction6 };
    Tu ne remplis pas ton tableau membre, mais en crée un autre local du même nom.
    Ce qui fait que ton tableau membre qui lui, persiste et est utilisé dans ta fonction membre fonction_qqe, n'a pas été initialisé et pointe vers des adresses totalement aléatoires.

    Tu n'aurais pas eu ce problème si tu l'avais initialisé avec tes autres membres dans la liste d'initialisation (faisable depuis la norme C++11 avec des accolades) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    test::test(): m_var(0), m_vfl(0.0f), ftab { &test::fonction1, &test::fonction2, /*etc...*/ &test::fonction6 }
    { }
    Si ton compilateur ne supporte pas C++11, tu n'auras pas d'autre choix que d'initialiser une à une les valeurs dans le corps de ton constructeur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    test::test(): m_var(0), m_vfl(0.0f)
    {
        ftab[0] = &test::fonction1;
        ftab[1] = &test::fonction2;
        // etc.
    }
    mais je t'encourage vivement à le mettre à jour .

  5. #5
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par BioKore Voir le message
    je débute vraiment en C++, mais ayant touché un peu aux tableaux de pointeurs sur fonctions en C, je souhaiterais pouvoir faire de même en C++
    Bonjour,

    En tant que vraiment débutant en C++, nous te conseillons (je parle au nom des autres, je sais qu'ils seront d'accord) que tu apprennes tout de suite quelque chose d'important : le C et le C++ ne partage presque qu'une syntaxe, une manière de représenter et d'accéder à la mémoire ainsi que quelques fonctions. Les philosophies sont très différentes et les codes que tu produiras avec ces 2 langages le seront donc forcément. S'il s'agit ici d'expérimenter un peu le langage, pas de problème. En revanche, si ton tableau de pointeurs de fonctions est vraiment une solution que tu envisages pour ton programme, alors je te conseille vivement de nous expliquer ton problème original (celui qui t'as mené à ce tableau de pointeurs de fonction). On sera très heureux de t'aider à une solution à l'esprit C++ (et non à l'esprit C).

    A plus !

  6. #6
    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 963
    Points
    32 963
    Billets dans le blog
    4
    Par défaut
    std::array<std::function<int(float)>, 6>
    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.

  7. #7
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par Bousk Voir le message
    std::array<std::function<int(float)>, 6>
    C'est effectivement la transcription la plus directe en C++ du tableau de pointeurs de fonction, mais ce n'est pas certain que ce soit la meilleure, selon ce que le BioKore essaye de faire...
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  8. #8
    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 963
    Points
    32 963
    Billets dans le blog
    4
    Par défaut
    Effectivement pour utiliser std::function il faudrait passer par un std::bind(&test::fonction1, *this, std::placeholders::_1), du coup ici j'y préfèrerais typedef int(test::*Func)(const float); std::array<Func, 6>
    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.

  9. #9
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Bonjour, et merci pour ces retours !


    N'ayant pas l'habitude du C++, lorsque j'ai créé le topic, je n'utilisais pas les listes d'initialisation (dont l'utilité était assez obscure pour moi). Maintenant que j'ai un peu pigé le truc, je passe effectivement par la liste d'initialisation, ce qui est pour le coup, tout à fait logique (même si, question esthétique, je trouve ça "moche") !

    En ce qui concerne la raison pour laquelle je veux utiliser les pointeurs de fonction, c'est assez simple : éviter de faire des switch assez conséquents, traités dans des boucles...
    Lorsque j'ai réalisé ça dans mon code en C, j'ai gagné en lisibilité et en simplicité. J'ai donc naturellement souhaité retrouver le même rapport performances / confort d'utilisation pour mon code en C++.

    Pour détailler un peu plus, mon programme initial est un réseau de neurones (sans prétention), nécessitant donc plusieurs fonctions mathématique différentes selon les neurones et selon le sens de la propagation. Je ne rentrerais pas dans le détail ici, mais en gros, j'ai 6 fonctions mathématiques possible et différentes par neurone (sur 300 neurones), et 6 fonctions permettant de calculer les dérivées des premières. Lors de l'apprentissage, il est nécessaire de faire plusieurs itérations, sur plusieurs exemples ; à chaque itération, pour chaque exemple (60000 exemples dans mon cas), on calcule la sortie de la fonction de chaque neurones, et la dérivée de ces fonctions lors du retour. En gros, on se retrouve avec, pour 300 neurones, 300*60.000 appels pour les fonctions de propagation (un switch pour 6 fonctions) et 300*60.000 appels pour la rétro-propagation (un switch sur 6 fonctions dérivées), soit 36.000.000 d'appels sur des switchs (pour une seule itération).

    La raison pour laquelle j'ai pensé aux pointeurs de fonctions est double : la principale raison est de me passer des appels aux switchs (oui, c'est peut-être bête mais je trouve que ça fait moche dans mon code...), et pourquoi pas, voir si je pouvais gagner un peu en performances. A ce jour, dans mon code en C, une itération est réalisée, selon les fonctions appelées, en ~14 secondes. Avec les pointeurs de fonction, j'ai gagné ~0.01 seconde (difficile à dire) par itération. Ça ne parait pas beaucoup comme ça mais on peut vite passer de 300 à 2000, voire 20000 neurones selon l'application que l'on souhaite, et nécessiter plusieurs milliers d'itérations sur des populations de centaines de milliers d'imputs, donc je suis dans une situation où le moindre millième de seconde gagné a son importance.

    Après, étant débutant, évidemment, je n'ai pas la prétention de faire tout au mieux, et encore moins d'avoir les capacités matérielles de calculer de grosses populations sur de gros réseaux, et il est tout à fait certain qu'il doit exister des solutions plus performantes que les pointeurs de fonction pour ce genre de choses. Cependant, souhaitant apprendre aussi pour le plaisir, je me suis dit que passer par des pointeurs de fonctions me permettrait d'apprendre et de trouver plusieurs solutions pour un seul problème.


    Je sais que le C et le C++ sont des standards différents ; c'est donc pour cela que j'aborde le C++ comme étant complètement nouveau pour moi (il est déjà très différent du C++ que j'ai eu l'occasion de bidouiller il y a 16 ans). Néanmoins, si quelque chose est faisable en C, je sais qu'il est faisable en C++ aussi (et vice/versa, même si c'est plus compliqué dans l'autre sens) ; mais là n'est pas la question.


    Merci encore pour vos retours.

  10. #10
    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 963
    Points
    32 963
    Billets dans le blog
    4
    Par défaut
    Tu as une discussion qui a exactement le même problème et où d'autres solutions sont présentées. Solutions spécifiques au C++ puisque via template qui devraient te permettre de gagner encore quelques perfs en supprimant cette indirection runtime du pointeur de fonction ou switch. https://www.developpez.net/forums/d1.../#post10010242
    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.

  11. #11
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Merci pour e retour ; j'ai effectivement fait un saut sur ce post il y a quelques jours. Je suis prêt à tester le coup des templates sauf que, je ne sais pas ce qu'est un template... Vous me direz si je me trompe mais s'agit-il bien des chevrons permettant de "sélectionner un mode pour la classe" ? Comme dans les std::vector<> c'est bien ça ?

    Je vais me renseigner à ce sujet et voir comment implémenter ça dans mon code sans trop de casse.... Si mon niveau m'est suffisant à l'heure actuelle...

    Je tiens à préciser que, dans mon cas, peu m'importe de passer par des enums ou des #defines (même si, pour le moment, je préfère passer par des #defines).


    Je n'ai plus la source, mais j'avais vu, dans un code en C si je me souviens bien, quelqu'un qui passait par des macros pour définir ses pointeurs de fonction (enfin, des #defines). A l'époque je n'ai pas étudié le sujet plus que ça mais quel serait l'avantage de passer par une telle méthode ? Bonne idée ou pas plus que ça ? De souvenir, cela simplifiait assez bien le code...

    Merci !

  12. #12
    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 963
    Points
    32 963
    Billets dans le blog
    4
    Par défaut
    Oui le template c'est un genre de paramétrage statique (à la compilation) de la classe.
    L'avantage du template c'est que c'est résolu à la compilation. Donc dès la compilation tu peux avoir des erreurs, et les appels peuvent être inlinés, pas d'indirection, de pointeur de fonction etc cachés donc à l'exécution aucun surcoût.

    Pour le code, tu peux faire très simple avec ce que j'ai mis dans l'autre topic:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    template<class Func>
    struct Algo {
      void call(arguments) { Func::exec(arguments); }
    };
    struct Algo1 {
      static void exec(arguments) { ... }
    };
    struct Algo2 {
      static void exec(arguments) { ... }
    };
    Algo<Algo1> algo;
    algo.call(arguments);
    De cette base tu peux étoffer comme bon te semble, en utilisant des macros pour implémenter les différents algos par exemple, ou en utilisant des variadic templates pour les appels aux algos.
    Si Algo n'a pas besoin de membres, call peut être static. Bref t'es libre et à toi d'adapter à ton projet.

    Un code comme
    void Test::fonction_qqe(const unsigned int i, const float v) {
    if (i >= Test::NB_FUNCS) { return; }
    en ajoutant du template tu peux vérifier que l'algo demandé existe sinon une erreur de compilation te prévient. Ca évitera les problèmes "bordel pourquoi il se passe plus rien ?" parce que t'as rajouté une valeur à l'enum mais pas changé le tableau de foncteurs ou autres joyeusetés.
    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.

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

Discussions similaires

  1. Réponses: 1
    Dernier message: 22/12/2009, 11h40
  2. appel pointeur sur fonction dans DLL
    Par dietrich dans le forum Windows
    Réponses: 6
    Dernier message: 24/10/2007, 21h48
  3. [BCB6]Tableau de pointeurs sur fonctions
    Par rtg57 dans le forum C++Builder
    Réponses: 6
    Dernier message: 06/10/2006, 20h49
  4. Tableau de pointeur de fonction
    Par Gryzzly dans le forum C
    Réponses: 7
    Dernier message: 31/12/2005, 10h47
  5. Tableau de pointeurs de fonctions
    Par Alp dans le forum C++
    Réponses: 7
    Dernier message: 29/10/2005, 13h19

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