template - types spécifiques

template - types spécifiques - C++ - Programmation

Marsh Posté le 05-05-2010 à 13:54:40    

Salut,
 
est-ce que c'est possible en c++, de déclarer un template, qui ne pourra être instancié que si le type présente certaines spécificités, par exemple un operateur *?

Reply

Marsh Posté le 05-05-2010 à 13:54:40   

Reply

Marsh Posté le 05-05-2010 à 14:13:06    

Il y a des moyens pour arriver a ce genre de chose.  Je conseille d'utiliser une bibliotheque qui les encapsule, p.e. boost concept check.


---------------
The truth is rarely pure and never simple (Oscar Wilde)
Reply

Marsh Posté le 05-05-2010 à 14:16:20    

ou bien boost::enable_if ca depend si tu veux empecher l'instanciation OU prevenir d'une erreur.
J'avais uploadé un addeduml à boost type_traits avec des truc genre has_operator_xxx, qui peut servir.
Quand je rentre je te montre


Message édité par Joel F le 05-05-2010 à 14:17:10
Reply

Marsh Posté le 05-05-2010 à 16:29:05    

Bon comme je me sens d'attaque, vla un truc fait sur le coin de la table.

 

Etape 1: detecter qu'un type T supporte operator*
En gros, on veut savoir si pour deux instances de T a et b, a*b est correcte.
L'idée est en gros d'essayer de caluler le type de retour de a*b, si on arrive * existe,
sinon non. Or, on veut pouvoir faire ça sans avoir d'erreur de compilation.

 

L'idée est d'alors d'utiliser le mécanisme de surcharge de fonction pour tomber dans un cas
foireux dans les moments ou * n'existe pas, et discriminer sur cet appel.

 

Le code:

 
Code :
  1. #include <iostream>
  2. #include <boost/mpl/bool.hpp>
  3. #include <boost/type_traits/remove_cv.hpp>
  4. using namespace std;
  5. namespace detail
  6. {
  7.     struct tag {};
  8.     struct any { template<class T> any(T const& ); };
  9.     tag operator*(any const&, any const& );
  10.                        char (& check(tag))[2];
  11.     template <class T> char check(T const& );
  12.     template<class T>
  13.     struct has_operator_multiplies_impl
  14.     {
  15.         static typename boost::remove_cv<T>::type& x;
  16.         static const bool value = sizeof( check((x * x)) ) == 1;
  17.     };
  18. }
  19. template <class T>
  20. struct has_operator_multiplies
  21. : boost::mpl::bool_<detail::has_operator_multiplies_impl<T>::value> {};
 

En gros, on cherche à calculer le sizeof de x*x. Soit x supporte cette operateur
et son type de retour tombe dans template <class T> char check(T const& )
soit non. Si il n'existe pas que ce passe-t-il ? et bien, c'est le * de any (qui est constructible
à partir de n'importe quoi) qui va etre selectionné. Il renvoit un tag qui va faire que l'on
va appeler char (& check(tag))[2];

 

Le sizeof de tout ça est donc : 1 si check renvoit char et 2 si il renvoit char[2].
On a donc discriminé la chose.

 

Exemple: http://codepad.org/ia244fNX

 

Etape 2: Utilisez ça dans une classe template
Maintenant qu'on sait que T a ou pas un operator*, il faut pouvoir utiliser cette information pour discriminer des classes.
On fait juste de la SFINAE avec enable_if.

 
Code :
  1. template<class T, class Enable = void>
  2. struct do_some_multiplication;
  3. template<class T>
  4. struct do_some_multiplication<T, typename boost::enable_if<has_operator_multiplies<T> >::type>
  5. {
  6.   void test(T const& a, T const& b )
  7.   {
  8.     cout << a*b << endl;
  9.   }
  10. };
 

Si le has_operator_multiplies<T> renvoit vrai pour T, la seconde specialsiation est prise. Sinon, c'ets la premiere.
Soit on ne mets rien pour obtenir une erreur du genre "incomplete type", soit on met sune static_assert.

 

Exemple: http://codepad.org/PIx3L7LS

 

Notes
les has_operator_xxx ont été plus ou  moins ajouter à Boost.typetraits mais je sais pas si c'ets dans la 1.43.
La stratégie à base de sizeof ce generalise avec une mini-macro, on peut aussi demander l'existence de methode avec une signature donnée ou l'existence de fonction avec un prototype donnée etc...


Message édité par Joel F le 05-05-2010 à 16:30:19
Reply

Marsh Posté le 08-05-2010 à 08:28:39    

Super, une fois de plus merci de poster ce genre de chose  :jap:  
 
Il y a juste un truc que je comprends pas trop:

Code :
  1. char (& check(tag))[2];


Ça c'est syntaxe pour la déclaration d'une fonction qui retourne un char[2] ?
 
 
Comme ca, c'est plus clair pour moi... (Je comprends bien qu'au final l'objectif est de retourner quelque chose de plus gros qu'un char si l'on tombe sur un "tag" )

Code :
  1. namespace detail
  2. {
  3.     struct tag {};
  4.     struct any { template<class T> any(T const& ); };
  5.     tag operator*(any const&, any const& );
  6.     int check(tag const& );
  7.     template <class T> char check(T const& );
  8.    
  9.     template<class T>
  10.     struct has_operator_multiplies_impl
  11.     {
  12.         static typename boost::remove_cv<T>::type& x;
  13.         static const bool value = sizeof( check((x * x)) ) == 1;
  14.     };
  15. }


Message édité par Amonchakai le 08-05-2010 à 08:33:50
Reply

Marsh Posté le 08-05-2010 à 14:01:59    

int n'est pas garanti d'etre plus gros que char

Reply

Marsh Posté le 08-05-2010 à 14:02:46    

ma semi-proposal de ce truc:

 

http://www.boostpro.com/vault/inde [...] directory=


Message édité par Joel F le 08-05-2010 à 14:02:54
Reply

Marsh Posté le 08-05-2010 à 16:48:50    

Hum ok, je vois...  
 
Je vais regarder de plus près boost::function_types. ca a l'air sympathoche ce que l'on peut faire avec.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed