What are we trying to achieve? Code reuse Supporting multiple
number formats Functionality customisation
Slide 3
What are templates?
Slide 4
Defer specification of types
Slide 5
Introducing the Accumulator class accum { private: int value_;
public: accum() : value_(0) {} void add(int n) { value_ += n; } int
result() { return value_; } };
Slide 6
Templating the Accumulator template class accum { private: int
value_; public: accum() : value_(0) {} void add(int n) { value_ +=
n; } int result() { return value_; } }; Add template declaration
Replace int with template parameter
Slide 7
Templating the Accumulator template class accum { private: Inc
value_; public: accum() : value_(0) {} void add(Inc n) { value_ +=
n; } Inc result() { return value_; } }; Add template declaration
Replace int with template parameter Generalise code that relied on
int
Slide 8
Templating the Accumulator template class accum { private: Inc
value_; public: accum() : value_() {} void add(Inc n) { value_ +=
n; } Inc result() { return value_; } }; Add template declaration
Generalise code that relied on int Replace int with template
parameter
Slide 9
Using the Accumulator accum acc1; accum > acc2; accum acc3;
Same as original code complex is part of the standard library We
can even use it to concatenate strings
Slide 10
What are templates Defer specification of types Allowed types
determined by required expressions
Slide 11
What are the expressions? template class accum { private: Inc
value_; public: accum() : value_() {} void add(Inc n) { value_ +=
n; } Inc result() { return value_; } }; Has a += operator Default
construction Can be copied The required set of operations is a
Concept
Slide 12
Concepts Entirely determined by valid expressions Generally
exist only in documentation The accum template class takes one
argument that must be Incrementable An Incremental class is default
and copy constructable and have the following valid expressions
where i1 and i2 are instances of Incrementable: i1 += i2//
Increment operator The accum template class takes one argument that
must be Incrementable An Incremental class is default and copy
constructable and have the following valid expressions where i1 and
i2 are instances of Incrementable: i1 += i2// Increment
operator
Slide 13
Concepts accum acc1; accum > acc2; accum acc3; Has built in
+= operator Have overloaded += operators
Slide 14
What happens if you fail? The compiler will error The compiler
has no knowledge of the concept The error message could be pretty
messy
Slide 15
What happens if you fail? accum acc1; accum > acc2;
acc2.add(a_list); Compiles fine Templates are only evaluated if
used error: no match for operator+= in ((accum
>*)this)->accum >::value_ += n error: no match for
operator+= in ((accum >*)this)->accum >::value_ += n This
doesnt refer to anything on this slide
Slide 16
What are templates Defer specification of types Allowed types
determined by required expressions Evaluated at compile time
Slide 17
Compilation Preprocessor Compilation Code Generation Synthesis
Templates evaluated here HDL generated here template class accum
{}; accum a; accum b; template class accum {}; accum a; accum b;
class accum_int {}; class accum_double {}; accum_int a;
accum_double b; class accum_int {}; class accum_double {};
accum_int a; accum_double b;
Slide 18
What are templates? Defer specification of types Allowed types
determined by required expressions Evaluated at compile time
Operate on semantics rather than syntax
Slide 19
Why not Macros? Macros Templates #define MIN(x,y) x accum_mult;
Non type parameters Default parameters Initialise value_ with
parameter Initialise value_ with parameter Addition typedef doesnt
change as default value can be used Addition typedef doesnt change
as default value can be used
Slide 38
Bringing it all together template class accum { int value_;
public: accum() : value_(Initial) {} void add(int n) { value_ =
Func()(value_, n); } int result() { return value_; } }; Reintroduce
Inc template parameter Reintroduce Inc template parameter
Slide 39
Bringing it all together template class accum { Inc value_;
public: accum() : value_(Initial) {} void add(Inc n) { value_ =
Func()(value_, n); } Inc result() { return value_; } }; Reintroduce
Inc template parameter Reintroduce Inc template parameter Make
addition the default
Slide 40
Bringing it all together template, int Initial = 0> class
accum { Inc value_; public: accum() : value_(Initial) {} void
add(Inc n) { value_ = Func()(value_, n); } Inc result() { return
value_; } }; Reintroduce Inc template parameter Reintroduce Inc
template parameter Make addition the default
Slide 41
Bringing it all together template, int Initial = 0> class
accum { Inc value_; public: accum() : value_(Initial) {} void
add(Inc n) { value_ = Func()(value_, n); } Inc result() { return
value_; } }; What happens if T cannot be constructed from an int?
What happens if T cannot be constructed from an int? What about the
maximum for a float/double? What about the maximum for a
float/double?
Slide 42
Someone Elses Problem template, int Initial = 0> class accum
{ Inc value_; public: accum() : value_(Initial) {} void add(Inc n)
{ value_ = Func()(value_, n); } Inc result() { return value_; } };
Make the user provide a suitable initial value Make the user
provide a suitable initial value
Slide 43
Someone Elses Problem template > class accum { Inc value_;
public: accum(Inc v = Inc()) : value_(v) {} void add(Inc n) {
value_ = Func()(value_, n); } Inc result() { return value_; } };
Make the user provide a suitable initial value Make the user
provide a suitable initial value
Slide 44
Everyone Elses Problem template class ALU { Acc1 acc1_; Acc2
acc2_; public: ALU(): acc1_(), acc2_() {} }; We have to let users
pass the correct defaults What should go here?
Slide 45
Factory Fun template > class accum { Inc value_; public:
accum(Inc v = Inc()) : value_(v) {} void add(Inc n) { value_ =
Func()(value_, n); } Inc result() { return value_; } }; Define a
simple factory
Slide 46
Factory Fun template struct default_factory { T create() {
return T(); } }; template > class accum { Inc value_; public:
accum(Inc v = Inc()) : value_(v) {} void add(Inc n) { value_ =
Func()(value_, n); } Inc result() { return value_; } }; Define a
simple factory Add the factory as a template argument
Slide 47
Factory Fun template struct default_factory { T create() {
return T(); } }; template, typename Fact = default_factory >
class accum { Inc value_; public: accum(Inc v = Inc()) : value_(v)
{} void add(Inc n) { value_ = Func()(value_, n); } Inc result() {
return value_; } }; Define a simple factory Add the factory as a
template argument Use the factory to initialise value_
Slide 48
Factory Fun template struct default_factory { T create() {
return T(); } }; template, typename Fact = default_factory >
class accum { Inc value_; public: accum(Fact f = Fact()) :
value_(f.create()) {} void add(Inc n) { value_ = Func()(value_, n);
} Inc result() { return value_; } }; Define a simple factory Add
the factory as a template argument Use the factory to initialise
value_
Slide 49
Factory Fun template struct mult_factory { T create() { return
T(1); } }; typedef accum, mult_factory > int_mult; Our
multiplication accum requires a new factory to give the correct
initial value Our multiplication accum requires a new factory to
give the correct initial value We can then typedef away the
horribleness
Slide 50
What about Max/Min? template struct max_factory { T create() {
return /* max value of T */;} }; Need a template that does
different things depending on type Need a template that does
different things depending on type
Slide 51
Specialisation template struct max_factory; template struct
max_factory { int create() { return INT_MAX; } }; template struct
max_factory { double create() { return DBL_MAX; } } We cant do
anything here so leave it undefined We cant do anything here so
leave it undefined Give the compiler a version which works for ints
One of these required for every type We probably ought to make this
possible to reuse
Slide 52
Traits template struct num_limits {}; template struct num_limts
{ static int max() { return INT_MAX; } static int min() { return
INT_MIN; } } template struct max_factory { T create() { return
num_limits ::max(); } } Create an undefined class Specialise for
what we care about Specialise for what we care about Use the trait
class to implement the factory
(Hopefully) Final - Client Code typedef accum string_append;
typedef accum, mult_factory > double_mult; typedef accum,
max_factory > int_and; We could make this nicer with MORE traits
but we have to stop somewhere. We could make this nicer with MORE
traits but we have to stop somewhere.
Slide 57
Compile time dimensional analysis
Slide 58
Consider a simple unit system Three base units meter for length
second for time kilogram for mass All derived units have a scaling
factor of one Only integer powers
Slide 59
Define our quantity template struct quantity { double value;
quantity(double value):value(value) {} }; typedef quantity meter;
typedef quantity second; typedef quantity kilogram; Length, time
and mass are template parameters Length, time and mass are template
parameters Constructor to make creating quantities easier
Constructor to make creating quantities easier Construction can be
implicit from a double
Slide 60
Addition template quantity operator+ (quantity lhs, quantity
rhs) { return lhs.value + rhs.value; } The two units have to be the
same Add the values Function can take any quantity type Function
can take any quantity type
Slide 61
Multiplication template quantity operator*(quantity lhs,
quantity rhs) { return lhs.value * rhs.value; } The two quantities
may be different types Return type sums the powers of the base
units Return type sums the powers of the base units The only code
executed at runtime is the multiplication Division is similar
Slide 62
Using our system typedef quantity speed; typedef quantity
acceleration; speed s = meters(2) / seconds(1); acceleration a = s
/ seconds(1); speed s2 = meters(2) * seconds(1); Compile time
error
Slide 63
Benefits All dimensional analysis is compile time only No
runtime overhead No effect on synthesisability All this is provided
by Boost in a more general way Preprocessor issues prevent
synthesis (for now)
Slide 64
Well some of you asked
Slide 65
What is it? We can get the compiler to perform computation
Discovered rather than invented A (sort of) real world use of
functional programming
Slide 66
Hello World! (-ish) template struct factorial { static const
int value = ???; }; Can we make value equal to the factorial of I ?
Can we make value equal to the factorial of I ? No loops, but we
can recurse
Slide 67
Hello World! (-ish) template struct factorial { static const
int value = I * factorial ::value; }; template struct factorial {
static const int value = 1; } We just copy the mathematical
recursive definition We just copy the mathematical recursive
definition Specialisation to implement the base case Specialisation
to implement the base case
Slide 68
Use cases? Static loop unrolling
Slide 69
Loop Unrolling int accumulate(int value) { static accum acc;
acc.add(value); return acc.result(); } Higher level synthesis
wrapper functions need to hold state statically Higher level
synthesis wrapper functions need to hold state statically What
happens if we want to compose wrapper functions?
Slide 70
Loop Unrolling int accumulate(int value) { static accum acc;
acc.add(value); return acc.result(); } int accum_wrapper(int value)
{ if (value % 2 == 0) return accumulate(value); else return
accumulate(value); } Wed like to accumulate odd and even numbers
separately. Wed like to accumulate odd and even numbers separately.
Both accumulate function calls will use the same static data
Slide 71
Loop Unrolling template int accumulate(int value) { static
accum acc; acc.add(value); return acc.result(); } int
accum_wrapper(int value) { if (value % 2 == 0) return
accumulate(value); else return accumulate(value); } Make the
function a template Use different template parameters for each
instance Use different template parameters for each instance No
effect on functionality, just allows us to create independent
instances No effect on functionality, just allows us to create
independent instances
Slide 72
Loop Unrolling template int accumulate(int value) { static
accum acc; acc.add(value); return acc.result(); } int
accum_wrapper(int value) { if (value % 2 == 0) return accumulate
(value); else return accumulate (value); } Make the function a
template Use different template parameters for each instance Use
different template parameters for each instance No effect on
functionality, just allows us to create independent instances No
effect on functionality, just allows us to create independent
instances What if we want to control the number of instances?
Slide 73
Loop Unrolling template struct looper { static int loop(int
value, int key) { if (key == I) return accumulate (value); else
return looper ::loop(value, key); } } template struct looper {
static int loop(int value, int key) { return accumulate (value); }
} key is the runtime provided index Recursive call to next index
Base case to avoid infinite recursion
Slide 74
Loop Unrolling int accum_wrapper(int value) { return looper
::loop(value, value % 2); } int accum_wrapper(int value) { return
looper ::loop(value, value % 16); } This is the maximum index
rather than the loop iterator count This is the maximum index
rather than the loop iterator count Easily extendable to 16 (or
more) accumulators
Slide 75
Use Cases? Static loop unrolling Type conditional compilation
Calculation of bus widths/ranges Other things I havent thought
of