Expression template au delà de l’arithmétique
19 / 06 / 2002
Francis MAES
maes_f@epita.fr
Expression template au dela de l’arithmétique
I Introduction II Préliminaires III Implémentation IV Exemple
V Conclusion
I.1 Introduction: le Tiger
let
type list = { head: int, tail: list } function read_list (size : int) : list =
if size > 0 then list {head = read_int (), tail = read_list (size - 1)} else nil var the_list := read_list (5)
var infinite := 2147483647 /* 2 ^ 31 - 1 */
function min (l : list) : int =
let function min_ (a : int, b : int) : int = if a < b then a else b
in
if l = nil then infinite else min_ ( l.head, min (l.tail) ) end
in
print_int (min (the_list) ) end
Langage introduit par Andrew Appel, dans [1]
I.2 Introduction: compilation
code Tiger
arbre de syntaxe abstraite en C++ statique
Représentation(s) intermédiaire(s)
Assembleur executable
•lexeur
•parseur compilation Tiger compilation Tiger
génération de code
(simple) méta-compilation C++:
instançiations de templates, inline de fonctions
arbre de syntaxe abstraite
Représentation(s) intermédiaire(s) C++
compilation C++
génération de code
(très complexe)
code C++
complexe
I.3 Introduction: arbre de syntaxe abstraite
(a + b * 3) / c
/ +
*
c a
b 3
I.4 Introduction: expression template
FloatArray100 a, b, c;
d = (a + b * 3) / c;
Binop< Binop< Var, Binop< Var, Const, Times>, Plus>, Var, Div >
méta-compilation for (unsigned i = 0; i < Size; ++i) d[i] = (a[i] + b[i] * 3) / d[i]
template<class T_left, T_right, T_op>
struct Binop {
inline float eval(unsigned i);
};
struct Const
{ inline float eval(unsigned i);
};
Technique introduite par
Todd Veldhuizen dans [3]
I.5 Introduction: au dela des expressions templates
• if a < 6 then square(b) else 1
If< Binop< Var, Const<6> , Less>, Square <Var>, Const<1> >
• Pas de typage
• Pas de déclaration
• Pas de fonctions
• Pas de structures de contrôle...
II.1 Préléminaires: élements du langage Tiger
• Déclarations:
– variables – types – fonctions
• Expressions:
– arithmétique
– accès aux variables – structure de controle
– instançiations de tableaux et enregistrements
– appel de fonctions
II.2 Préléminaires: quelques exemples...
/* déclaration et usage de variables */
let
var i := 50 var j := 1
in i + j
end
/* déclaration et usage de types */
let
type int32 = int
type entry = {num: int32, desc: string}
var e := entry {num = 505, desc = "smousse"}
in
() end
LetInEnd<T_decs, T_exps >
Decs< ? >
VarDec < ? > VarDec < ? >
Binop<T_left, T_right, T_op>
Var< ? > Var < ? > Plus
II.3 Préléminaires: quelques exemples...
/* déclaration et usage de fonctions */
let
function double(x : int) : int = 2 * x function inc(x : int) : int = x + 1 in
double(30) - inc(8) end
on doit pouvoir:
• déclarer des types, variables et fonctions
• accéder à ces éléments...
• …via un mécanisme d’identification
II.4 Préléminaires: déclarations en Tiger
• structure de déclaration : let…in…end
• équivalence :
let
function f2 = ...
function f1 = … type t1 = … type t2 = … type t3 = ...
var v1 = ...
var v2 = ...
in
...
end
let
function f1 = … function f2 = ...
in let
type t1 = … type t2 = … type t3 = ...
in let
var v1 = ...
var v2 = ...
in
...
II.5 Préléminaires: déclarations et environnement
• Block de déclaration (chunk): ensemble de déclarations successives de même nature.
• Environnement: ensemble de chunks actifs
à un point donné du programme.
II.6 Préléminaires: opérations sur l’environnement
• Initialisations: builtins
• Let…in…end
– empilage d’un chunk
• Appel de fonction
– dépilage des chunks situés entre la déclaration et l’appel – empilage du chunk décrivant les paramètres formels
• Identification et Extraction : paire d’entiers
III.1 Implémentation: liste statique
struct EmptyList {
enum {size = 0};
};
template<class T_head, class T_tail = EmptyList>
struct List {
enum {size = 1 + tail::size};
typedef T_head head;
typedef T_tail tail;
};
typedef List<int, List<float, List<char> > > foo_t;
III.2 Implémentation: opération sur les listes statiques
• Sous-liste: template<class T_list, unsigned N>
struct ListSub {
typedef typename ListSub< typename T_list::tail, N - 1>::T res;
};
template<class T_list>
struct ListSub<T_tlist, 0>
{
typedef T_list res;
};
//example : typedef typename ListSub<foo_t, 2>::res bar_t ;
• Extraction: template<class T_list, unsigned N>
struct ListGet {
typedef typename ListSub<T_list, N>::head res;
};
//example : typedef typename ListGet<foo_t, 2>::res bar_t;
III.3 Implémentation: classes de types
• En Tiger toutes les variables font 4 octets.
• Un type doit pouvoir créer, détruire, assigner et comparer ses instances.
• Un type peut avoir besoin de l’environnement.
typedef void* var_t;
struct Type {
template<class T_env>
struct eval {
void create(var_t& v);
void destroy(var_t v);
void assign(var_t& left, var_t right);
int compare(var_t left, var_t right);
};
III.4 Implémentation:
déclarations
• déclaration de variables:
– Variable <class T_init_exp, unsigned TyChunk, unsigned TyNum>
• déclaration de fonctions:
– Function <class T_formal_list, class T_exp, unsigned ChunkLevel>
• déclaration de types:
– TypeLnk <unsigned TyChunk, unsigned TyNum>
– ArrayType <unsigned TyChunk, unsigned TyNum>
– RecordType <class T_fields_type_list>
– IntType
– StringType
III.5 Implémentation: classes d’expressions
• Une expression doit pouvoir évaluer son type.
• Une expression doit pouvoir évaluer sa valeur.
• Une expression peut
– avoir besoin de l’environnement.
– modifier l’environnement.
struct Exp {
template< class T_env>
struct eval {
typedef ... T;
var_t doit ();
};
};
III.6 Implémentation: exemples d’expressions
• ConstInt: template<signed Value>
struct ConstInt {
template<class T_env>
struct eval {
typedef IntType T;
var_t doit () {return Value;}
};
};
• LetInEnd:
(simplifié)
template<class T_dec_chunk, class T_seq_exp>
struct LetInEnd {
template<class T_env>
struct eval {
typedef List< T_dec_chunk, T_env> T_new_env;
typedef typename T_seq_exp::eval<T_new_env>::T T;
var_t doit ()
{ return T_seq_exp::eval<T_new_env>::doit();
}
};
III.7 Implémentation:
expressions
• arithmétique et constantes:
– BinOp<class T_exp_left, class T_exp_right, class T_op>
– ConstInt<signed Value>
– ConstString<unsigned Index>
• accès aux variables:
– SimpleVar<unsigned Chunk, unsigned Num>
– FieldVar<class T_lvalue, unsigned FieldNum>
– SubscriptVar<class T_lvalue, class T_exp>
– Assign<class T_lvalue, class T_exp>
• déclarations et appels de fonctions:
– LetInEnd<class T_dec_chunk, class T_seqexp>
– FuncCall<unsigned Chunk, unsigned Num, class T_param_list>
III.7 Implémentation:
expressions
• instanciations:
– Record<unsigned TyChunk, unsigned TyNum, class T_exp_list>
– Array<unsigned TyChunk, unsigned TyNum, class T_size, class T_init>
• structures de contrôle:
– If<class T_cond, class T_exp_then, class T_exp_else = Void>
– While<class T_cond, class T_exp>
– For<unsigned BlockID, unsigned VarID, class T_init, class T_end, class T_exp>
– Break
– ExpList<class T_first, class T_next = Void>
III.8 Implémentation: Stockage et accès aux variables
• On aimerait stocker les variables sur la pile du programme.
• Tiger possède la structure de blocs.
let
var a := 51 var b := 86
function f(c : int) : int = let
function g(d : int) : int = a + b + c + d
in g(1) end in
f(2) end
Chunk 0: int a int b
Chunk 2: int c
Chunk 4: int d
Chunk 1: (function f)
Chunk 3: (function g) Chunk -1: Tiger builtins
&a NULL
&c NULL
&d
Environnement Stack
IV.1 Exemple: code Tiger
/* A program to solve the 8-queens problem */
let
var N := 8
type intArray = array of int var row := intArray [ N ] of 0 var col := intArray [ N ] of 0
var diag1 := intArray [N+N-1] of 0 var diag2 := intArray [N+N-1] of 0 function printboard() =
(for i := 0 to N-1 do (for j := 0 to N-1
do print(if col[i]=j then " O" else " .");
print("\n"));
print("\n"))
function try(c: int) = if c=N
then printboard() else for r := 0 to N-1
do if row[r]=0 & diag1[r+c]=0 & diag2[r+7-c]=0 then (row[r]:=1; diag1[r+c]:=1; diag2[r+7-c]:=1;
col[c]:=r;
try(c+1);
row[r]:=0; diag1[r+c]:=0; diag2[r+7-c]:=0) in
try(0)
end
IV.2 Exemple: code C++ généré.
/* generated tiger->c++ code */
#include "all.h"
using namespace metasmousse;
typedef LetInEnd< TList< Variable< ConstInt< 8 >, builtin_types, 1 > >, LetInEnd< TList< ArrayType< builtin_types, 1 > >,
LetInEnd< TList< Variable< Array< 1, 0, SimpleVar< 0, 0 >, ConstInt< 0 > >, 1, 0 >, TList< Variable< Array< 1, 0, SimpleVar< 0, 0 >, ConstInt< 0 > >, 1, 0 >, TList<
Variable< Array< 1, 0, BinOp< BinOp< SimpleVar< 0, 0 >, SimpleVar< 0, 0 >, Plus >, ConstInt< 1 >, Minus >, ConstInt< 0 > >, 1, 0 >, TList< Variable< Array< 1, 0, BinOp< BinOp< SimpleVar< 0, 0 >, SimpleVar< 0, 0 >, Plus >, ConstInt< 1 >, Minus >, ConstInt< 0 > >, 1, 0 > > > > >,
LetInEnd< TList< Function< TList< >, ExpList< For< 5, 0, ConstInt< 0 >, BinOp< SimpleVar< 0, 0 >, ConstInt< 1 >, Minus >, ExpList< For< 6, 0, ConstInt< 0 >, BinOp< SimpleVar< 0, 0 >, ConstInt< 1 >, Minus >, FuncCall< builtin_funcs, 0, TList< If< BinOp< SubscriptVar< SimpleVar< 2, 1 >, SimpleVar< 5, 0 > >, SimpleVar< 6, 0 >, Equal >, ConstString< 0 >, ConstString< 1 > > > > >, ExpList< FuncCall< builtin_funcs, 0, TList< ConstString< 2 > > > > > >, ExpList<
FuncCall<builtin_funcs, 0, TList< ConstString< 3 > > > > >, 3 >, TList<
Function< TList< TypeLnk< builtin_types, 1 > >, If< BinOp< SimpleVar< 4, 0 >, SimpleVar< 0, 0 >, Equal >, FuncCall< 3, 0, TList< > >, For< 5, 0, ConstInt< 0 >, BinOp<SimpleVar< 0, 0 >, ConstInt< 1 >, Minus >, If< If< If< BinOp< SubscriptVar< SimpleVar< 2, 0 >, SimpleVar< 5, 0 > >, ConstInt< 0 >, Equal >, BinOp<
SubscriptVar<SimpleVar< 2, 2 >, BinOp< SimpleVar< 5, 0 >, SimpleVar< 4, 0 >, Plus > >, ConstInt< 0 >, Equal >, ConstInt< 0 > >, BinOp< SubscriptVar<
SimpleVar< 2, 3 >, BinOp< BinOp< SimpleVar< 5, 0 >, ConstInt< 7 >, Plus >, SimpleVar< 4, 0 >, Minus > >, ConstInt< 0 >, Equal >, ConstInt< 0 > >, ExpList<
Assign< SubscriptVar< SimpleVar< 2, 0 >, SimpleVar< 5, 0 > >, ConstInt< 1 > >, ExpList< Assign< SubscriptVar< SimpleVar< 2, 2 >, BinOp< SimpleVar< 5, 0 >, SimpleVar< 4, 0 >, Plus > >, ConstInt< 1 > >, ExpList< Assign< SubscriptVar< SimpleVar< 2, 3 >, BinOp< BinOp< SimpleVar< 5, 0 >, ConstInt< 7 >, Plus >, SimpleVar< 4, 0 >, Minus > >, ConstInt< 1 > >, ExpList< Assign< SubscriptVar< SimpleVar< 2, 1 >, SimpleVar< 4, 0 > >, SimpleVar< 5, 0 > >, ExpList< FuncCall<
3, 1, TList< BinOp<SimpleVar< 4, 0 >, ConstInt< 1 >, Plus > > >, ExpList< Assign< SubscriptVar< SimpleVar< 2, 0 >, SimpleVar< 5, 0 > >, ConstInt< 0 > >, ExpList< Assign< SubscriptVar<SimpleVar< 2, 2 >, BinOp< SimpleVar< 5, 0 >, SimpleVar< 4, 0 >, Plus > >, ConstInt< 0 > >, ExpList< Assign< SubscriptVar<
SimpleVar< 2, 3 >, BinOp< BinOp< SimpleVar< 5, 0 >, ConstInt< 7 >, Plus >, SimpleVar< 4, 0 >, Minus > >, ConstInt< 0 > > > > > > > > > > > > >, 3 >> >, ExpList< FuncCall< 3, 1, TList< ConstInt< 0 > > > > > > > >
program_t;
const char* metasmousse::const_string[] = {" O", " .", "\012", "\012",NULL};
int main() {
return (int)program_t::eval< TList<> >::doit();