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 :

CRTP, noexcept et sémantique de valeur


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    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 202
    Par défaut CRTP, noexcept et sémantique de valeur
    Bonjour à tous.

    Je construis actuellement une bibliothèque (vouée à être publiée), permettant de lire des fonctions dans un fichier texte, et de les évaluer le moment venu.

    Mon premier cas d'usage est un jeu vidéo (mon projet fil rouge ), dont les données contiennent des formules.
    Par exemple, les dégats du "MegaCanon" à une certaine distance est donné par "30 * niveau - 5 * distance"
    Mon objectif est de supporter formula<dmg> f = 30_dmg * level{} - 5_dmg * distance{};.

    J'ai donc créé une template de classe nommée expression, destinée à representer par exemple la multiplication de plusieurs opérandes.
    Cette expression est sensée être un type valeur (notamment copiable).

    J'ai plusieurs types manipulable: les booléens, les entiers, et peut-être d'autres choses, menant donc à des spécialisations de expression.
    La différence entre ces classes est limitée aux opérateurs mathématiques disponible (&& et || pour l'un, + - * / pour l'autre).
    Tous les autres mécanismes sont communs:
    • création
    • copie
    • swap
    • ajout d'un nouveau terme
    • evaluation (c'est la classe des opérandes qui gère cela)


    Donc, j'ai créé une classe de base pour partager un peu de ce code:
    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
    template <typename T, typename CRTP>
    class expression_base {
    protected:
      using operators = type_operators<T>; // alias sur l'enum class contenant des identifiants d'operateurs possibles pour T.
     
    private:
      operand<T> first;
      std::list<operation<T>> others; // avec struct operation<T>{ type_operators<T> op; operand<T> value; };
     
    protected:
      ~expression_base(){}
      expression_base(operand<T> const& o);
     
      expression_base(expression_base const& other): first(other.first), others(other.others) {}
      expression_base(expression_base && other): first(std::move(other.first)), others(std::move(other.others)) {}
     
      CRTP& operator=(expression_base other) noexcept(
        std::is_nothrow_move_assignable<decltype(std::declval<expression_base>().first)>::value &&
        std::is_nothrow_move_assignable<decltype(std::declval<expression_base>().others)>::value
      ) {
        swap(other);
        return static_cast<CRTP&>(*this);
      }
     
      void swap(expression_base & other) noexcept( noexcept(swap(first, other.first)) && noexcept(swap(others, other.others)) ) {
        using std::swap;
        swap(first, other.first);
        swap(others, other.others);
      }
     
      //des utilitaires...
    };
    Et a partir de cette base, je tire les différentes spécialisations de expression:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    template <>
    class expression<bool> final: private expression_base<bool, expression<bool>> {
    private:
      using base = expression_base<bool, expression<bool>>;
    
    public:
      ~expression(){}
      expression(operand<T> const& o): base(o) {}
    
      expression(expression const& other): base(other) {}
      expression(expression && other): base(std::forward<expression>(other)) {}
      
      expression& operator=(expression other) noexcept(noexcept( ??? )) {
        return base::operator=(other);
      }
    
      void swap(expression & other) noexcept(noexcept( ???  )) {
        base::swap(other);
      }
    
      // ...
    };
    Mais je n'arrive pas à exprimer les noexcept.
    Je me heurte à "inaccessible base class in this context". Et pour cause, l'expression de l'opérateur noexcept() est située "hors contexte".

    Du coup, j'imagine que je m'y prends comme une brèle.

    Mes questions sont donc:
    • Est-il raisonnable de passer par un CRTP et un héritage privé pour des types à sémantique de valeur?
    • Est-je raison de vouloir rendre le contenu de expression_base protected? (passer en public résoudrait mon problème)
    • Est-ce expression_base peut fournir la capacité de copie?
    • Est-ce qu'il serait logique de déplacer le static_assert de expression_base::swap à expression_base, et rendre ses swap et operator= inconditionnellement noexcept?
    • Y a-t-il une solution technique pour exprimer les noexcept?

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

    Informations professionnelles :
    Activité : Tech Lead

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 513
    Par défaut
    Bonjour,

    Citation Envoyé par ternel Voir le message
    Y a-t-il une solution technique pour exprimer les noexcept?
    Avec GCC 7.2.0, le code suivant compile :
    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
    #include <deque>
    #include <iostream>
    #include <list>
    #include <utility>
    #include <vector>
     
    template<class T>
    using operand = std::vector<T>;
     
    template<class T>
    using operation = std::deque<T>;
     
    template<typename T, typename CRTP>
    class expression_base
    {
    private:
    	operand<T> first;
    	std::list<operation<T>> others;
     
    protected:
    	static constexpr bool is_swap_noexcept =
    		noexcept(swap(std::declval<operand<bool>&>(), std::declval<operand<bool>&>())) &&
    		noexcept(swap(std::declval<std::list<operation<bool>>&>(), std::declval<std::list<operation<bool>>&>()));
     
    	void swap(expression_base& other) noexcept(is_swap_noexcept)
    	{
    		using std::swap;
    		swap(first, other.first);
    		swap(others, other.others);
    	}
    };
     
    template<class T>
    class expression;
     
    template<>
    class expression<bool> final: private expression_base<bool, expression<bool>>
    {
    private:
    	using base = expression_base<bool, expression<bool>>;
    public:
    	void swap(expression& other) noexcept(is_swap_noexcept) {
    		base::swap(other);
    	}
    };
     
    int main()
    {
    	expression<bool> toto;
    	expression<bool> titi;
    	toto.swap(titi);
    	std::cout << "OK !" << std::endl;
    }

  3. #3
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    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 202
    Par défaut
    Ah oui, en effet.
    Merci pour cette solution.

    Le problème concret est résolu, mais si quelqu'un a des avis sur mes questions théoriques, je prends aussi.

  4. #4
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,
    Citation Envoyé par ternel Voir le message

    Mes questions sont donc:
    1. Est-il raisonnable de passer par un CRTP et un héritage privé pour des types à sémantique de valeur?
    2. Est-je raison de vouloir rendre le contenu de expression_base protected? (passer en public résoudrait mon problème)
    1: L'héritage privé signifie "est implémenté en termes de" et le résultat revient -- peu ou prou -- à ce que l'on obtient grâce à une agrégation classique. Donc, oui: l'héritage privé (avec ou sans CRTP) peut parfaitement être utlisé avec les classes ayant sémantique de valeur

    2:A priori, tu ne devrais avoir que des fonctions dans l'accessibilité protégées. Les données, c'est de préférence toujours dans l'accessibilité privée. Mais il te reste une possibilité à explorer: l'amitié .

    Penses cependant que, a priori, l'amitié ne devrait être utilisée que pour permettre l'accès qu'à des fonctions qui ne sont pas exposées de manière publique, car il n'est jamais bon de permettre un accès direct au données (on risque beaucoup trop d'aller les modifier en dehors de tout contrôle)
    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

  5. #5
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    La réponse à tes autres questions théoriques
    Citation Envoyé par ternel Voir le message
    1. Est-ce expression_base peut fournir la capacité de copie?
    2. Est-ce qu'il serait logique de déplacer le static_assert de expression_base::swap à expression_base, et rendre ses swap et operator= inconditionnellement noexcept?
    3. Y a-t-il une solution technique pour exprimer les noexcept?
    1: Dans le cadre d'un héritage publique, non: Bien qu'il s'agisse d'une classe template, deux classes qui en héritent avec les même paramètres template interviennent dans une hiérarchie de classes, et ta classe de base agit donc comme une "interface" (au sens java du terme) classique

    Dans le cadre d'un héritage privé, pourquoi pas, vu que cela correspond -- comme je l'ai déjà dit -- peu ou prou à une agrégation classique

    2: Vu que ta classe de base dispose de l'information concernant le type de la classe dérivée grâce au CRTP, oui, cela pourrait avoir du sens, vu que tu ne peux envisager de swapper que deux expressions qui utilisent exactement les même paramètres templates (ou qui interviennent dans une hiérarchie de classe qui utilise les même paramètres template).

    Cependant, tu devrais peut-être envisager de scinder tes deux paramètres template, et de partir sur quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    template <typename T>
    class parent_expression{
        /* ... */
    };
    template <typename T, typename CRT>
    class expression_base : public parent_expression<T>{
     
    };
    car tout élément d'une expression booléenne devrait intervenir dans une hiérarchie de classe qui permet de représenter... une expression booléenne.

    Mais attention: à cause de l'héritage publique, tu rentre dans une logique de sémantique d'entité, avec tout ce que cela peut avoir comme effets secondaires:
    • copie et affectation interdite
    • swap peu recommandable
    • ...
    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

  6. #6
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    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 202
    Par défaut
    C'est là toute ma difficulté.

    Mes expressions sont des valeurs (non entité), qui n'ont pas à intervenir dans une hiérarchie.
    Elles sont par contre composées à l'exécution.

    Ma structure de base, c'est using operand<T> = boost::variant<T, std::function<T()>, expression_complexe<T>>.

    Mais je pense que je vais changer cela, pour avoir des spécialisations de template (notamment pour bool et les identifiants)

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

Discussions similaires

  1. Sémantique d'entité et sémantique de valeur
    Par Milleras dans le forum Langage
    Réponses: 1
    Dernier message: 19/07/2016, 09h56
  2. Réponses: 7
    Dernier message: 01/02/2016, 19h54
  3. Sémantique de valeur et héritage
    Par oodini dans le forum C++
    Réponses: 16
    Dernier message: 20/08/2013, 10h11
  4. Sémantique de valeur et bosst::shared_ptr
    Par bolhrak dans le forum Boost
    Réponses: 1
    Dernier message: 11/09/2007, 00h35

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