350 likes | 494 Vues
This presentation dives into the intricacies of C++ template metaprogramming, demonstrating its power and flexibility in software development. We explore the concept of parameterized types, the generative capabilities of templates, and their implementation in modern programming languages such as Java and C#. By examining practical examples, we reveal the advantages and nuances of template usage in C++, addressing common pitfalls, and open questions related to generics and type safety. Join us to enhance your understanding of metaprogramming and its role in the industry today.
E N D
Metaprogrammingfrom University to Industry Zoltán Porkoláb gsd@elte.hu http://gsd.web.elte.hu Dept. of Programming Languages and Compilers, Faculty of Informatics Eötvös Loránd University, Budapest
Agenda I always knew C++ templates were the work of the Devil, and now I'm sure... - Cliff Click cited by Todd Veldhuisen • Parameterized types • C++ Template Metaprograms • Power of generative metaprograms • Sample usages • Open questions
Parameterized types (Generics) Widely used in modern programming languages: • ADA generics • Eiffel generics • C++ templates • Java generics: Pizza, GJ, Java 1.5 • C# generics • Clean, Generic Haskell, other functional langs.
Why generics? Conventional techniques are working only for complete types: int max( int a, int b) { if ( a > b ) return a; else return b; } double max( double a, double b) { if ( a > b ) return a; else return b; } //… classdate { /* … */ }; date d = max ( d1, d2);
PreprocessorMacro #define MAX(a,b) a > b ? a : b • Works, because a macro is typeless. • Processed not by the compiler, therefore there are a number of "secondary effects": MAX( x, y)*2 ->x > y ? x : y*2 MAX( ++x, y) -> ++x > y ? ++x : y
Macro – the limits void swap( int& x, int& y) { int temp = x; x = y; y = temp; } • Does not work with macro:a macro is typeless. • We need a facility to use type parameters.
Templates template <typename T> void swap( T& x, T& y) { T temp = x; x = y; y = temp; } • Ada generics: "a form of context-sensitive macro" • C++ templates: "a clever kind of macro that obeys the scope, naming, and type rules of C++" • Does not require different types used as arguments to be explicitly related. In particular, the argument types used as a template need not be from a single inheritance hierarchy.
C++ Function Templates • Template is not a single function • A schema to instantiate functions on request • Parameter deduction • Template instantiation • Compilaton time template <typename T> T max( T a, T b) { if ( a > b ) return a; else return b; } int i = 3, j = 6, k; double x = 3.14, y = 4.15, z; k = max(i,j); z = max(x,y);
C++ Function Templates 2. • Strong type system rules are applied int i = 3; double y = 3.14, z; z = max(i,y); // error template <typename T, typename S> T max( T a, S b) { if ( a > b ) return a; else return b; } z == 3.0; • No deduction on return type • No runtime information could be used
Explicit specialization • Explicit specialization int i = 3; double y = 3.14, z; template <typenameR, typename T, typename S> R max( T a, S b) { if ( a > b ) return a; else return b; } z = max<double>(i,y);
User specialization const char *s1 = “Hello”, *s2 = “world”; const char *s = max(s1,s2); template <> const char *max(const char *a, const char *b) { if ( strcmp(a,b) < 0 ) return a; else return b; }
Template overloading • You can provide overloadd template definitions • The compiler selects the most specific template cout << max (4,5); cout << max<double>(3.14,’6’); cout << max (“this”, “greater”); R max(S,T) T max(T, T) char *max(char *,char*)
C++ Class Templates 1. • Similar way template classes could be defined template <class T> class matrix { public: matrix( int i, int j ); matrix& operator=( const matrix &other); T at(int i, int j); //… private: vector<T> v; }; • Created always with explicit specialisation matrix<int> m(4,5);
C++ Class Templates 2. • User specialization template <> class matrix<bool> { public: matrix( int i, int j ); matrix( const matrix &other) bool at(int i, int j); private: aBetterRepresentation v; }; • Used the same way matrix<bool> m(4,5);
C++ Templates • The C++ templates were first implemented in the early ’90s • Accepted as part of the ANSI/ISO in 1994 • Erwin Unruh: 1994 unruh.cpp 30: conversion from enum to D<2> requested unruh.cpp 30: conversion from enum to D<3> requested unruh.cpp 30: conversion from enum to D<5> requested unruh.cpp 30: conversion from enum to D<7> requested unruh.cpp 30: conversion from enum to D<11> requested unruh.cpp 30: conversion from enum to D<13> requested unruh.cpp 30: conversion from enum to D<17> requested unruh.cpp 30: conversion from enum to D<19> requested
Power of C++ templates • The Compiler “executes” template metaprograms • The result is a non-templated program executed in“run-time” • In 1966 Böhm and Jacopini proved: Turing machine implementation <==> conditional and looping constructions • The C++ templates are Turing-complete in compilation time
The Factorial example The run-time solution int factorial( int n) { if ( n == 1 ) return 1; else return n*factorial(n-1); } int main() { cout << factorial(15) << endl; return 0; }
The Factorial example The metaprogram solution template <int N> struct Factorial { enum { value = N * Factorial<N-1>::value }; }; template <> struct Factorial<1> { enum { value = 1 }; }; int main() { const int fact15 = Factorial<15>::value; std::cout << fact15 << endl; return 0; }
Conditional statement template <bool condition, class Then, class Else> struct IF { typedef Then RET; }; template <class Then, class Else> struct IF<false, Then, Else> { typedef Else RET; }; template <typename T, typename S> IF< sizeof(T)<sizeof(S), S, T>::RET max(T t, S s) { if (t > s) return t; else return s; }
(Run-time) Programs vs. Metaprograms • Function • (runtime) Data • Variable • Condition • Loop • Assignment • Class • Type and constant • Symbolic names • Type selection • Recursion • No assignment
Data in Template Metaprograms • Referential transparency: No assignment • Still possible to store, modify, and retrieve data • Typelist struct NullType {}; typedef Typelist< char, Typelist<signed char, Typelist<unsigned char, NullType> > > Charlist; char signed char unsigned char NullType
Data handling template <class TList> struct Length; template <> struct Length<NullType> { enum { value = 0 }; }; template <class T, class U> struct Length <Typelist<T,U> > { enum { value = 1 + Length<U>::value }; };
Motivation int main() { const unsigned int di = 12; const unsigned int oi = 014; const unsigned int hi = 0xc; const unsigned int bi0 = binary_value("1101"); const unsigned int bi1 = binary<1100>::value; } template<unsigned long N> struct binary { // prepend higher bits to lowest bit static const int value=binary<N/10>::value*2+N%10; }; template<> struct binary<0> // specialization { static unsigned const value = 0; };
Motivation • Constant expression – array size, case label, etc… • Better code compiled • Faster in run-time • Syntactically checked – the language semantic is extended Design time Compilation time Run-time t Decisions on Stategies, policies Template metaprograms apply, automatic config of the program No change in type system Change in run-time values
Generative Metaprograms • metaprogramming: writing programs that represent and manipulate other programs or themselves (ie:reflection). Metaprograms are programs about programs. • introspection: the ability of a program to observe its own state • intercession: the ability to modify its own state Open compilers: transformations on AST Hygenic macros (Scheme) Two level languages: AspectJ, Template Haskell
Areas of Template Metaprogramming • Expression templates • Blitz+, PETE • Static interface checking • Early catch of syntactical/semantical errors • Extending the C++ type system • Introspection • Code adaption/optimalization • Language embedding
Expression templates Array a, b, c, d, e; // Object-oriented way of a = b + c + d + e double* _t1 = new double[N]; for ( int i=0; i<N; ++i) _t1[i] = b[i] + c[i]; double* _t2 = new double[N]; for ( int i=0; i<N; ++i) _t2[i] = _t1[i] + d[i]; double* _t3 = new double[N*M]; for ( int i=0; i<N; ++i) _t3[i] = _t2[i] + e[i]; for ( int i=0; i<N; ++i) a[i] = _t3[i]; delete [] _t3; delete [] _t2; delete [] _t1; // Fortran like solution: for ( int i=0; i<N; ++i) a[i] = b[i] + c[i] + d[i] + e[i];
Language embedding • SQL • Gil, et.al.: AraRat • XML parsing • Jarvi, et.al.: type-safe XML library • Regular expressions • Boost::Xpressive • Compiler embedding • boost::spirit
Language embedding • SQL example string s = ”select form ”+tName + ” where 1=1” ; if ( cond1 ) s += ” and fName1 = ” + field1; if ( cond2 ) s += ” and fName2 < ” + field2; if ( cond3 ) s += ” and fName3 = ” + field3; • Run-time errors • Injection attacks • Conversion problems
Language embedding SQL example void f() { conststring s = ( (tName / (fName1 == field1 && fName2 < field2) ) [ fName1, fName3 ] ).asSQL(); }
Language embedding Boost::Xpressive example std::string hello( "hello world!" ); sregex rex1 = sregex::compile( "(\\w+)(\\w+)!" ); sregex rex2 = (s1= +_w) >> ' ' >> (s2= +_w) >> '!'; smatch what; if( regex_match( hello, what, rex1 ) ) { std::cout << what[0] << '\n'; // whole match std::cout << what[1] << '\n'; // first capture std::cout << what[2] << '\n'; // second capture }
Embedding alternatives If you are (accidently) not a C++ programmer • .NET platform, VB, C#: • LINQ project • Java platform: • Eric Van Wijk et.al.: Attribute Grammar-based Language Extensions for Java • Stratego • TU Delft • Charles Simonyi: Intentional programming
Intentional programming Charles Simonyi • XEROX Palo Alto Bravo • Microsoft Word, Excel • Intentional software http://www.intentsoft.com
Open questions • The real expressive power • Standard tools • but: Loki from Andrei Alexandrescu • boost::mpl • Garantees (number of instantiations, etc…) • How to design? • How to debug?
Metaprogrammingfrom University to Industry Zoltán Porkoláb gsd@elte.hu http://gsd.web.elte.hu Dept. of Programming Languages and Compilers, Faculty of Informatics Eötvös Loránd University, Budapest Thank you! Questions?