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:
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
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... };
Mais je n'arrive pas à exprimer les noexcept.
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); } // ... };
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?
Partager