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 :

Valeur template par défaut pour des classes de politique


Sujet :

C++

  1. #1
    Membre averti Avatar de Seabirds
    Homme Profil pro
    Post-doctoral fellow
    Inscrit en
    Avril 2015
    Messages
    294
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Post-doctoral fellow
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Avril 2015
    Messages : 294
    Points : 341
    Points
    341
    Par défaut Valeur template par défaut pour des classes de politique
    Bonjour à toutes et à tous,

    J'essaie de décomposer une classes en politiques, et comme c'est la première fois que j'ai plus d'une politique à gérer, je ne sais pas bien m'y prendre. Ou disons plutôt que j'ai l'impression de vouloir faire très compliqué pour les cas les plus simples. Pourriez-vous m'aiguiller je vous prie ?

    Par défaut, j'aimerais que le Prédicat unaire retourne true quelque soit la valeur de l'argument fourni. Est-ce qu'il faut que je code un foncteur bidon qui renvoie toujours true pour pouvoir le mettre en argument template par défaut ou il y a un truc du genre std::true<T>, ou bien tout simplement le type true fera l'affaire ? Est-ce que le compilateur va faire sauter le test conditionnel (je sens qu'on va me dire qu'il fera ce qu'il voudra ... ) ?

    D'autre part, pour le SpectrumHandler par défaut, j'aimerais que la fonction SpectrumHandler::handle(bar_type) de la politique par défaut renvoie directement le bar qui a été passé en argument (en gros, qu'elle ne fasse rien de particulier). Est ce qu'il faut que j'implémente une politique bidon qui retourne directement l'argument, où il y a t'il une manière plus explicite/concise de faire (une sorte de std::return_directly<T>) ?

    Merci d'avance

    EDIT : Des erreurs de compilation se sont glissées dans les lignes de code et sont corrigées par Pyramidev plus bas

    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
     
      template<class UnaryPredicate, class SpectrumHandler>
      class OccupancySpectrumDistribution {
     
        using spectrum_type = Algorithm::occupancy_spectrum_type;
        using support_type = std::vector<spectrum_type>;
        using probabilities_type = std::vector<double>;
     
        support_type m_support;
        probabilities_type m_probas;
        mutable std::discrete_distribution<size_t> m_dist;
     
        static auto make_callback(unsigned int k, unsigned int N, UnaryPredicate pred){
          auto& s_ref = m_support;
          auto& p_ref = m_probas;
          return [s_ref, p_ref](spectrum_type const& M_j){
            auto p = compute_probability(k, N, M_j);
            if(pred(p)){
              s_ref.push_back(SpectrumHandler::handle(M_j));
              p_ref.push_back(p);
            }
          };
        }
     
    // .... 
    };
    Le débutant, lui, ignore qu'il ignore à ce point, il est fier de ses premiers succès, bien plus qu'il n'est conscient de l'étendue de ce qu'il ne sait pas, dès qu'il progresse en revanche, dès que s'accroît ce qu'il sait, il commence à saisir tout ce qui manque encore à son savoir. Qui sait peu ignore aussi très peu. [Roger Pol-Droit]
    Github
    Mon tout premier projet: une bibliothèque de simulation de génétique des populations

  2. #2
    Expert éminent sénior

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

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Veux-tu un prédicat toujours vrai pour un usage particulier de ta template ou que le prédicat doivent toujours être vrai dans l'usage que tu en fais dans la classe?
    Ce n'est pas du tout la même signification, ni la même manière de t'en servir.

    Dans le premier cas, effectivement, c'est un [](auto const&) { return true; }.

    Pour ta deuxième question, il fut un temps question d'une "std::identity".
    Après moult propositions de standardisation, elle fut finalement rejetée, car elle s'avère être identique à std::forward.
    Cela dit, il en existe une dans std::ranges: cf cppreference.com.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  3. #3
    Membre averti Avatar de Seabirds
    Homme Profil pro
    Post-doctoral fellow
    Inscrit en
    Avril 2015
    Messages
    294
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Post-doctoral fellow
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Avril 2015
    Messages : 294
    Points : 341
    Points
    341
    Par défaut
    Merci de ta réponse ternel !

    Citation Envoyé par ternel Voir le message
    Veux-tu un prédicat toujours vrai pour un usage particulier de ta template ou que le prédicat doivent toujours être vrai dans l'usage que tu en fais dans la classe?
    Ce n'est pas du tout la même signification, ni la même manière de t'en servir.
    Argh, attend, j'ai lu dix fois la même phrase et j'ai du mal à comprendre ce qu'elle veut dire ...
    J'aimerais que l'instantiation par défaut de la classe template mène à un code où l'évaluation pred(p) renvoie true, c'est à dire que le code à l'intérieur du if{...} s'exécute quelque soit la valeur de la probabilité. Est-ce que ça répond à ta question ?

    Pour ta deuxième question, il fut un temps question d'une "std::identity".
    Après moult propositions de standardisation, elle fut finalement rejetée, car elle s'avère être identique à std::forward.
    Donc ça veut dire que j'utilise std::forward ?
    Le débutant, lui, ignore qu'il ignore à ce point, il est fier de ses premiers succès, bien plus qu'il n'est conscient de l'étendue de ce qu'il ne sait pas, dès qu'il progresse en revanche, dès que s'accroît ce qu'il sait, il commence à saisir tout ce qui manque encore à son savoir. Qui sait peu ignore aussi très peu. [Roger Pol-Droit]
    Github
    Mon tout premier projet: une bibliothèque de simulation de génétique des populations

  4. #4
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 471
    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 471
    Points : 6 109
    Points
    6 109
    Par défaut
    Bonjour,

    Attention aux erreurs de compilation, Seabirds.

    Un bout de code qui compile et répond à ta description :
    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
    struct ReturnAlwaysTrue {
        bool operator()(double param) const
        {
          return true;
        }
    };
     
    struct HandleIdentity {
        using spectrum_type = Algorithm::occupancy_spectrum_type;
        static const spectrum_type& handle(const spectrum_type& M_j)
        {
            return M_j;
        }
    };
     
    template<class UnaryPredicate=ReturnAlwaysTrue, class SpectrumHandler=HandleIdentity>
    class OccupancySpectrumDistribution {
     
        using spectrum_type = Algorithm::occupancy_spectrum_type;
        using support_type = std::vector<spectrum_type>;
        using probabilities_type = std::vector<double>;
     
        support_type m_support;
        probabilities_type m_probas;
        mutable std::discrete_distribution<size_t> m_dist;
    public: // public ajouté (attention aux erreurs de compilation)
        auto make_callback(unsigned int k, unsigned int N, UnaryPredicate pred = UnaryPredicate{}){ // static enlévé (attention aux erreurs de compilation)
          auto& s_ref = m_support;
          auto& p_ref = m_probas;
          return [&s_ref, &p_ref, k, N, pred](spectrum_type const& M_j) { // liste de capture modifiée (attention aux erreurs de compilation)
            auto p = compute_probability(k, N, M_j);
            if(pred(p)){
              s_ref.push_back(SpectrumHandler::handle(M_j));
              p_ref.push_back(p);
            }
          };
        }
     
    // .... 
    };
    Remarque : Je ne connais pas le reste du code de ton modèle de classe OccupancySpectrumDistribution, mais il faut peut-être transformer make_callback en template avec en paramètre UnaryPredicate et enlever ce dernier des paramètres du modèle OccupancySpectrumDistribution.

  5. #5
    Membre averti Avatar de Seabirds
    Homme Profil pro
    Post-doctoral fellow
    Inscrit en
    Avril 2015
    Messages
    294
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Post-doctoral fellow
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Avril 2015
    Messages : 294
    Points : 341
    Points
    341
    Par défaut
    Citation Envoyé par Pyramidev Voir le message
    Bonjour,
    Attention aux erreurs de compilation, Seabirds.
    Aïe aïe aïe oui j'ai fait le debuggage après avoir reçu les propositions de ternel, et je me suis dit que je modifierai le code sur mon post ce soir, mais trop taaaard ... Merci de tes corrections et excusez-moi la piètre qualité de mon code

    Effectivement la liste de capture était pourrie, je capture maintenant le pointeur this pour avoir accès aux membres, ce qui est peut-être plus logique

    Par contre je ne crois pas avoir besoin de mettre make_callback en public, elle est utilisée par le constructeur de la classe, ce qui répond peut-être aussi à ta dernière remarque :
    Je ne connais pas le reste du code de ton modèle de classe OccupancySpectrumDistribution, mais il faut peut-être transformer make_callback en template avec en paramètre UnaryPredicate et enlever ce dernier des paramètres du modèle OccupancySpectrumDistribution.
    Du coup je vais peut-être opter pour ta solution. Je n'avais pas trop envie d'avoir des bouts de code satellites, mais la solution de ternel m'amène sur des chemins plus compliqués (faute de bien m'y prendre sans doute...).

    Ces différentes manières de faire amènent toutefois sur une question de fond que je voulais poser il y a quelques temps, et faute de pouvoir trouver une ressource où la question a été traitée c'est peut-être le bon moment :

    Quand on veut injecter des comportements, quand est-ce qu'on va vouloir utiliser :
    • Des fonctions/foncteurs ?
    • Des classes de politiques avec des comportements statiques ?
    • Des classes de politiques à instancier avec des comportements non statiques ?


    Je dirais bêtement que pour les fonctions, c'est quand la syntaxe operator()() est suffisamment claire, le foncteur quand il y a besoin de stocker un état (comme le Callback qui stocke le pointeur this par exemple). Mais évidemment je suis sûr que c'est plus subtil ...
    Le débutant, lui, ignore qu'il ignore à ce point, il est fier de ses premiers succès, bien plus qu'il n'est conscient de l'étendue de ce qu'il ne sait pas, dès qu'il progresse en revanche, dès que s'accroît ce qu'il sait, il commence à saisir tout ce qui manque encore à son savoir. Qui sait peu ignore aussi très peu. [Roger Pol-Droit]
    Github
    Mon tout premier projet: une bibliothèque de simulation de génétique des populations

  6. #6
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 471
    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 471
    Points : 6 109
    Points
    6 109
    Par défaut
    En C++, pour "injecter des comportements", la palette est très large.

    Quelques solutions en vrac :
    • Les classes de politique.
      En programmation par templates, c'est la manière privilégiée de personnaliser des comportements indépendants.
      Un bon exemple est std::set avec le comparateur et l'allocateur.
      std::set stocke un comparateur et un allocateur. Ils peuvent être vides ou avoir un état. Quand ils ont un état, on les construit puis on les passe en paramètre du constructeur de std::set.
      Comme le comparateur a une seule opération, il s'utilise avec la même syntaxe qu'une fonction. Cela donne la liberté à l'utilisateur de choisir que le type de comparateur soit un pointeur de fonction. Il n'est donc pas obligé d'utiliser une classe qui surcharge operator().
    • Le CRTP.
      Une classe Deriv dérive publiquement de Base<Deriv>. C'est l'héritage sans virtualité.
      C'est bien quand les personnalisations sont interdépendantes.
      La classe de base définit les fonctions publiques qui appellent des fonctions privées définies dans la classe dérivée.
      Un bon exemple est boost::iterator_facade.
    • Le patron de conception Template Method de la programmation orientée objet.
      La classe de base définit un comportement général qui appelle des fonctions virtuelles. On personnalise dans la classe dérivée en redéfinissant les fonctions virtuelles.
      Comme le CRTP, c'est bien quand les personnalisations sont interdépendantes. On a aussi les avantages et les inconvénients de l'orienté objet.
      Un exemple dans la STL en C++17 est std::pmr::memory_resource qui sert à initialiser un std::pmr::polymorphic_allocator.
    • Le patron de conception Stratégie de la programmation orientée objet.
      Une classe contient un ensemble de classes de base. On personnalise les classes dérivées.
      Comme les classes de politique, c'est bien quand les personnalisations sont indépendantes. On a aussi les avantages et les inconvénients de l'orienté objet.
      Un exemple dans la STL est std::locale avec ses facets.
    • std::function
      On a toute la flexibilité déjà présente dans le comparateur de std::set : on peut y mettre un pointeur de fonction ou un objet qui a un état ou non. On évite aussi d'ajouter un argument de template. Par contre, on perd en performances.
    • Les pointeurs de fonction.
      C'est la solution la moins flexible, car on ne peut pas créer plusieurs instances qui ont un état différent. D'ailleurs, c'est rare qu'une fonction ait un état (mais ça peut arriver, par exemple avec des variables locales statiques). Cependant, les pointeurs de fonction ont le mérite d'être légers. En outre, les pointeurs de fonctions non membres sont rétrocompatibles avec le C.


    En ce qui concerne le duel entre la programmation orientée objet avec la virtualité et la programmation générique avec les templates :
    Les templates sont généralement plus performants.
    Les templates sont aussi parfois plus type-safe et plus simples (ex : la comparaison entre les objets est plus simple en programmation générique que en programmation orientée objet où on doit prévoir la comparaison de ce qui n'est pas comparable).
    En outre, en C++, la surcharge des opérateurs rend la programmation par templates plus flexible, par exemple en pouvant utiliser une fonction comme un foncteur ou un pointeur comme un itérateur.
    La programmation orientée objet, elle, permet des temps de compilation beaucoup plus rapides.
    Elle permet aussi de manipuler plus facilement des données dont on ne connaît le type qu'à l'exécution (en programmation par templates, on doit passer par un std::variant).
    Enfin, elle nécessite moins de connaissances techniques propres au langage et le compilateur est moins méchant en cas de bêtise.

    Maintenant, tu vas pouvoir réfléchir à toutes les solutions à chaque fois que tu voudras "injecter des comportements".

  7. #7
    Membre averti Avatar de Seabirds
    Homme Profil pro
    Post-doctoral fellow
    Inscrit en
    Avril 2015
    Messages
    294
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Post-doctoral fellow
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Avril 2015
    Messages : 294
    Points : 341
    Points
    341
    Par défaut
    Citation Envoyé par Pyramidev Voir le message
    En C++, pour "injecter des comportements", la palette est très large.
    Woooh effectivement ! Merci pour le récapitulatif, j'espère savoir faire les bons choix au bon moment !

    Citation Envoyé par Pyramidev Voir le message
    std::set stocke un comparateur et un allocateur. Ils peuvent être vides ou avoir un état. Quand ils ont un état, on les construit puis on les passe en paramètre du constructeur de std::set.
    Comme le comparateur a une seule opération, il s'utilise avec la même syntaxe qu'une fonction. Cela donne la liberté à l'utilisateur de choisir que le type de comparateur soit un pointeur de fonction. Il n'est donc pas obligé d'utiliser une classe qui surcharge operator().
    Aaaaaah c'est bon je commence à comprendre pour les politiques vides/avec état, mille merci c'est beaucoup plus clair !!! Mmhhhh oui en fait on se débrouille pour repousser les décisions au niveau du code appelant supérieur... comme d'habitude

    Bon, ça fait bien plaisir quand les choses s'éclairent !!!
    Le débutant, lui, ignore qu'il ignore à ce point, il est fier de ses premiers succès, bien plus qu'il n'est conscient de l'étendue de ce qu'il ne sait pas, dès qu'il progresse en revanche, dès que s'accroît ce qu'il sait, il commence à saisir tout ce qui manque encore à son savoir. Qui sait peu ignore aussi très peu. [Roger Pol-Droit]
    Github
    Mon tout premier projet: une bibliothèque de simulation de génétique des populations

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

Discussions similaires

  1. Constructeur par défaut pour une classe fille
    Par Difré91 dans le forum Langage
    Réponses: 6
    Dernier message: 05/11/2010, 21h00
  2. [JSF] valeur par défaut pour InputText
    Par gondek dans le forum JSF
    Réponses: 3
    Dernier message: 11/07/2010, 19h08
  3. valeur par défaut pour un parametre de fonction
    Par maximenet dans le forum Langage
    Réponses: 2
    Dernier message: 19/07/2006, 10h29
  4. Valeur par défaut pour un iterateur
    Par karmaki dans le forum C++
    Réponses: 10
    Dernier message: 20/05/2005, 08h22
  5. Réponses: 2
    Dernier message: 18/10/2003, 14h42

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