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 :

argument de fonction/méthode de "type" void*


Sujet :

C++

  1. #1
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2016
    Messages
    277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2016
    Messages : 277
    Points : 946
    Points
    946
    Par défaut argument de fonction/méthode de "type" void*
    Bonjour,

    est-il possible de déclarer pour un argument de fonction/méthode le "type" void* afin de pouvoir lui transmettre un pointeur de n'importe quel type?
    Par exemple une fonction faitTruc déclarée comme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int faitTruc(int arg1, void* arg2)
    Mon compilateur se plaint.

    merci

    cordialement

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 113
    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 113
    Points : 32 960
    Points
    32 960
    Billets dans le blog
    4
    Par défaut
    Oui, mais en C++ faut avoir une sacrée bonne raison de le faire quand tu peux tout simplement faire une fonction template et avoir le compilateur qui vérifie les types et permet d'attraper de potentielles erreurs.
    Sinon, pour avoir de l'aide sur un message d'erreur, la rumeur dit que donner le message d'erreur en question (tel quel et non modifié) est la meilleure approche.
    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
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2016
    Messages
    277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juin 2016
    Messages : 277
    Points : 946
    Points
    946
    Par défaut
    Merci pour ta réponse.
    Mais je ne te trouve pas hyper sympa au niveau de ta façon de t'exprimer, cela fait deux fois que je le remarque aujourd'hui. Nous sommes sur un forum d'entraide, on poste forcément sur des choses que l'on ne connaît pas.

  4. #4
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 564
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    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 564
    Points : 7 640
    Points
    7 640
    Par défaut
    Bonjour,

    @chezkele, la réponse a ta première question est Oui.

    Quant à ce qu'en dit ton compilateur, ce que je peux répondre est :
    Citation Envoyé par Bousk Voir le message
    Sinon, pour avoir de l'aide sur un message d'erreur, la rumeur dit que donner le message d'erreur en question (tel quel et non modifié) est la meilleure approche.
    J'espère que ma réponse sera assez sympa.

  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 678
    Points
    13 678
    Billets dans le blog
    1
    Par défaut
    Utiliser des void* est une technique du C.

    En C++, on préfèrera très largement utiliser une fonction template et prendre une référence (constante si possible) en paramètre. Si arg1 te sert à savoir quel est le type de arg2, je te conseille de regarder std::variant.

  6. #6
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Salut,
    Citation Envoyé par Chezkele Voir le message
    Bonjour,

    est-il possible de déclarer pour un argument de fonction/méthode le "type" void* afin de pouvoir lui transmettre un pointeur de n'importe quel type?
    Par exemple une fonction faitTruc déclarée comme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int faitTruc(int arg1, void* arg2)
    La réponse à ta question est "oui", mais la véritable question à se poser est :
    Pourquoi voudrais tu le faire
    Car il faut bien comprendre qu'une fonction n'est que le moyen que le langage nous donne de définir un comportement particulier. Comportement que l'on souhaite voir exécuté dans certaines situations bien particulières, parce qu'il aura été appelé au travers, justement, d'autres comportements tout aussi particuliers.

    Il faut également comprendre que les paramètres / arguments transmis à une fonction ont uniquement pour but de nous permettre de fournir à la fonction des informations qu'elle (la fonction) est incapable de récupérer par elle-même et dont elle a besoin pour pouvoir travailler "correctement".

    Or, si ces données sont nécessaires pour permettre à la fonction de travailler, ben, on peut décemment estimer que si on ignore tout de la nature même (du type réel) du paramètre fournit -- car c'est ce que représente réellement ton void * -- l'information transmise n'a, en définitive, que peu de sens car:
    1. ou bien, nous n'avons aucun moyen de savoir si l'information fournie correspond à la donnée dont la fonction a besoin pour travailler
    2. ou bien, nous sommes obligés de mettre en place une logique qui permette de s'assurer que l'information transmise correspond effectivement à un des types de données que la fonction est susceptible de traiter

    Il se fait que C++ nous donne largement les moyens de résoudre ce genre de problèmes et d'éviter tous les inconvénients liés à ces deux points, par exemple
    par la surcharge de fonctions
    Contrairement au C, C++ nous autorise parfaitement à définir plusieurs fonctions portant le même nom, pour autant que chaque "version" de la fonction en question utilise un nombre ou des types de paramètre(s) différents.

    Ainsi, il n'y a aucun problème à avoir des fonctions proches de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    void faisTruc(int );  // version 1
    void faisTruc(double); // version 2
    void faisTruc(std::string); // version 3
    dans le même code, et qui seront utilisées dans le même code, tout en s'évitant les problèmes liés au fait de déterminer si le paramètre transmis c'est un int, un double ou un std::string.

    Nous avons donc bel et bien la possibilité d'avoir un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main(){
        int i= 10;
        faisTruc(i); // appelle la version 1 de la fonction
        double pi = 3.1415926;
        faisTruc(pi); // appelle la version 2 de la fonction
        std::string str = "Salut tout le monde";
        faisTruc(str); // appelle la version 3 de la fonction
     
    }
    par l'héritage et le polymorphisme (d'inclusion) que nous permet l'approche orientée objets
    Si il y a du sens à avoir une hiérarchie de classes, tu peux tout à fait envisager de créer plusieurs classes selon un modèle proche de
    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
    classe Base{
    public:
       /* on peut construire un objet de type Base */
        Base() = default;
        /* on ne veut en aucun cas que ce qui passe pour être de type Base
         * puisse être copié ou assigné
         */
        Base(Base const &) = delete;
        Base & operator = (Base const &) = delete;
        /* On veut que toute donnée "passant pour être de type Base"
         * puisse être détruite
         */
        virtual ~Base() = default;
        /* Voici une fonction dont le comportement doit s'adapter au type réel de la donnée
         */
       virtual void jaiDitFaisTruc() /* const */;
       /* d'autres choses peuvent suivre */
    };
    /* Tout objet de type A est un objet pouvant "passer pour être" de type Base
     * (dans le respect du LSP)
     */
    class A : public Base{
    public:
        /* on défini un comportement spécifique pour le type A */
        void jaiDitFaisTruc() /* const */ override;
    };
    /* Tout objet de type B est un objet pouvant "passer pour être" de type Base
     * (dans le respect du LSP)
     */
     
    class B : public Base{
    public:
        /* on défini un comportement spécifique pour le type A */
        void jaiDitFaisTruc() /* const */ override;
    };
    On peut alors partir du principe que chaque objet "passant pour être de type Base" sait exactement quel est son type réel (le type qui a été utilisé lors de la création de l'objet), et nous pouvons sans aucun problème créer une fonction s'attendant à recevoir une référence (ou un pointeur) sur un objet "passant pour être de type Base" qui utilisera le comportement (spécifique au type réel de l'objet) de la fonction jaiDitFaisTruc(), rendue accessible depuis le type Base:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void faisTruc(Base /* const*/  & b){
         b.jaiDitFaisTruc(); // la "version" de jaiDitFaisTruc dépendra
                            // du type réel de b
    }
    que nous pourrons appeler en lui transmettant n'importe quel objet "pouvant passer comme étant" de type Base
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    int main(){
        Base laBase;
        faisTruc(laBase); //faisTruc utilisera la "version" spécifique à un objet de type Base
        A leA;
        faisTruc(leA); //faisTruc utilisera la "version" spécifique à un objet de type A
        B leB;
        faisTruc(leB); //faisTruc utilisera la "version" spécifique à un objet de type B
    }
    par une approche "générique" du problème
    Il arrive, dans certaines circonstances, que l'on veuille dire au compilateur quelque chose qui se rapproche de
    Je ne sais pas encore quel sera effectivement le type de la donnée qui sera manipulée

    Par contre, je sais très bien comment cette donnée devra être manipulée
    Hé bien, le C++ nous permet d'exprimer cette approche à travers de ce que l'on appelle les [i]template[/].

    Cela consiste -- je simplifie à l'extrême ici -- à indiquer au compilateur que l'on va utiliser un "type inconnu de donnée" auquel nous donnons un nom "par convenance" et qui va remplacer ... tous les types de données "compatibles" avec l'usage que l'on en fait. Cela se fait sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <typename T> // je précise qu'il s'agit "d'un modèle" de fonction, qui va manipuler le type que j'appellerai T
    void faisTruc(T param){ // la fonction prend un objet de type T en paramètre et qui est appelé ici "param"
        /* tout ce qui doit être fait */
    }
    Par la suite, je peux "simplement" appeler la fonction faisTruc avec n'importe quel type de donnée "compatible" avec l'usage qui en est fait (comprends: qui soit susceptible d'être manipulé de la manière prévue par la fonction), avec un code qui, dans le cas d'espèce, pourrait être "aussi simple" que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    int main(){
        int i= 10;
        faisTruc(i); // appelle explicitement faisTruc avec un int
        double pi = 3.1415926;
        faisTruc(pi); // appelle explicitement faisTruc avec un double
        std::string str = "Salut tout le monde";
        faisTruc(str); // appelle explicitement faisTruc avec une std::string
    }
    Pour être complet:
    On peut discuter "à l'envi" quant à savoir s'il vaut mieux choisir une possibilité plutôt qu'une autre, voire, pourquoi pas, un mélange des trois.

    Il faut juste prendre en compte la différence majeure qui existe entre la deuxième et la troisième possibilité invoquée: la deuxième (l'approche "orientée objets") va "sélectionner" la "version" adéquate de la fonction jaiDitFaisTruc à l'exécution, alors que la troisième (l'approche "générique") le fera lors de la compilation, avec comme conséquence que le compilateur pourra émettre un message d'erreur si le type de l'information qu'on lui a transmise en paramètre est "incompatible" avec l'usage qui en est fait (comprends: qu'il ne permet pas de faire ce que l'on s'attend à pouvoir en faire).

    Mainteant que tu as -- je l'espère -- une vision un peu plus complète des moyens mis à ta disposition pour te passer du besoin de transmettre un void *, te reste-t-il vraiment "une bonne raison", une raison "valable et cohérente" de vouloir le faire
    Citation Envoyé par Chezkele Voir le message
    Mon compilateur se plaint.
    Si ton compilateur se plaint, c'est, de toute évidence, que tu as fait quelque chose "de travers", ou, du moins, d'une manière que le compilateur n'a pas comprise.

    Seulement, comme tu ne nous dis ni ce que tu as fait (tu aurais pu nous donner un code minimum compilable correspondant à ce que tu as fait, cela nous aurait aidé ) ni de quoi le compilateur se plaint (tu aurais pu nous donner le message d'erreur du compilateur), il va nous être vachement difficile de t'expliquer ce que tu as fait "de travers".

    La remarque de Bousk, aussi "agressive" peut-elle sembler (et encore, il a été gentil sur ce coup ), est donc tout à fait pertinente et pleine de bon sens
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

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

Discussions similaires

  1. Réponses: 14
    Dernier message: 18/07/2011, 21h00
  2. Réponses: 5
    Dernier message: 01/02/2011, 15h34
  3. Fonction Replace pour quote
    Par piero53 dans le forum ASP.NET
    Réponses: 7
    Dernier message: 17/11/2009, 01h19
  4. Type de class et arguments pour fonctions et new
    Par Alfred12 dans le forum C++
    Réponses: 15
    Dernier message: 19/01/2007, 01h02

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