SlideShare a Scribd company logo
Practical Meta-programming By Reggie Meisler
Topics
How it works in general All based around template specialization and partial template specialization mechanics Also based on the fact that we can recursively instantiate template classes with about 500 levels of depth Conceptually analogous to  functional programming languages Can only operate on types and immutable data Data is never modified, only transformed Iteration through recursion
Template mechanics Template specialization rules are simple When you specialize a template class, that specialization now acts as a higher-priority filter for any types (or integral values) that attempt to instantiate the template class
Template mechanics template  < typename  T> class  MyClass {  /*…*/  };  // Full specialization template  <> class  MyClass< int > {  /*…*/  }; // Partial specialization template  < typename  T> class  MyClass<T*> {  /*…*/  };
Template mechanics template  < typename  T> class  MyClass {  /*…*/  };  // Full specialization template  <> class  MyClass< int > {  /*…*/  }; // Partial specialization template  < typename  T> class  MyClass<T*> {  /*…*/  }; MyClass<float> goes here MyClass<int> goes here MyClass<int*> goes here
Template mechanics This filtering mechanism of  specialization and partial specialization  is like branching at compile-time When combined with recursive template instantiation, we’re able to actually construct all the fundamental components of a programming language
How it works in general // Example of a simple summation template  < int  N> struct  Sum {   // Recursive call!   static const int  value = N + Sum<N-1>::value; }; // Specialize a base case to end recursion! template  <> struct  Sum<1> {   static const int  value = 1; }; // Equivalent to  ∑ (i=1 to N)  i
How it works in general // Example of a simple summation int  mySum = Sum<10>::value; // mySum = 55 = 10 + 9 + 8 + … + 3 + 2 + 1
How it works in general // Example of a type trait that checks for const template  < typename  T> struct  IsConst {   static const bool  value =  false ; }; // Partially specialize for <const T> template  < typename  T> struct  IsConst< const  T> {   static const bool  value =  true ; };
How it works in general // Example of a type trait that checks for const bool  amIConst1 = IsConst< const float >::value; bool  amIConst2 = IsConst< unsigned >::value;  // amIConst1 = true // amIConst2 = false
Type Traits Already have slides on how these work (Go to C++/Architecture club moodle) Similar to  IsConst  example, but also allows for type transformations that remove or add qualifiers to a type, and deeper type introspection like checking if one type inherits from another Later in the slides, we’ll talk about  SFINAE , which is considered to be a very powerful type trait
Math Mathematical functions are by definition,  functional . Some input is provided, transformed by some operations, then we’re given an output This makes math functions a perfect candidate for compile-time precomputation
Fibonacci template  < int  N>  // Fibonacci function struct  Fib {   static const int  value = Fib<N-1>::value + Fib<N-2>::value; }; template  <> struct  Fib<0>  // Base case: Fib(0) = 1 {   static const int  value = 1; }; template  <> struct  Fib<1>  // Base case: Fib(1) = 1 {   static const int  value = 1; };
Fibonacci Now let’s use it! // Print out 42 fib values for (  int  i = 0; i < 42; ++i )   printf( “fib(%d) = %d\n” , i, Fib<i>::value); What’s wrong with this picture?
Real-time vs Compile-time Oh crap! Our function doesn’t work with real-time variables as inputs! It’s completely impractical to have a function that takes only  literal  values We might as well just calculate it out and type it in, if that’s the case!
Real-time vs Compile-time Once we create compile-time functions, we need to convert their results into real-time data We need to drop all the data into a table  (Probably an array for O(1) indexing) Then we can access our data in a practical manner  (Using real-time variables, etc)
Fibonacci Table int  FibTable[ MAX_FIB_VALUE ];  // Our table template  < int  index = 0> struct  FillFibTable {   static void  Do()   {   FibTable[index] = Fib<index>::value;     FillFibTable<index + 1>::Do();  // Recursive loop, unwinds at compile-time    } }; // Base case, ends recursion at MAX_FIB_VALUE  template  <> struct  FillFibTable<MAX_FIB_VALUE> {   static void  Do() {} };
Fibonacci Table Now our Fibonacci numbers can scale based on the value of  MAX_FIB_VALUE , without any extra code To build the table we can just start the template recursion like so: FillFibTable<>::Do(); The template recursion should compile into code equivalent to: FibTable[0] = 1; FibTable[1] = 1;  // etc… until MAX_FIB_VALUE
Using Fibonacci // Print out 42 fib values for (  int  i = 0; i < 42; ++i )   printf( “fib(%d) = %d\n” , i, FibTable[i]); // Output: // fib(0) = 1 // fib(1) = 1 // fib(2) = 2 // fib(3) = 3 // …
The Meta Tradeoff Now we can quite literally see the tradeoff for meta-programming’s magical O(1) execution time A classic memory vs speed problem Meta, of course, favors speed over memory Which is more important for your situation?
Compile-time recursive function calls Similar to how we unrolled our loop for filling the Fibonacci table, we can unroll other loops that are usually placed in mathematical calculations to reduce code size and complexity As you’ll see, this increases the flexibility of your code while giving you  near-hard-coded  performance
Dot Product template  < typename  T,  int  Dim> struct  DotProd {   static  T Do( const  T* a,  const  T* b)   {   // Recurse (Ideally unwraps to the  hard-coded equivalent  in assembly)   return  (*a) * (*b) + DotProd<T, Dim – 1>::Do(a + 1, b + 1);   } }; // Base case: end recursion at single element vector dot prod template  < typename  T> struct  DotProd<T, 1> {   static  T Do( const  T* a,  const  T* b)   {   return  (*a) * (*b);   } };
Dot Product // Syntactic sugar template  < typename  T,  int  Dim> T DotProduct(T (&a)[Dim], T (&b)[Dim]) {   return  DotProd<T, Dim>::Do(a, b); } // Example use float  v1[3] = { 1.0f, 2.0f, 3.0f }; float  v2[3] = { 4.0f, 5.0f, 6.0f }; DotProduct(v1, v2);  // = 32.0f Always take advantage of auto-type detection!
Dot Product // Other possible method, assuming POD vector // * Probably more practical template  < typename  T> float  DotProduct( const  T& a,  const  T& b) {   static const size_t  Dim =  sizeof (T)/ sizeof ( float );   return  DotProd< float , Dim>::Do(( float *)&a,   ( float *)&b); }
Dot Product // Other possible method, assuming POD vector // * Probably more practical template  < typename  T> float  DotProduct( const  T& a,  const  T& b) {   static const size_t  Dim =  sizeof (T)/ sizeof ( float );   return  DotProd< float , Dim>::Do(( float *)&a,   ( float *)&b); } We can auto-determine the dimension based on size since T is a POD vector
Approximating Sine Sine is a function we’d usually like to approximate for speed reasons Unfortunately, we’ll only get exact values on a degree-by-degree basis Because sine technically works on an uncountable set of numbers (Real Numbers)
Approximating Sine template  < int  degrees> struct  Sine {   static const float  radians;   static const float  value; }; template  < int  degrees> const float  Sine<degrees>::radians = degrees*PI/180.0f; // x – x 3 /3! + x 5 /5! – x 7 /7!  (A very good approx) template  < int  degrees> const   float  Sine<degrees>::value = radians - ((radians*radians*radians)/6.0f) + ((radians*radians*radians*radians*radians)/120.0f) – ((radians*radians*radians*radians*radians*radians*radians)/5040.0f);
Approximating Sine template  < int  degrees> struct  Sine {   static const float  radians;   static const float  value; }; template  < int  degrees> const float  Sine<degrees>::radians = degrees*PI/180.0f; // x – x 3 /3! + x 5 /5! – x 7 /7!  (A very good approx) template  < int  degrees> const   float  Sine<degrees>::value = radians - ((radians*radians*radians)/6.0f) + ((radians*radians*radians*radians*radians)/120.0f) – ((radians*radians*radians*radians*radians*radians*radians)/5040.0f); Floats can’t be declared inside the template class Need radians for Taylor Series formula Our approximated result
Approximating Sine We’ll use the same technique as shown with the Fibonacci meta function for generating a real-time data table of Sine values from 0-359 degrees Instead of accessing the table for its values directly, we’ll use an interface function We can just floor any in-between degree values to index into our table
Final Result: FastSine // Approximates sine by degree float  FastSine( float  radians) {   // Convert to degrees and floor result   unsigned  degrees =  unsigned (radians * 180.0f/PI);     // Wrap degrees and index SineTable   return  SineTable[degrees % 360]; }
Tuples Ever want a heterogeneous container? You’re in luck! A Tuple is simple, elegant, sans polymorphism, and 100% type-safe! A Tuple is a static data structure defined recursively by templates
Tuples struct  NullType {};  // Empty structure template  < typename  T,  typename  U = NullType> struct  Tuple {   typedef  T head;   typedef  U tail;   T data;   U next; };
Making a Tuple typedef  Tuple< int ,   Tuple< float ,   Tuple<MyClass>>>   MyType;  MyType t; t.data  // Element 1 t.next.data  // Element 2 t.next.next.data  // Element 3 This is what I mean by “recursively defined”
Tuple in memory Tuple<int, Tuple<float, Tuple<MyClass>>> data:  int next:  Tuple<float, Tuple<MyClass>> data:  float next:  Tuple<MyClass > data:  MyClass next:  NullType
Tuple<MyClass> Tuple<float, Tuple<MyClass>> Tuple<int, Tuple<float, Tuple<MyClass>>> data:  int data:  float data:  MyClass NullType next next next
Better creation template  < typename  T1 = NullType,  typename  T2 = NullType, …> struct  MakeTuple; template  < typename  T1> struct  MakeTuple<T1, NullType, …>  // Tuple of one type {   typedef  Tuple<T1> type; }; template  < typename  T1,  typename  T2> struct  MakeTuple<T1, T2, …>  // Tuple of two types {   typedef  Tuple<T1, Tuple<T2>> type; }; // Etc… Not the best solution, but simplifies syntax
Making a Tuple Pt 2 typedef  MakeTuple< int ,  float , MyClass>   MyType; MyType t; t.data  // Element 1 t.next.data  // Element 2 t.next.next.data  // Element 3 But can we do something about this indexing mess? Better
Better indexing template  < int  index> struct  GetValue {   template  < typename  TList>   static   typename  TList::head& From(TList& list)   {   return  GetValue<index-1>::From(list.next);  // Recurse   } }; template  <> struct  GetValue<0>  // Base case: Found the list data {   template  < typename  TList>   static   typename  TList::head& From(TList& list)   {   return list.data;   } }; It’s a good thing we made those typedefs Making use of template function auto-type detection again
Better indexing // Just to sugar up the syntax a bit #define  TGet(list, index) \  GetValue<index>::From(list)
Delicious Tuple MakeTuple< int ,  float , MyClass> t; // TGet works for both access and mutation TGet(t, 0)  // Element 1 TGet(t, 1)  // Element 2 TGet(t, 2)  // Element 3
Tuple There are many more things you can do with Tuple, and many more implementations you can try (This is probably the simplest) Tuples are both heterogeneous containers, as well as recursively-defined types This means there are a lot of potential uses for them Consider how this might be used for messaging or serialization systems
SFINAE (Substitution Failure Is Not An Error) What is it? A way for the compiler to deal with this: struct  MyType {  typedef int  type; }; // Overloaded template functions template  < typename  T> void  fnc(T arg); template  < typename  T> void  fnc( typename  T::type arg); void  main() {   fnc<MyType>(0);  // Calls the second fnc   fnc< int >(0);  // Calls the first fnc (No error) }
SFINAE (Substitution Failure Is Not An Error) When dealing with overloaded function resolution, the compiler can  silently reject ill-formed function signatures As we saw in the previous slide,  int  was ill-formed when matched with the function signature containing,  typename  T::type, but this did not cause an error
Does MyClass have an iterator? // Define types of different sizes  typedef   long  Yes; typedef   short  No; template  < typename  T> Yes fnc( typename  T::iterator*);  // Must be pointer! template  < typename  T> No fnc(…);  // Lowest priority signature void  main() {   // Sizeof check, can observe types without calling fnc   printf( “Does MyClass have an iterator? %s \n” ,   sizeof (fnc<MyClass>(0)) ==  sizeof (Yes) ?  “Yes”  :  “No” ); }
Nitty Gritty We can use sizeof to inspect the return value of the function without calling it We pass the overloaded function 0 (A null ptr to type T) If the function signature is not ill-formed with respect to type T, the null ptr will be  less implicitly convertible  to the ellipses
Nitty Gritty Ellipses are SO low-priority in terms of function overload resolution, that any function that even stands a chance of working (is  not  ill-formed) will be chosen instead! So if we want to check the existence of something on a given type, all we need to do is figure out whether or not the compiler chose the ellipses function
Check for member function // Same deal as before, but now requires this struct // (Yep, member function pointers can be template //  parameters) template  < typename  T, T& (T::*)( const  T&)> struct  SFINAE_Helper; // Does my class have a * operator? // (Once again, checking w/ pointer) template  < typename  T> Yes fnc(SFINAE_Helper<T, &T::operator*>*); template  < typename  T> No fnc(…);
Nitty Gritty This means we can silently inspect any public member of a given type at compile-time! For anyone who was disappointed about C++0x dropping concepts, they still have a potential implementation in C++ through SFINAE
Questions?

More Related Content

PPT
Diploma ii cfpc- u-5.1 pointer, structure ,union and intro to file handling
PPT
Bsc cs 1 pic u-5 pointer, structure ,union and intro to file handling
PPT
Mca 1 pic u-5 pointer, structure ,union and intro to file handling
PPT
pointer, structure ,union and intro to file handling
PPT
pointer, structure ,union and intro to file handling
PPT
Advanced C programming
PPT
Generic Programming seminar
PPT
Lecture 5
Diploma ii cfpc- u-5.1 pointer, structure ,union and intro to file handling
Bsc cs 1 pic u-5 pointer, structure ,union and intro to file handling
Mca 1 pic u-5 pointer, structure ,union and intro to file handling
pointer, structure ,union and intro to file handling
pointer, structure ,union and intro to file handling
Advanced C programming
Generic Programming seminar
Lecture 5

What's hot (20)

PPT
Glimpses of C++0x
PPT
Lap trinh C co ban va nang cao
PPT
Csharp4 operators and_casts
PDF
Data types in c++
PDF
C Programming - Refresher - Part II
PPT
C++ Programming Course
PDF
(4) cpp automatic arrays_pointers_c-strings
PDF
Generic programming
PPT
C++ Advanced
PDF
Applicative Functor
PPTX
Presentation 5th
PDF
Definitions of Functional Programming
PPTX
Pointers,virtual functions and polymorphism cpp
PPT
Advanced pointers
PDF
Regular types in C++
PDF
仕事で使うF#
PDF
Lecturer23 pointersin c.ppt
PPTX
Modern C++
PPTX
Advance topics of C language
Glimpses of C++0x
Lap trinh C co ban va nang cao
Csharp4 operators and_casts
Data types in c++
C Programming - Refresher - Part II
C++ Programming Course
(4) cpp automatic arrays_pointers_c-strings
Generic programming
C++ Advanced
Applicative Functor
Presentation 5th
Definitions of Functional Programming
Pointers,virtual functions and polymorphism cpp
Advanced pointers
Regular types in C++
仕事で使うF#
Lecturer23 pointersin c.ppt
Modern C++
Advance topics of C language
Ad

Viewers also liked (16)

PDF
Generative programming (mostly parser generation)
PDF
Generative Software Development. Overview and Examples
ODP
Generative Programming In The Large - Applied C++ meta-programming
PDF
Practical Multi-language Generative Programming
PDF
Seeking Enligtenment - A journey of purpose rather tan instruction
PDF
A Generative Programming Approach to Developing Pervasive Computing Systems
PDF
Generative and Meta-Programming - Modern C++ Design for Parallel Computing
PDF
Practical pairing of generative programming with functional programming.
PDF
Japanese Open and Generative Design
PDF
Practical C++ Generative Programming
PDF
Seri Belajar Mandiri - Pemrograman C# Untuk Pemula
ODP
Probabilistic programming
PPTX
Summary - Transformational-Generative Theory
PPTX
Transformational-Generative Grammar
PPTX
Deep structure and surface structure
PPT
Ubiquitous Computing
Generative programming (mostly parser generation)
Generative Software Development. Overview and Examples
Generative Programming In The Large - Applied C++ meta-programming
Practical Multi-language Generative Programming
Seeking Enligtenment - A journey of purpose rather tan instruction
A Generative Programming Approach to Developing Pervasive Computing Systems
Generative and Meta-Programming - Modern C++ Design for Parallel Computing
Practical pairing of generative programming with functional programming.
Japanese Open and Generative Design
Practical C++ Generative Programming
Seri Belajar Mandiri - Pemrograman C# Untuk Pemula
Probabilistic programming
Summary - Transformational-Generative Theory
Transformational-Generative Grammar
Deep structure and surface structure
Ubiquitous Computing
Ad

Similar to Practical Meta Programming (20)

PPT
btech-1picu-5pointerstructureunionandintrotofilehandling-150122010700-conver.ppt
PPT
Functions
PPT
Lập trình C
PPT
C++ Function
PDF
Functions And Header Files In C++ | Bjarne stroustrup
PDF
Let's get comfortable with C++20 concepts (XM)
PPTX
Introduction to Programming c language.pptx
PPT
Btech 1 pic u-5 pointer, structure ,union and intro to file handling
PPTX
Combinators, DSLs, HTML and F#
PPT
C intro
PPTX
C# for C++ programmers
PDF
[gbgcpp] Let's get comfortable with concepts
PPTX
PPT
STL ALGORITHMS
PPT
Advanced Programming C++
PPT
JavaScript
PDF
Cpp17 and Beyond
PPTX
C programming
PDF
Let's get comfortable with C++20 concepts - Cologne C++ User group
PPT
Python 3000
btech-1picu-5pointerstructureunionandintrotofilehandling-150122010700-conver.ppt
Functions
Lập trình C
C++ Function
Functions And Header Files In C++ | Bjarne stroustrup
Let's get comfortable with C++20 concepts (XM)
Introduction to Programming c language.pptx
Btech 1 pic u-5 pointer, structure ,union and intro to file handling
Combinators, DSLs, HTML and F#
C intro
C# for C++ programmers
[gbgcpp] Let's get comfortable with concepts
STL ALGORITHMS
Advanced Programming C++
JavaScript
Cpp17 and Beyond
C programming
Let's get comfortable with C++20 concepts - Cologne C++ User group
Python 3000

Practical Meta Programming

  • 3. How it works in general All based around template specialization and partial template specialization mechanics Also based on the fact that we can recursively instantiate template classes with about 500 levels of depth Conceptually analogous to functional programming languages Can only operate on types and immutable data Data is never modified, only transformed Iteration through recursion
  • 4. Template mechanics Template specialization rules are simple When you specialize a template class, that specialization now acts as a higher-priority filter for any types (or integral values) that attempt to instantiate the template class
  • 5. Template mechanics template < typename T> class MyClass { /*…*/ }; // Full specialization template <> class MyClass< int > { /*…*/ }; // Partial specialization template < typename T> class MyClass<T*> { /*…*/ };
  • 6. Template mechanics template < typename T> class MyClass { /*…*/ }; // Full specialization template <> class MyClass< int > { /*…*/ }; // Partial specialization template < typename T> class MyClass<T*> { /*…*/ }; MyClass<float> goes here MyClass<int> goes here MyClass<int*> goes here
  • 7. Template mechanics This filtering mechanism of specialization and partial specialization is like branching at compile-time When combined with recursive template instantiation, we’re able to actually construct all the fundamental components of a programming language
  • 8. How it works in general // Example of a simple summation template < int N> struct Sum { // Recursive call! static const int value = N + Sum<N-1>::value; }; // Specialize a base case to end recursion! template <> struct Sum<1> { static const int value = 1; }; // Equivalent to ∑ (i=1 to N) i
  • 9. How it works in general // Example of a simple summation int mySum = Sum<10>::value; // mySum = 55 = 10 + 9 + 8 + … + 3 + 2 + 1
  • 10. How it works in general // Example of a type trait that checks for const template < typename T> struct IsConst { static const bool value = false ; }; // Partially specialize for <const T> template < typename T> struct IsConst< const T> { static const bool value = true ; };
  • 11. How it works in general // Example of a type trait that checks for const bool amIConst1 = IsConst< const float >::value; bool amIConst2 = IsConst< unsigned >::value; // amIConst1 = true // amIConst2 = false
  • 12. Type Traits Already have slides on how these work (Go to C++/Architecture club moodle) Similar to IsConst example, but also allows for type transformations that remove or add qualifiers to a type, and deeper type introspection like checking if one type inherits from another Later in the slides, we’ll talk about SFINAE , which is considered to be a very powerful type trait
  • 13. Math Mathematical functions are by definition, functional . Some input is provided, transformed by some operations, then we’re given an output This makes math functions a perfect candidate for compile-time precomputation
  • 14. Fibonacci template < int N> // Fibonacci function struct Fib { static const int value = Fib<N-1>::value + Fib<N-2>::value; }; template <> struct Fib<0> // Base case: Fib(0) = 1 { static const int value = 1; }; template <> struct Fib<1> // Base case: Fib(1) = 1 { static const int value = 1; };
  • 15. Fibonacci Now let’s use it! // Print out 42 fib values for ( int i = 0; i < 42; ++i ) printf( “fib(%d) = %d\n” , i, Fib<i>::value); What’s wrong with this picture?
  • 16. Real-time vs Compile-time Oh crap! Our function doesn’t work with real-time variables as inputs! It’s completely impractical to have a function that takes only literal values We might as well just calculate it out and type it in, if that’s the case!
  • 17. Real-time vs Compile-time Once we create compile-time functions, we need to convert their results into real-time data We need to drop all the data into a table (Probably an array for O(1) indexing) Then we can access our data in a practical manner (Using real-time variables, etc)
  • 18. Fibonacci Table int FibTable[ MAX_FIB_VALUE ]; // Our table template < int index = 0> struct FillFibTable { static void Do() { FibTable[index] = Fib<index>::value; FillFibTable<index + 1>::Do(); // Recursive loop, unwinds at compile-time } }; // Base case, ends recursion at MAX_FIB_VALUE template <> struct FillFibTable<MAX_FIB_VALUE> { static void Do() {} };
  • 19. Fibonacci Table Now our Fibonacci numbers can scale based on the value of MAX_FIB_VALUE , without any extra code To build the table we can just start the template recursion like so: FillFibTable<>::Do(); The template recursion should compile into code equivalent to: FibTable[0] = 1; FibTable[1] = 1; // etc… until MAX_FIB_VALUE
  • 20. Using Fibonacci // Print out 42 fib values for ( int i = 0; i < 42; ++i ) printf( “fib(%d) = %d\n” , i, FibTable[i]); // Output: // fib(0) = 1 // fib(1) = 1 // fib(2) = 2 // fib(3) = 3 // …
  • 21. The Meta Tradeoff Now we can quite literally see the tradeoff for meta-programming’s magical O(1) execution time A classic memory vs speed problem Meta, of course, favors speed over memory Which is more important for your situation?
  • 22. Compile-time recursive function calls Similar to how we unrolled our loop for filling the Fibonacci table, we can unroll other loops that are usually placed in mathematical calculations to reduce code size and complexity As you’ll see, this increases the flexibility of your code while giving you near-hard-coded performance
  • 23. Dot Product template < typename T, int Dim> struct DotProd { static T Do( const T* a, const T* b) { // Recurse (Ideally unwraps to the hard-coded equivalent in assembly) return (*a) * (*b) + DotProd<T, Dim – 1>::Do(a + 1, b + 1); } }; // Base case: end recursion at single element vector dot prod template < typename T> struct DotProd<T, 1> { static T Do( const T* a, const T* b) { return (*a) * (*b); } };
  • 24. Dot Product // Syntactic sugar template < typename T, int Dim> T DotProduct(T (&a)[Dim], T (&b)[Dim]) { return DotProd<T, Dim>::Do(a, b); } // Example use float v1[3] = { 1.0f, 2.0f, 3.0f }; float v2[3] = { 4.0f, 5.0f, 6.0f }; DotProduct(v1, v2); // = 32.0f Always take advantage of auto-type detection!
  • 25. Dot Product // Other possible method, assuming POD vector // * Probably more practical template < typename T> float DotProduct( const T& a, const T& b) { static const size_t Dim = sizeof (T)/ sizeof ( float ); return DotProd< float , Dim>::Do(( float *)&a, ( float *)&b); }
  • 26. Dot Product // Other possible method, assuming POD vector // * Probably more practical template < typename T> float DotProduct( const T& a, const T& b) { static const size_t Dim = sizeof (T)/ sizeof ( float ); return DotProd< float , Dim>::Do(( float *)&a, ( float *)&b); } We can auto-determine the dimension based on size since T is a POD vector
  • 27. Approximating Sine Sine is a function we’d usually like to approximate for speed reasons Unfortunately, we’ll only get exact values on a degree-by-degree basis Because sine technically works on an uncountable set of numbers (Real Numbers)
  • 28. Approximating Sine template < int degrees> struct Sine { static const float radians; static const float value; }; template < int degrees> const float Sine<degrees>::radians = degrees*PI/180.0f; // x – x 3 /3! + x 5 /5! – x 7 /7! (A very good approx) template < int degrees> const float Sine<degrees>::value = radians - ((radians*radians*radians)/6.0f) + ((radians*radians*radians*radians*radians)/120.0f) – ((radians*radians*radians*radians*radians*radians*radians)/5040.0f);
  • 29. Approximating Sine template < int degrees> struct Sine { static const float radians; static const float value; }; template < int degrees> const float Sine<degrees>::radians = degrees*PI/180.0f; // x – x 3 /3! + x 5 /5! – x 7 /7! (A very good approx) template < int degrees> const float Sine<degrees>::value = radians - ((radians*radians*radians)/6.0f) + ((radians*radians*radians*radians*radians)/120.0f) – ((radians*radians*radians*radians*radians*radians*radians)/5040.0f); Floats can’t be declared inside the template class Need radians for Taylor Series formula Our approximated result
  • 30. Approximating Sine We’ll use the same technique as shown with the Fibonacci meta function for generating a real-time data table of Sine values from 0-359 degrees Instead of accessing the table for its values directly, we’ll use an interface function We can just floor any in-between degree values to index into our table
  • 31. Final Result: FastSine // Approximates sine by degree float FastSine( float radians) { // Convert to degrees and floor result unsigned degrees = unsigned (radians * 180.0f/PI); // Wrap degrees and index SineTable return SineTable[degrees % 360]; }
  • 32. Tuples Ever want a heterogeneous container? You’re in luck! A Tuple is simple, elegant, sans polymorphism, and 100% type-safe! A Tuple is a static data structure defined recursively by templates
  • 33. Tuples struct NullType {}; // Empty structure template < typename T, typename U = NullType> struct Tuple { typedef T head; typedef U tail; T data; U next; };
  • 34. Making a Tuple typedef Tuple< int , Tuple< float , Tuple<MyClass>>> MyType; MyType t; t.data // Element 1 t.next.data // Element 2 t.next.next.data // Element 3 This is what I mean by “recursively defined”
  • 35. Tuple in memory Tuple<int, Tuple<float, Tuple<MyClass>>> data: int next: Tuple<float, Tuple<MyClass>> data: float next: Tuple<MyClass > data: MyClass next: NullType
  • 36. Tuple<MyClass> Tuple<float, Tuple<MyClass>> Tuple<int, Tuple<float, Tuple<MyClass>>> data: int data: float data: MyClass NullType next next next
  • 37. Better creation template < typename T1 = NullType, typename T2 = NullType, …> struct MakeTuple; template < typename T1> struct MakeTuple<T1, NullType, …> // Tuple of one type { typedef Tuple<T1> type; }; template < typename T1, typename T2> struct MakeTuple<T1, T2, …> // Tuple of two types { typedef Tuple<T1, Tuple<T2>> type; }; // Etc… Not the best solution, but simplifies syntax
  • 38. Making a Tuple Pt 2 typedef MakeTuple< int , float , MyClass> MyType; MyType t; t.data // Element 1 t.next.data // Element 2 t.next.next.data // Element 3 But can we do something about this indexing mess? Better
  • 39. Better indexing template < int index> struct GetValue { template < typename TList> static typename TList::head& From(TList& list) { return GetValue<index-1>::From(list.next); // Recurse } }; template <> struct GetValue<0> // Base case: Found the list data { template < typename TList> static typename TList::head& From(TList& list) { return list.data; } }; It’s a good thing we made those typedefs Making use of template function auto-type detection again
  • 40. Better indexing // Just to sugar up the syntax a bit #define TGet(list, index) \ GetValue<index>::From(list)
  • 41. Delicious Tuple MakeTuple< int , float , MyClass> t; // TGet works for both access and mutation TGet(t, 0) // Element 1 TGet(t, 1) // Element 2 TGet(t, 2) // Element 3
  • 42. Tuple There are many more things you can do with Tuple, and many more implementations you can try (This is probably the simplest) Tuples are both heterogeneous containers, as well as recursively-defined types This means there are a lot of potential uses for them Consider how this might be used for messaging or serialization systems
  • 43. SFINAE (Substitution Failure Is Not An Error) What is it? A way for the compiler to deal with this: struct MyType { typedef int type; }; // Overloaded template functions template < typename T> void fnc(T arg); template < typename T> void fnc( typename T::type arg); void main() { fnc<MyType>(0); // Calls the second fnc fnc< int >(0); // Calls the first fnc (No error) }
  • 44. SFINAE (Substitution Failure Is Not An Error) When dealing with overloaded function resolution, the compiler can silently reject ill-formed function signatures As we saw in the previous slide, int was ill-formed when matched with the function signature containing, typename T::type, but this did not cause an error
  • 45. Does MyClass have an iterator? // Define types of different sizes typedef long Yes; typedef short No; template < typename T> Yes fnc( typename T::iterator*); // Must be pointer! template < typename T> No fnc(…); // Lowest priority signature void main() { // Sizeof check, can observe types without calling fnc printf( “Does MyClass have an iterator? %s \n” , sizeof (fnc<MyClass>(0)) == sizeof (Yes) ? “Yes” : “No” ); }
  • 46. Nitty Gritty We can use sizeof to inspect the return value of the function without calling it We pass the overloaded function 0 (A null ptr to type T) If the function signature is not ill-formed with respect to type T, the null ptr will be less implicitly convertible to the ellipses
  • 47. Nitty Gritty Ellipses are SO low-priority in terms of function overload resolution, that any function that even stands a chance of working (is not ill-formed) will be chosen instead! So if we want to check the existence of something on a given type, all we need to do is figure out whether or not the compiler chose the ellipses function
  • 48. Check for member function // Same deal as before, but now requires this struct // (Yep, member function pointers can be template // parameters) template < typename T, T& (T::*)( const T&)> struct SFINAE_Helper; // Does my class have a * operator? // (Once again, checking w/ pointer) template < typename T> Yes fnc(SFINAE_Helper<T, &T::operator*>*); template < typename T> No fnc(…);
  • 49. Nitty Gritty This means we can silently inspect any public member of a given type at compile-time! For anyone who was disappointed about C++0x dropping concepts, they still have a potential implementation in C++ through SFINAE