IMPLEMENTATION EN PYTHON DES CONVENTIONS ALGORITHMIQUES (2022-2023)
C++ Metaprogramming : multidimensional typelist
1. MetaProgrammation C++
type list multidimmensionnelle
Vincent Agnus,
Institut de Recherche contre les Cancers de l'Appareil Digestif
Developer Forum 4
18 septembre 2008
Strasbourg
University of Strasbourg, France
4. Meta Programmation C++
Meta Programmation =
« génération par le compilateur d'algorithmes dans le
code source »
Language compilé : C++, Curl, D, Eiffel, Haskell, ML and XL.
Language interprété : modifiable pdt runtime
C++ MetaProgrammation : basée sur les templates
Vincent Agnus 4/50
5. Meta Programmation C++
Template
Permet de définir des fonctions génériques --> comportement
commun
#include <iostream>
template <typename T>
inline const T& maximum(const T& x,const T& y)
{
if(y > x)
return y;
else
return x;
}
int main(void)
{
// template instanciation
std::cout << maximum<int>(3,7) << endl; //outputs 7
std::cout << maximum(3, 7) << endl; //same as above
std::cout << maximum(3.2,7.3) << endl; //outputs 7.3
return 0;
}
Vincent Agnus 5/50
7. Meta Programmation C++
Template
Classe gérant des types/valeurs génériques
template<typename T>
class MyVector
{
...
protected :
int m_nbElements;
T *m_elements;
};
template<typename T, int N> // ici un template du genre valeur
class MyArray
{
...
protected :
T[N] m_elements;
};
Vincent Agnus 7/50
8. Meta Programmation C++
Template
Les templates lors de la compilation génèrent du code
temporaire qui est inséré dans le code source qui est
recompilé
Metaprogrammation à partir des templates
Génération de classes à la compilation
Optimisation de code à la compilation
Définition de nouveaux idioms
Vincent Agnus 8/50
9. Meta Programmation C++
Template
Génération de classe
int factoriel(int n)
{
return (n==0?1:n*factoriel(n-1));
}
void foo()
{
// 4 est connu au moment de la compilation
int y = factoriel(4); // valeur calculée à chaque l'exécution !!!
}
Vincent Agnus 9/50
10. Meta Programmation C++
Template
Génération de classe
template <int N>
struct Factoriel
{
enum { value = N * Factoriel<N - 1>::value };
};
template <>
struct Factoriel<0>
{
enum { value = 1 };
};
int factoriel(int n)
{
return (n==0?1:n*factoriel(n-1));
}
void foo()
{
int x = Factoriel<4>::value; //== 24 constante générée lors de la compilation
int y = factoriel(4); // valeur calculée lors de l'exécution
}
Vincent Agnus 10/50
11. Meta Programmation C++
Template
Optimisation de Code
template<class T, int N>
class MyArray
{
typedef MyArray<T,N> Self;
Self &operator+=(const Self& rhs)
{
for (int i = 0; i < N; ++i)
{ m_elements[i] += rhs.value[i]; } // N connu => boucle déroulable
return *this;
}
protected :
T[N] m_elements;
};
Compilatation : myArrayInstance += myArrayInstance2;
N connu lors de la compilation
le compilateur peut dérouler la boucle
Créer du code parallélisable
Vincent Agnus 11/50
12. Meta Programmation C++
Template
Créer des idioms
Exemple «Polymorphisme statique» ou «Curiously Recurring
Template Pattern»
template <typename DERIVED>
struct base
{
// ...
};
struct derived : base<derived>
{
// ...
};
Objectif :
Ajout de fonctionnalités à une classe
Dérivation sans utilisation de table de virtualité
Vincent Agnus 12/50
14. Meta Programmation C++
Template
Curiously Recurring Template Pattern-- exemple 1
template <typename T>
struct counter class X : counter<X>
{ {
counter() // ...
{ };
objects_created++;
objects_alive++; class Y : counter<Y>
} {
// ...
virtual ~counter() };
{
--objects_alive;
}
static int objects_created;
static int objects_alive;
};
template <typename T> int counter<T>::objects_created( 0 );
template <typename T> int counter<T>::objects_alive( 0 );
Vincent Agnus 14/50
15. Meta Programmation C++
Template
Curiously Recurring Template Pattern -- Exemple 2
// A class template to express an equality comparison interface.
template<typename T> class equal_comparable
{
friend bool operator==(T const &a, T const &b) { return a.equal_to(b); }
friend bool operator!=(T const &a, T const &b) { return !a.equal_to(b); }
};
class value_type
// Class value_type wants to have == and !=, so it derives from
// equal_comparable with itself as argument (which is the CRTP).
: private equal_comparable<value_type>
{
public:
bool equal_to(value_type const& rhs) const; // to be defined
};
Vincent Agnus 15/50
16. MetaProgrammation C++
Avantages – Inconvénients :
Générique
Temps compilation – Performance lors runtime
Maintenance
Portabilité
• Différent compilateurs
Lisibilité :
• syntaxe
• message d'erreurs
Bibliothèque C++ : boost meta programming Language
http://www.boost.org/doc/libs/release/libs/mpl/
Vincent Agnus 16/50
18. Type List & Dispatcher
Objectif : Automatisation écriture de code pour la gestion de
plusieurs types/classes
Exemples
problématique IRCAD
Principe de la solution mise en place
Détail de l'implémentation
Discussion
Vincent Agnus 18/50
19. Type List & Dispatcher
Motivation
class Shape {...};
class Point : public Shape {...};
Exemple une Fabrique
class Ball : public Shape {...};
template<T> T *create()
{
return new T;
}
class TrivialShapeFactory
{
static Shape *build(std::string &key)
{
if ( key == "Point" )
{
return create<Point>();
}
else if ( key == "Ball" )
{
return create<Ball>();
}
}
};
Vincent Agnus 19/50
20. Type List & Dispatcher
Motivation
Plus de degrés de liberté
class Shape {...};
class Point : public Shape {...};
class Ball : public Shape {...};
class Color {...};
class Red : public Color {...};
class Green : public Color {...};
class Blue : public Color {...};
template< class COLOR, class SHAPE >
class ColoredShape : public SHAPE
{
// ....
COLOR m_color;
};
template< COLOR, SHAPE> Shape *createColoredShape()
{
return new ColoredShape< COLOR , SHAPE >;
}
Vincent Agnus 20/50
21. Type List & Dispatcher
Motivation
La nouvelle fabrique à 2 degrés de liberté
class ColoredShapeFactory
{
static Shape *build(std::string &color, std::string &shape)
{
if ( shape == "Point" )
{
if ( color == "Red" ) { return createColoredShape<Red, Point>();}
if ( color == "Green") { return createColoredShape<Green, Point>();}
if ( color == "Blue" ) { return createColoredShape<Blue, Point>();}
}
else if ( shape == "Ball" )
{
if ( color == "Red" ) { return createColoredShape<Red, Ball>();}
// and so on ...
}
...
}
};
Ajout d'une nouvelle classe -> risque d'erreurs
Typelist --> automatisation
Vincent Agnus 21/50
22. Type List & Dispatcher
Motivation
Motivation « IRCAD »
Utilisation de la librairie template ITK
template <typename PIXELTYPE, int DIM>
itk::Image<PIXELTYPE,DIM>
template <typename TImageIn, typename TImageOut>
itk::ImageFilter<TImageIn,TImageOut>
Nous utilisons des types images non templates (conversion <->
ITK )
void *m_buffer;
PixelType m_pixelType;
Il faut gérer les entrées/sorties des filtres avec des images de
différents types
Vincent Agnus 22/50
23. Type List & Dispatcher
Mise en Oeuvre
Automatisation l'écriture du code
MetaProgrammation C++ :
Typelist : définir la liste des types à traiter
Le Dispatcher qui va parcourrir cette liste
Mapping entre Type de la typelist et Type requis lors du runtime
Functor (traitement)
Gestion des type list n-dimensionnelles
Cas d'utilisation : Dispatcher sur Fabriques
Vincent Agnus 23/50
24. Type List & Dispatcher
définition d'une type list
Type List = « Un container qui contient des types »
La boost MPL fournit plusieurs type de typelist
#include<vector>
#include<boost/mpl/vector.hpp>
#include<boost/mpl/at.hpp>
void f()
{
std::vector<int> classicContainer(10);
int i = classicContainer.at(1);
typedef boost::mpl::vector< char , std::string, int > TypeList;
typedef boost::mpl::at_c< TypeList, 1 >::type StringType;
StringType v = ''toto''; // OK !
}
Vincent Agnus 24/50
25. Type List & Dispatcher
Cas d'utilisation
Dispatcher & Fabrique a 1 degré de liberté
#include <boost/mpl/vector.hpp> // used to define type list
#include <Dispatcher.hpp> // define our dispatcher
struct ShapeFactory
{
static Shape *build(std::string key)
{/
// type list of supported types
typedef boost::mpl::vector<Point,Ball> GeneratedShapeTypeList;
Shape *shape=NULL;
Dispatcher< GeneratedShapeTypeList, ShapeGenerator >::invoke(key,shape);
return shape;
}
}; Vincent Agnus 25/50
26. Type List & Dispatcher
Cas d'utilisation
Dispatcher & Fabrique a 1 degré de liberté
#include <boost/mpl/vector.hpp> // used to define type list
#include <Dispatcher.hpp> // define our dispatcher
struct ShapeGenerator
{
template<class T>
void operator()(Shape * &t) // we use a reference on a pointer to modify it
{
t=new T;
}
};
// Correspondance valeur clef <--> type ( fonction IsMapping )
// exemple chaine ''Point'' avec le type Point
struct ShapeFactory
{
static Shape *build(std::string key)
{/
// type list of supported types
typedef boost::mpl::vector<Point,Ball> GeneratedShapeTypeList;
Shape *shape=NULL;
Dispatcher< GeneratedShapeTypeList, ShapeGenerator >::invoke(key,shape);
return shape;
}
}; Vincent Agnus 26/50
27. Type List & Dispatcher
Cas d'utilisation
Correspondance valeur clef <--> Type
#include <TypeMapping.hpp>
// used to define binding between key value and concrete type
// define template isMapping function
//template<typename TYPE,KEYTYPE>
//bool isMapping<TYPE>(const KEYTYPE &key)
//{
// // mandatory template specialization requested
// // missing binding declaration involve compilation fail
//}
template<> bool isMapping<Point>(const std::string &key)
{
return key == "Point";
}
template<> bool isMapping<Ball>(const std::string &key)
{
return key == "Ball";
}
Vincent Agnus 27/50
28. Type List & Dispatcher
Cas d'utilisation
Code Généré Compilation de
Dispatcher< GeneratedShapeTypeList, ShapeGenerator >::invoke(key,shape);
Ps e u d o Co d e Gé n é ré
{
// generated at compile time
if ( isMapping< Point , std::string >(key) )
{
ShapeGenerator shapeGenerator;
shapeGenerator.operator<Point>( shape );
}
else if ( isMapping< Ball , std::string >(key) )
{
ShapeGenerator shapeGenerator;
shapeGenerator.operator<Ball>( shape );
}
... // same for each type stored in GeneratedShapeTypeList
}
Vincent Agnus 28/50
29. Type List & Dispatcher
Cas d'utilisation
API du Dispatcher
Généricité vis à vis
Nature clef
Functor
Type du container de la TypeList
// file Dispatcher.hpp
template<class MANAGEDTYPELIST, class FUNCTOR>
class Dispatcher
{
static void invoke();
template<class KEYTYPE>
static invoke(const KEYTYPE &key);
template<class KEYTYPE,class PARAMETER>
static invoke(const KEYTYPE &key, PARAMETER ¶m);
};
Vincent Agnus 29/50
30. Type List & Dispatcher
Cas d'utilisation
Dispatcher & Fabrique a 2 degrés de liberté
#include <Combinatory.hpp> // contain our meta-function cartesianProduct
// isMapping specialized for Colors
// isMapping for Combinaisont Type auto managed
// functor ColoredShapeGenerator;
struct ColoredShapeFactory
{
static Shape *build(std::string shape, std::string color)
{
//define two type lists for shape and color
typedef boost::mpl::vector< Point, Ball >::type ManagedShape;
typedef boost::mpl::vector< Red, Green, Blue >::type ManagedColor;
// create the 2-dimensional typelist
typedef boost::mpl::vector< ManagedShape, ManagedColor >::type MultiDimList;
// generate mono-dimensional typelist with element of size 2 (shape,color)
typedef boost::mpl::apply< CartesianProduct, MultiDimList>::type CombinaisonList;
Shape *coloredShape=NULL;
// generate the multi-key
std::list< std::string > key; key.push_back(shape);key.push_back(color);
Dispatcher< CombinaisonList , ColoredShapeGenerator >::invoke(key,coloredShape);
return coloredShape;
}
};
Vincent Agnus 30/50
31. Type List & Dispatcher
Cas d'utilisation
Dispatcher & Fabrique a 2 degrés de liberté
Appel du Functor ColoredShapeGenerator
Dispatcher< CombinaisonList , ColoredShapeGenerator >::invoke(key,coloredShape);
struct ColoredShapeGenerator
{
template<class TYPESEQUENCE> // paire (SHAPE,COLOR)
void operator()(Shape * &t)
{
// insure is a TSEQUENCE
BOOST_STATIC_ASSERT ( boost::mpl::is_sequence<TYPESEQUENCE>::value );
// retreive each type in 2 sized element TYPESEQUENCE
typedef typename boost::mpl::at_c<TYPESEQUENCE,0>::type ChoosenShape;
typedef typename boost::mpl::at_c<TYPESEQUENCE,1>::type ChoosenColor;
t=new ColoredShape< ChoosenShape, ChoosenColor> ;
}
};
Vincent Agnus 31/50
32. Type List & Dispatcher
Cas d'utilisation
Fabrique a 2 degrés de liberté
Mix des 2 types list : CombinaisonList
struct ColoredShapeFactory
{
static Shape *build(std::string shape, std::string color)
{
//define two type list for shape and color
typedef boost::mpl::vector< Point, Ball >::type ManagedShape;
typedef boost::mpl::vector< Red, Green, Blue >::type ManagedColor;
// create the 2-dimensional typelist
typedef boost::mpl::vector< ManagedShape, ManagedColor >::type MultiDimList;
// generate mono-dimensional typelist with element of size 2 (shape,color)
typedef boost::mpl::apply< CartesianProduct, MultiDimList>::type CombinaisonList;
Shape *coloredShape=NULL;
// generate the multi-key
std::list< std::string > key; key.push_back(shape);key.push_back(color);
Dispatcher< CombinaisonList , ColoredShapeGenerator >::invoke(key,coloredShape);
return coloredShape;
}
};
Vincent Agnus 32/50
33. Type List & Dispatcher
Cas d'utilisation
Implémentation utilisant la boost::mpl
Fonction Produit Cartésien :
génère les combinaisons (ordonnées) de plusieurs ensembles
Produit Cartésien de n ensembles de taille si donne un
ensemble de s1⋯sn n-uplets
Exemple :
t 1,1 t 3,1
t 1,3
t
TL1= t 1,2 , TL2= 2,1 , TL3= t 3,2
t 2,2
t 3,3
t 1,1 , t 2,1 , t 3,1 , t 1,1 , t 2,1 , t 3,2 , t 1,1 ,t 2,1 ,t 3,3 ,
TL1⊗TL2⊗TL3 = t 1,1 , t 2,2 , t 3,1 , t 1,1 , t 2,2 , t 3,2 , t 1,1 ,t 2,2 ,t 3,3 ,
⋮ ⋮ ⋮
t 1,3 , t 2,2 ,t 3,1 , t 1,3 , t 2,2 , t 3,2 , t 1,3 ,t 2,2 ,t 3,3
Vincent Agnus 33/50
34. Type List & Dispatcher
Cas d'utilisation
Génération des combinaisons de typelist :
ManagedShape ~ mpl::vector<Point,Ball>
ManagedColor ~ mpl::vector<Red,Green,Bue>
MultiDimList mpl::vector< mpl::vector<Point,Ball> mpl::vector<Red,Green,Bue> >
Appel Meta Fonction Produit Cartesien
typedef boost::mpl::apply< CartesianProduct, MultiDimList>::type CombinaisonList;
Génération à la compilation de :
typedef boost::mpl::vector<
boost::mpl::vector<Point, Red>,
boost::mpl::vector<Point, Green>,
boost::mpl::vector<Point, Blue>,
boost::mpl::vector<Ball , Red>,
boost::mpl::vector<Ball , Green,
boost::mpl::vector<Ball , Blue> >::type CombinaisonList
Vincent Agnus 34/50
35. Type List & Dispatcher
Cas d'utilisation
Dispatcher Fabrique a 3 degrés de liberté et + :
Modification mineure du code ( couleurs )
struct ColoredShapeFactory
{
static Shape *build(std::string shape, std::string color, std::string style)
{
//define two type list for shape and color
typedef boost::mpl::vector< Point, Ball >::type ManagedShape;
typedef boost::mpl::vector< Red, Green, Blue >::type ManagedColor;
typedef boost::mpl::vector< Plain, WireFrame, Hidden >::type ManagedStyle;
// create the 2-dimensional typelist
typedef boost::mpl::vector< ManagedShape, ManagedColor , ManagedStyle>::type MultiDimList;
// generate mono-dimensional typelist with element of size 3 (shape,color,style)
typedef boost::mpl::apply< CartesianProduct, MultiDimList>::type CombinaisonList;
Shape *newShape=NULL;
// generate the multi-key
std::list< std::string > key; key.push_back(shape);key.push_back(color);
key.push_back(style);
Dispatcher< CombinaisonList , 3DOFShapeGenerator >::invoke(key,newShape);
return newShape;
}
};
Vincent Agnus 35/50
36. Type List & Dispatcher
Détail
Détail d'implémentation
Dispatcher : méthod invoke
IsMapping n-D ( auto géré )
CartesianProduct Meta Function
Vincent Agnus 36/50
37. Type List & Dispatcher
Détail
Dispatcher : méthod invoke
Pour chaque type dans la typelist
Test si type et clef compatible isMapping
Si OK Appel functor
template< class TYPELIST, class FUNCTOR >
class Dispatcher
{
template< class KeyType, class Parameter >
static void invoke( const KeyType &keytype, Parameter ¶m );
};
Vincent Agnus 37/50
38. Type List & Dispatcher
Détail
template< class TYPELIST, class FUNCTOR >
class Dispatcher {
template< class KeyType, class Parameter >
static void invoke( const KeyType &keytype, Parameter ¶m )
{
namespace mpl = boost::mpl;
typedef typename mpl::pop_front<TYPELIST>::type Tail;
typedef typename mpl::front<TYPELIST>::type Head;
if ( isMapping<Head>(keytype) )
{
// create the functor then excute it
FUNCTOR f;
f.template operator()<Head>(param);
}
else
{
// compile time : recursively call other element in the list
typedef typename mpl::eval_if< mpl::empty<Tail>,
mpl::identity< EmptyTypeListAction >,
mpl::identity< Dispatcher<Tail,FUNCTOR > >
>::type typex;
typex::invoke(keytype,param);
}
} Vincent Agnus 38/50
39. Type List & Dispatcher
Détail
Compilation de
Dispatcher<ManagedShape,GeneratorFuncor>::invoke(key,shape)
typedef mpl::vector<Point,Ball> ManagedShape;
Dispatcher<ManagedShape, GenFunctor>::invoke( key, shape)
{
if( isMapping<Point>(key) )
{ GenFunctor().operator<Point>()(shape); }
else
{
typedef Dispatcher< mpl::vector<Ball>, GenFunctor> typex;
typex::invoke(key,shape);
}
}
Dispatcher< mpl::vector<Ball>, GenFunctor>::invoke( key, shape)
{
if ( isMapping<Ball>(key) ) { ... }
else
{
// same API than Dispatcher but always throw ''Incorrect Key''
EmptyTypeListAction::invoke(key,shape);
}
}
Vincent Agnus 39/50
40. Type List & Dispatcher
Détail
Détail d'implémentation
Dispatcher : méthod invoke
IsMapping n-D ( auto géré )
CartesianProduct Meta Function
Vincent Agnus 40/50
41. Type List & Dispatcher
Détail
IsMapping : binding Type <-> valeur clef
Robuste vis à vis oublie spécialisation -> erreur de compilation
template<class TYPE,class KEY>
bool isMapping<TYPE>(const KEY &key)
{
BOOST_STATIC_ASSERT(sizeof(TYPE) == 0)
}
Gestion automatique des clefs multiple càd
isMapping< mpl::vector<...> >(keys) =
isMapping< mpl::front(mpl::vector<...>) >(keys.front()) &&
isMapping< mpl::pop_front(mpl::vector<...>) >(keys.pop_front());
Vincent Agnus 41/50
42. Type List & Dispatcher
Détail
Détail d'implémentation
Dispatcher : méthod invoke
IsMapping n-D ( auto géré )
CartesianProduct Meta Function
Vincent Agnus 42/50
43. Type List & Dispatcher
Détail
Produit cartésien est défini comme une meta fonction MPL :
(équivalent functor C++)
struct MaMetaFunction
{
template<class PARAM1, class PARAM2, ...>
struct apply
{
typedef .... type;
};
};
Appel boost::mpl::apply<MetaFct,P1,...>::type est un
typedef sur MetaFct::apply<P1,...>::type
exemple boost::mpl::identity
Vincent Agnus 43/50
44. Type List & Dispatcher
Détail
Produit Cartésien :
Prop TL1 TL2 ... TLn = TL1 ( TL1 (... TLn )))
struct CartesianProduct
{
template< class MultiSet >
struct apply
{
typedef typename boost::mpl::reverse_fold<
MultiSet,
boost::mpl::vector<>,
boost::mpl::apply2< CartesianProduct_1_N_Recurser,
boost::mpl::_2, boost::mpl::_1
>
>::type type;
};
};
3 meta fonctions intermédiaires
Au total 45 lignes
Vincent Agnus 44/50
45. Type List & Dispatcher
Discussion
Discussion
Généricité vis à vis clef
Spécificité de notre approche
Retour d'expériences
Vincent Agnus 45/50
46. Type List & Dispatcher
Discussion
Généricité vis à vis des clefs
Dimension des images : utilisation de
namespace boost {
namespace mpl {
template< int N >
struct int_ { ... }:
Gestion des Multi-méthodes :
• Clef spéciale : struct TypeInfo { std::type_info *m_data};
• Spécialisation :
template<> bool isMapping<Point>(const TypeInfo &key)
{ return *key.m_data == typeid(Point); }
Vincent Agnus 46/50
47. Type List & Dispatcher
Discussion
Spécificité de notre approche
Gestion native des types lists multidimensionnelles
Externalisation de la combinaison des type lists
Statique
Recherche clef type en O(n)
Vincent Agnus 47/50
48. Type List & Dispatcher
Discussion
Retour d'expériences
MPL :
• Documentation web : boost mpl réduite
• Debuggeage à la compilation par STATIC_ASSERT
• Multi OS ( Linux, OsX, win32 ) OK
• paradigme programmation fonctionnelle
MetaProgrammation C++
• Message erreur de compilation « illisible »
• Généricité
• Code Utilisateur : simplifié & sécurisé
Vincent Agnus 48/50
49. Type List & Dispatcher
Ressources
Livres :
Modern C++ Design, A. Alexandrescu. « C++ in depth series»
C++ Template Metaprogramming, D. Abrahams, A. Gurtovoy. « C++ in
depth series»
C++ Templates - The Complete Guide, D. Vandevoorde et N. Josuttis.
Addison-Wesley
Article :
En cours rédaction : « Multi-Dimensional C++ Type List » pour revue
« Computer Languages, Systems & Structures » Ed. Elsevier
Distribution des sources ?
Vincent Agnus 49/50