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 :

[Débat] Gestion des paramètres IN/OUT en C++


Sujet :

C++

  1. #1
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 460
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 460
    Points : 6 064
    Points
    6 064
    Par défaut [Débat] Gestion des paramètres IN/OUT en C++
    Au niveau de la syntaxe d'appel des fonctions, le C++ ne permet pas à l'utilisateur de différencier du premier coup d'œil les paramètres IN des paramètres IN/OUT.

    Exemple :
    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
    // Dans un fichier X :
    void foo(const Type& paramIn, Type& paramInOut);
     
    // Dans un autre fichier Y :
    void uneFonction()
    {
        Type obj1, obj2, *ptr1, *ptr2;
     
        // ...
     
        foo(obj1, obj2);
        if(ptr1 != nullptr && ptr2 != nullptr) {
            foo(*ptr1, *ptr2);
        }
    }
    Sans lire le fichier X, le lecteur du fichier Y ne peut pas deviner que le 1er paramètre de foo est IN tandis que le 2e est IN/OUT.

    Une première solution serait d'adopter la convention suivante :
    • Les paramètres IN/OUT sont toujours passés par pointeur vers type non constant.
    • Les paramètres IN sont passés par défaut par référence constante. Ils sont passés par pointeur vers type constant si et seulement si ce pointeur peut être nul.


    Le code devient alors :
    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
    // Dans un fichier X :
    void bar(const Type& paramIn, Type* paramInOut); // précondition : paramInOut != nullptr
     
    // Dans un autre fichier Y :
    void uneFonction()
    {
        Type obj1, obj2, *ptr1, *ptr2;
     
        // ...
     
        bar(obj1, &obj2);
        if(ptr1 != nullptr && ptr2 != nullptr) {
            bar(*ptr1, ptr2);
        }
    }
    Alors, quand l'utilisateur observe ce code dans le fichier Y, il sait que, selon cette convention, bar ne modifie ni obj1, ni *ptr1. Il n'a pas besoin d'aller chercher cette information dans le fichier X.

    Mais il y a un inconvénient : Dans la version avec foo(*ptr1, *ptr2), grâce à l'étoile, l'utilisateur sait que ptr2 doit être non nul. Par contre, dans la version avec bar(*ptr1, ptr2), l'utilisateur risque d'oublier le test ptr2 != nullptr.
    Pour pallier un peu ce problème, on peut utiliser gsl::not_null (vanté dans cet article), mais ce n'est pas la panacée.

    Une autre solution serait que chaque paramètre IN/OUT soit signalé à chaque fois de manière explicite à l'initiative de l'appelant de la fonction.

    Exemple 1 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // Dans le fichier Y :
    void uneFonction()
    {
        Type obj1, obj2, *ptr1, *ptr2;
     
        // ...
     
        foo(obj1, obj2); // peut modifier obj2 !
        if(ptr1 != nullptr && ptr2 != nullptr) {
            foo(*ptr1, *ptr2); // peut modifier *ptr2 !
        }
    }
    Exemple 2 :
    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
    // Dans le fichier Y :
    void uneFonction()
    {
        Type  obj1_mutable;
        Type  obj2_mutable;
        Type* ptr1_canModify;
        Type* ptr2_canModify;
     
        const Type&         obj1 = obj1_mutable;
        const Type&         obj2 = obj2_mutable;
        const Type* const & ptr1 = ptr1_canModify;
        const Type* const & ptr2 = ptr2_canModify;
     
        // ...
     
        foo(obj1, obj2_mutable);
        if(ptr1 != nullptr && ptr2 != nullptr) {
            foo(*ptr1, *ptr2_canModify);
        }
    }
    L'inconvénient est que l'appelant de la fonction a de fortes chances de ne pas avoir ce genre d'initiative.

    Personnellement, actuellement, je passe les paramètres IN/OUT par référence non constante.
    Si je vois un paramètre IN/OUT qui porte à confusion, j'ajoute un commentaire du style "peut modifier tel paramètre" lors de chaque appel à la fonction.
    Et vous ?

  2. #2
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    J'opterais pour un type spécifique avec un constructeur explicite pour que l'appelant n'ait l'alternative de l'ignorer.

    Plus une fonction de construction pour ne pas se taper la déduction du type: foo(obj1, inout_param(obj2));.

  3. #3
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 460
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 460
    Points : 6 064
    Points
    6 064
    Par défaut
    Bonne idée !

    Ça donnerait quelque chose du genre :

    inout.h :
    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
    #include <type_traits>
     
    template<typename T>
    class inout {
        static_assert(std::is_same< T, typename std::remove_const<T>::type >::value, "Erreur : Le type doit ne pas être constant.");
    private:
        T& m_ref;
    public:
        explicit inout(T& x) : m_ref(x) {}
        T& get() {return m_ref;}
    };
     
    template<typename T>
    inout<T> make_inout(T& x) { // sera inutile en C++17
        return inout<T>(x);
    }
     
    template<typename T>
    class opt_inout {
        static_assert(std::is_same< T, typename std::remove_const<T>::type >::value, "Erreur : Le type doit ne pas être constant.");
    private:
        T* const m_ptr;
    public:
        explicit opt_inout(T* x) : m_ptr(x) {}
        T* get() {return m_ptr;}
    };
     
    template<typename T>
    inout<T> make_opt_inout(T* x) { // sera inutile en C++17
        return opt_inout<T>(x);
    }
    Dans le fichier X :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void foo(const Type& paramIn, inout<Type> paramInOut);
    Dans le fichier Y :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void uneFonction()
    {
        Type obj1, obj2, *ptr1, *ptr2;
     
        // ...
     
        foo(obj1, make_inout(obj2));
        if(ptr1 != nullptr && ptr2 != nullptr) {
            foo(*ptr1, make_inout(*ptr2));
        }
    }
    EDIT : ajout de opt_inout dans inout.h pour prendre en compte le cas des paramètres IN/OUT optionnels (pointeurs vers type non constant qui peuvent être nuls).

  4. #4
    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
    Certains coding styles (ceux de google par exemple) imposent ta solution à base de pointeur pour in/out, référence pour in.

    J'avoue avoir du mal à être convaincu par cet argument, pour les raisons suivantes :
    - Le cas le plus courant de paramètre in/out est géré par la syntaxe objet : f(a /*in/out*/, b); => a.f(b); (petit aparté amusant : parmi les personnes que j'ai pu rencontrer qui disent qu'il faudrait distinguer au niveau de l'appel les passages par références constante ou non, je n'ai jamais rencontré personne voulant différencier au niveau de l'appel une fonction membre const d'une non const, alors que c'est exactement le même problème).
    - En dehors de ce cas, les paramètres in/out sont généralement assez rares, et les paramètres n'étant que out sont généralement avec profit transmis par la valeur de retour de la fonction.
    - Les exemples comme tu as montré avec des fonction foo et bar peuvent sembler convaincants, mais généralement, dans du vrai code, le nom de la fonction et éventuellement de ses arguments permet de lever le doute sans aucun effort. Je ne suis encore jamais tombé sur une exemple où je ressentre une nécessité d'expliciter la modification du paramètre (sauf dans du vieux code où une référence non constante est utilisée pour un paramètre out)...

    Si je prends un exemple, le premier qui me vient (il n'y a pas tellement de passages par référence non constante dans la lib standard...) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    std::list<int> l1, l2;
    /*...*/
    auto it = l1.find(/* ...*/);
    l1.splice(it, l2);
    Le gars qui n'a pas compris que l2 va être modifié par cet appel n'a clairement pas compris ce que fait la fonction splice... Et si tel est le cas, je ne pense pas qu'ajouter quelque décoration que ce soit au site d'appel va l'aider.
    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.

  5. #5
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 460
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 460
    Points : 6 064
    Points
    6 064
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    petit aparté amusant : parmi les personnes que j'ai pu rencontrer qui disent qu'il faudrait distinguer au niveau de l'appel les passages par références constante ou non, je n'ai jamais rencontré personne voulant différencier au niveau de l'appel une fonction membre const d'une non const, alors que c'est exactement le même problème
    J'y avais pensé, mais le besoin est moins fort dans le cas du paramètre this des fonctions membres d'une classe.
    Le paramètre a est souvent IN/OUT dans un appel de la forme a.f(b) mais rarement dans un appel de la forme f(a, b).
    Du coup, quand on analyse de manière rapide et superficielle le code pour chercher où la variable a est modifiée, on risque d'ignorer les passages comme f(a, b) si le nom de la fonction ne sous-entend pas que a est IN/OUT.

    Citation Envoyé par JolyLoic Voir le message
    - Les exemples comme tu as montré avec des fonction foo et bar peuvent sembler convaincants, mais généralement, dans du vrai code, le nom de la fonction et éventuellement de ses arguments permet de lever le doute sans aucun effort. Je ne suis encore jamais tombé sur une exemple où je ressentre une nécessité d'expliciter la modification du paramètre (sauf dans du vieux code où une référence non constante est utilisée pour un paramètre out)...
    L'exemple que je vais donner est très général mais, dans un programme d'une interface graphique, que penses-tu du cas d'un constructeur d'une classe qui représente une fenêtre ?
    Les éventuelles infos que la fenêtre peut modifier sont dans des paramètres IN/OUT. Les autres paramètres sont IN.

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 627
    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 627
    Points : 10 548
    Points
    10 548
    Par défaut
    Et pourquoi pas "un truc à l'ancienne" couplé avec tes recommandations

    Le truc à l'ancienne:
    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
    /** 
     * This is a definition which has sole purpose of helping readability.
     * It indicates that formal parameter is an input parameter.
     */
    #ifndef IN
    #define IN
    #endif
     
    /** 
     * This is a definition which has sole purpose of helping readability.
     * It indicates that formal parameter is an output parameter.
     */
    #ifndef OUT
    #define OUT
    #endif
     
    /** 
     * This is a definition which has sole purpose of helping readability.
     * It indicates that formal parameter is both input and output parameter.
     */
    #ifndef INOUT
    #define INOUT
    #endif
     
    /** 
     * This is a definition which has sole purpose of helping readability.
     * It indicates that formal parameter is an optional parameter.  
     */
    #ifndef OPTIONAL
    #define OPTIONAL
    #endif

    Et à l'utilisation:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    void bar(
            IN const Type& param_01,
            INOUT Type* param_02);

    Après on peut étendre avec des INOUT_NOT_NULL par exemple et tester si l'IDE les garde lorsqu'il affiche dans son info-bulle le prototype de la fonction

  7. #7
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Hello

    Ce n'est pas vraiment un débat, mais plutôt une question de préférences en terme de conventions. L'argument principal de Google est : le code doit être optimisé pour la lecture, pas pour l'écriture. Pour ma part, comme Loïc, l'argument me plait mais je ne suis pas convaincu par une application aussi extrême. Du reste, les paramètres IN/OUT sont en effet assez rare dans du code bien conçu. Les paramètres OUT vont le devenir encore plus avec les structured bindings que C++17 va introduire. Et, enfin l'absence de const et de pointeurs dans d'autres langages rend l'usage de IN/OUT déconseillé et leur distinction impossible "par pointeur" dans ces langages. Ce n'est pas nécessairement une bonne idée, du coup, de le faire en C++, car ça rajoute encore un coût d'apprentissage.

    Pour ma part, je n'ai jamais de IN/OUT dans mon code alors ça ne me pose pas trop de problèmes. Quant aux macros proposés par foetus, je ne vois pas trop l'utilité, c'est un peu de l'over engineering à mon goût.

    Edit: correction suggérée par Pyramidev appliquée.
    Find me on github

  8. #8
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Je suis d'accord avec JolyLoic et jblecanard.
    Si on veut vraiment faire une distinction, la solution de jo_link_noir me paraît plus appropriée.

    Petite remarque (1) :
    Citation Envoyé par jblecanard Voir le message
    les paramètres IN/OUT sont en effet assez rare dans du code bien conçu.
    Je dirais plus un truc du genre :
    « les paramètres IN/OUT difficilement indentifiables sont en effet assez rare dans du code bien conçu ».
    Un des exemples les plus répendu est l'opérateur std::ostream & <<(std::ostream &, T const & t).

    Il s'agit de la remarque de :
    Citation Envoyé par JolyLoic Voir le message
    le nom de la fonction et éventuellement de ses arguments permet de lever le doute sans aucun effort
    Petite remarque (2) :
    La distinction grâce à un pointeur ne fonctionne qu'avec un niveau de profondeur et donne du code non uniforme. Voici un exemple :
    Déclaration :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void foo(T const & a, T * b);
    Appel :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    foo(a, &b); // On voit bien le &b
    Définition :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void foo(T const & a, T * b)
    {
        // ...
        foo(a, b); // On ne voit plus le &b
        // ...
    }
    Petite remarque (3) :
    Si les règles définissent que l'utilisation d'un pointeur apporte la sémantique de in/out.
    Comment se caractérisent les autres sémantiques ?
    Par exemples, pour le déplacement, le partage et la responsabilité mémoire.

  9. #9
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 460
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 460
    Points : 6 064
    Points
    6 064
    Par défaut
    @foetus : Ta macro INOUT est une bonne alternative au commentaire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
        foo(obj1, INOUT obj2);
        if(ptr1 != nullptr && ptr2 != nullptr) {
            foo(*ptr1, INOUT *ptr2);
        }
    A un moment, j'avais pensé à ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
        foo(obj1, /* INOUT */ obj2);
        if(ptr1 != nullptr && ptr2 != nullptr) {
            foo(*ptr1, /* INOUT */ *ptr2);
        }
    Mais c'était moins bien, car c'était plus difficile de masquer des portions de code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    /*
        foo(obj1, /* INOUT */ obj2);
        if(ptr1 != nullptr && ptr2 != nullptr) {
            foo(*ptr1, /* INOUT */ *ptr2);
        }
    */
    Cependant, je préfère la solution de jo_link_noir.

    Ta macro IN serait trop présente dans le code à mon goût.

    A la place de ta macro OUT, je préfère mettre le paramètre dans le retour de la fonction, même si cela implique de retourner une structure. L'avantage du retour de la fonction, c'est qu'il n'y a pas d'ambigüité possible : c'est forcément OUT.

    Je ne vois pas l'intérêt de la macro OPTIONAL en C++.
    En langage C, ça me semble bien car on est inondé de pointeurs. Quand la majorité des pointeurs doivent ne pas être nuls, il est pertinent de notifier ceux qui peuvent l'être.
    Par contre, en C++, quand un pointeur doit ne pas être nul, on peut souvent le remplacer par une référence. Dans les quelques cas restants, on peut utiliser std::reference_wrapper (référence réassignable) ou, éventuellement, gsl::not_null.

    Citation Envoyé par jblecanard Voir le message
    Du reste, les paramètres IN/OUT sont en effet assez rare dans du code bien conçu. Ils vont le devenir encore plus avec les structured bindings que C++17 va introduire.
    Je vois un rapport entre les structured bindings du C++17 et les paramètres OUT, mais quel est le rapport avec les paramètres IN/OUT ?

    Citation Envoyé par Ehonn Voir le message
    Si les règles définissent que l'utilisation d'un pointeur apporte la sémantique de in/out.
    Comment se caractérisent les autres sémantiques ?
    Par exemples, pour le déplacement, le partage et la responsabilité mémoire.
    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
    //////////////
    // Déplacement
     
    // Déclaration
    void f(Type&& param);
    void g(std::unique_ptr<      Type> param);
    void h(std::unique_ptr<const Type> param);
     
    // Appels
    Type                  x(blabla);
    std::unique_ptr<Type> y(new TypeOuClasseQuiDeriveDeType(blabla));
    std::unique_ptr<Type> z(new TypeOuClasseQuiDeriveDeType(blabla));
    f(std::move(x));
    g(std::move(y));
    h(std::move(z));
     
    // Appels avec un objet temporaire :
    f(Type(blabla));
    g(std::make_unique<TypeOuClasseQuiDeriveDeType>(blabla));
    h(std::make_unique<TypeOuClasseQuiDeriveDeType>(blabla));
     
    ////////////////////////////////////////////////////
    // Partage de la responabilité de détruire la donnée
     
    // Déclaration
    void foo(std::shared_ptr<      Type> param);
    void bar(std::shared_ptr<const Type> param);
     
    // Appels
    auto x_shared = std::make_shared<TypeOuClasseQuiDeriveDeType>(blabla);
    f2(x_shared);
    g2(x_shared);

  10. #10
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Citation Envoyé par Pyramidev Voir le message
    Je vois un rapport entre les structured bindings du C++17 et les paramètres OUT, mais quel est le rapport avec les paramètres IN/OUT ?[/CODE]
    Exact, ça ne s'applique qu'aux OUT.

    J'apprécie la remarque de Ehonn sur la profondeur du pointeur, c'est une vraie faiblesse technique de cette convention.
    Find me on github

  11. #11
    Expert éminent sénior

    Avatar de dragonjoker59
    Homme Profil pro
    Software Developer
    Inscrit en
    Juin 2005
    Messages
    2 045
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Software Developer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 2 045
    Points : 11 368
    Points
    11 368
    Billets dans le blog
    10
    Par défaut
    Citation Envoyé par Pyramidev Voir le message
    A la place de ta macro OUT, je préfère mettre le paramètre dans le retour de la fonction, même si cela implique de retourner une structure. L'avantage du retour de la fonction, c'est qu'il n'y a pas d'ambigüité possible : c'est forcément OUT.
    Du coup, ça lève (selon moi) l'ambiguïté initiale, les IN sont en réf constantes, les IN/OUT en réf non constantes, et c'est réglé?
    Si vous ne trouvez plus rien, cherchez autre chose...

    Vous trouverez ici des tutoriels OpenGL moderne.
    Mon moteur 3D: Castor 3D, presque utilisable (venez participer, il y a de la place)!
    Un projet qui ne sert à rien, mais qu'il est joli (des fois) : ProceduralGenerator (Génération procédurale d'images, et post-processing).

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Il n'y a qu'à regarder std::getline() pour voir le problème des paramètres modifiés par la fonction.
    C'est pour ça que je préfère le modèle C#, où c'est explicitement signalé lors de l'appel; malheureusement il serait difficilement possible de causer ça en C++ sans briser la compatibilité. J'aime la proposition du template inout_param par contre, je vais probablement garder ça sous le coude pour mon propre code.
    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.

  13. #13
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 460
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 460
    Points : 6 064
    Points
    6 064
    Par défaut
    Citation Envoyé par dragonjoker59 Voir le message
    Du coup, ça lève (selon moi) l'ambiguïté initiale, les IN sont en réf constantes, les IN/OUT en réf non constantes, et c'est réglé?
    Le problème initial était :
    Citation Envoyé par Pyramidev Voir le message
    Au niveau de la syntaxe d'appel des fonctions, le C++ ne permet pas à l'utilisateur de différencier du premier coup d'œil les paramètres IN des paramètres IN/OUT.

    Exemple :
    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
    // Dans un fichier X :
    void foo(const Type& paramIn, Type& paramInOut);
     
    // Dans un autre fichier Y :
    void uneFonction()
    {
        Type obj1, obj2, *ptr1, *ptr2;
     
        // ...
     
        foo(obj1, obj2);
        if(ptr1 != nullptr && ptr2 != nullptr) {
            foo(*ptr1, *ptr2);
        }
    }
    Sans lire le fichier X, le lecteur du fichier Y ne peut pas deviner que le 1er paramètre de foo est IN tandis que le 2e est IN/OUT.
    En lisant simplement le fichier Y, on ne voit pas que obj1 et *ptr1 sont passés par référence constante tandis que obj2 et *ptr2 sont passés par référence non constante.

  14. #14
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    780
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2006
    Messages : 780
    Points : 1 176
    Points
    1 176
    Par défaut
    Perso, je crois que c'est une solution compliquée pour un faux problème.

    Si une fonction est un peu complexe (autre chose qu'un getter/setter), on va être obligé d'aller voir la documentation pour savoir exactement ce qu'il s'y passe. Et même avec les getter/setter, si c'est mal fait, on pourrait avoir des surprises

    Parce que sinon, hein, on peut écrire:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    mafonction( /*param IN*/ param, /*param IN/OUT*/ param2 );
    mais finalement, dans les cas où ça peut poser problème parce que c'est pas évident, rien n'empêche un petit:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
     
    /* Attention, la modif de param2 */
    mafonction( param, param2);
    pour les rares cas où c'est problématique?

  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 704
    Points
    2 704
    Par défaut
    Mais quel éditeur utilisez-vous donc ?

    Sur le mien, si j'ai un doute en lisant un appel de fonction, il me suffit de passer la souris dessus pour voir sa signature dans un pop-up.
    Et la question est réglée.

  16. #16
    Membre éclairé Avatar de Matthieu76
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Mars 2013
    Messages
    568
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

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

    Informations forums :
    Inscription : Mars 2013
    Messages : 568
    Points : 890
    Points
    890
    Par défaut Je suis pas expert mais ...
    Moi je trouve que la méthode de foetus est pas trop mal, de plus j'ai l'impression que c'est la méthode qu'utilise Microsoft.
    En tout ça quand je vois cette méthode je trouve ça très claire.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    DWORD WINAPI GetSecurityInfo(
      _In_      HANDLE               handle,
      _In_      SE_OBJECT_TYPE       ObjectType,
      _In_      SECURITY_INFORMATION SecurityInfo,
      _Out_opt_ PSID                 *ppsidOwner,
      _Out_opt_ PSID                 *ppsidGroup,
      _Out_opt_ PACL                 *ppDacl,
      _Out_opt_ PACL                 *ppSacl,
      _Out_opt_ PSECURITY_DESCRIPTOR *ppSecurityDescriptor
    );

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Citation Envoyé par oodini Voir le message
    Mais quel éditeur utilisez-vous donc ?

    Sur le mien, si j'ai un doute en lisant un appel de fonction, il me suffit de passer la souris dessus pour voir sa signature dans un pop-up.
    Et la question est réglée.
    1. L'objectif, c'est de voir du premier coup d'œil, donc "passer la souris dessus" ne remplit pas cet objectif.
    2. Bonne chance pour les codes sur dvp, car à ma connaissance aucun navigateur ne fait ça. Ensuite, si tu copie-colles le code, il te faut un éditeur plus lourd que Notepad++ (qui ne fait pas de reconnaissance) et plus léger que Visual Studio, qui exige un projet avant de le faire.
    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.

  18. #18
    Expert éminent sénior

    Avatar de dragonjoker59
    Homme Profil pro
    Software Developer
    Inscrit en
    Juin 2005
    Messages
    2 045
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Software Developer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 2 045
    Points : 11 368
    Points
    11 368
    Billets dans le blog
    10
    Par défaut
    Ma préférence reste du coup sur la solution proposés par jo_link_noir, solution que j'utilise dans un autre contexte (génération de code GLSL depuis le C++).
    C'est une solution qui permet de voir, à l'appel, le type de passage de paramètre. Par contre je ne le ferais pas pour IN, mais pour IN/OUT seulement, je pense (pour juste supprimer l'ambigüité)
    Si vous ne trouvez plus rien, cherchez autre chose...

    Vous trouverez ici des tutoriels OpenGL moderne.
    Mon moteur 3D: Castor 3D, presque utilisable (venez participer, il y a de la place)!
    Un projet qui ne sert à rien, mais qu'il est joli (des fois) : ProceduralGenerator (Génération procédurale d'images, et post-processing).

  19. #19
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    780
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2006
    Messages : 780
    Points : 1 176
    Points
    1 176
    Par défaut
    Mais qu'est ce qui empêche la personne d'écrire un IN à la place d'un INOUT?

    Vous faites vraiment confiance au code "à la première lecture" sans aller voir au moins la signature des fonctions appelés?

  20. #20
    Expert éminent sénior

    Avatar de dragonjoker59
    Homme Profil pro
    Software Developer
    Inscrit en
    Juin 2005
    Messages
    2 045
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Software Developer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 2 045
    Points : 11 368
    Points
    11 368
    Billets dans le blog
    10
    Par défaut
    La première solution de jo_link_noir n'est pas celle avec les macros, mais celle avec les types intermédiaires, donc là on ferait surtout confiance au compilateur, en fait (comme d'hab, quoi)
    Si vous ne trouvez plus rien, cherchez autre chose...

    Vous trouverez ici des tutoriels OpenGL moderne.
    Mon moteur 3D: Castor 3D, presque utilisable (venez participer, il y a de la place)!
    Un projet qui ne sert à rien, mais qu'il est joli (des fois) : ProceduralGenerator (Génération procédurale d'images, et post-processing).

Discussions similaires

  1. Gestion des paramètres dans le temps
    Par Terminator dans le forum SQL
    Réponses: 8
    Dernier message: 06/05/2009, 15h34
  2. Gestion des paramètres
    Par delavega dans le forum ActionScript 1 & ActionScript 2
    Réponses: 0
    Dernier message: 21/04/2009, 22h03
  3. [bash / sh] Mauvaise gestion des paramètres d'entrée
    Par Rei Angelus dans le forum Shell et commandes GNU
    Réponses: 7
    Dernier message: 05/04/2008, 07h08
  4. [MFC][VC6.0] Gestion des paramètres régionaux
    Par Yellowmat dans le forum MFC
    Réponses: 3
    Dernier message: 11/01/2008, 12h04
  5. [Conseils généraux] Gestion des paramètres
    Par Julien Dufour dans le forum Access
    Réponses: 1
    Dernier message: 02/05/2006, 12h04

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