Object-Oriented Programming for Scientific Computing
Template Metaprogramming
Ole Klein
Interdisciplinary Center for Scientific Computing Heidelberg University
ole.klein@iwr.uni-heidelberg.de
30. Juni 2015
Calculating the Square Root
We can calculate the square root as follows using nested intervals:
# include< i o s t r e a m >
t e m p l a t e< std :: s i z e _ t N , std :: s i z e _ t L =1 , std :: s i z e _ t H = N >
s t r u c t S q r t {
p u b l i c:
e n u m{ mid = ( L + H +1) /2 };
e n u m{ v a l u e = ( N < mid * mid ) ? ( std :: s i z e _ t ) Sqrt < N , L , mid -1 >:: v a l u e : ( std :: s i z e _ t ) Sqrt < N , mid , H >:: v a l u e };
};
t e m p l a t e< std :: s i z e _ t N , std :: s i z e _ t M >
s t r u c t Sqrt < N , M , M >
{
e n u m{ v a l u e = M };
};
int m a i n () {
std :: cout < < Sqrt <9 >:: value < <" "< < Sqrt <42 >:: value < < std :: e n d l ; }
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 2 / 51
Template Instantiations
•
Calculating
Sqrt<9>first leads to the execution of:
Sqrt <9 ,1 ,9 >:: v a l u e = (9 <25) ? Sqrt <9 ,1 ,4 >:: v a l u e : Sqrt <9 ,5 ,9 >:: v a l u e = Sqrt <9 ,1 ,4 >:: v a l u e
As a result
Sqrt<9,1,4>is calculated next:
Sqrt <9 ,1 ,4 >:: v a l u e = (9 <9) ? Sqrt <9 ,1 ,2 >:: v a l u e : Sqrt <9 ,3 ,4 >:: v a l u e = Sqrt <9 ,3 ,4 >:: v a l u e
The next recursion step is then:
Sqrt <9 ,3 ,4 >:: v a l u e = (9 <16) ? Sqrt <9 ,3 ,3 >:: v a l u e : Sqrt <9 ,4 ,3 >:: v a l u e = Sqrt <9 ,3 ,3 >:: v a l u e = 3
•
However, there is a problem with the ternary operator
<condition>?<true-path>:<false-path>
. The compiler generates not just the
relevant part, but also the other that is not used. This means it has to
expand the next recursion level on that side as well (although the result will
Type Selection at Compile Time
We can get rid of the unnecessary template instantiations by simply selecting the correct type and evaluating it directly.
This can be carried out with a small metaprogram that corresponds to an
ifstatement (also called a “compile time type selection”).
// D e f i n i t i o n i n c l u d i n g s p e c i a l i z a t i o n f o r t h e t r u e c a s e t e m p l a t e<b o o l B , t y p e n a m e T1 , t y p e n a m e T2>
s t r u c t I f T h e n E l s e {
t y p e d e f T1 R e s u l t T ; };
// P a r t i a l s p e c i a l i z a t i o n f o r t h e f a l s e c a s e t e m p l a t e<t y p e n a m e T1 , t y p e n a m e T2>
s t r u c t I f T h e n E l s e<f a l s e , T1 , T2>
{
t y p e d e f T2 R e s u l t T ; };
#e n d i f
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 4 / 51
Improved Calculation of the Square Root
Using our meta-
ifstatement we can implement the square root as follows:
t e m p l a t e<s t d : : s i z e t N , s t d : : s i z e t L=1 , s t d : : s i z e t H=N>
s t r u c t S q r t {
p u b l i c:
enum{ mid = ( L+H+1) /2 };
t y p e d e f t y p e n a m e I f T h e n E l s e<(N<mid∗mid ) , S q r t<N , L , mid−1>,
S q r t<N , mid , H> >: : R e s u l t T R e s u l t T y p e ;
enum{ v a l u e = R e s u l t T y p e : : v a l u e }; };
t e m p l a t e<s t d : : s i z e t N , s t d : : s i z e t M>
s t r u c t S q r t<N ,M,M>
{
enum{ v a l u e = M };
Turing Completeness of Template Metaprogramming
Template meta programs may include:
•
State variables: the template parameters.
•
Loops: using recursion.
•
Conditional execution: using the ternary operator or template specialization (e.g. the meta-
if).
•
Integer calculations.
This is sufficient to perform any calculation, as long as there isn’t any restriction on the number of recursive instantiations and on the number of state variables (which does not imply that it is useful to calculate everything with template metaprogramming) .
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 6 / 51
Loop Unrolling
In the calculation of a scalar product, as in:
t e m p l a t e<t y p e n a m e T >
i n l i n e T d o t _ p r o d u c t (int dim , T * a , T * b ) {
T r e s u l t = T () ;
for (int i =0; i < dim ;++ i ) {
r e s u l t += a [ i ]* b [ i ];
}
r e t u r n r e s u l t ; }
the compiler often optimizes the computation for large arrays. However, if small scalar products of the type
dp = d o t _ p r o d u c t (3 , a , b ) ;
Loop Unrolling
// P r i m a r y t e m p l a t e
t e m p l a t e <int DIM , t y p e n a m e T >
c l a s s D o t P r o d u c t {
p u b l i c:
s t a t i c T r e s u l t ( T * a , T * b ) {
r e t u r n * a * * b + D o t P r o d u c t < DIM -1 , T >:: r e s u l t ( a +1 , b +1) ; }
};
// P a r t i a l s p e c i a l i z a t i o n as s t o p p i n g c r i t e r i o n t e m p l a t e <t y p e n a m e T >
c l a s s D o t P r o d u c t <1 , T >
{
p u b l i c:
s t a t i c T r e s u l t ( T * a , T * b ) {
r e t u r n * a * * b ; }
};
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 8 / 51
Loop Unrolling
// for s i m p l i f i c a t i o n
t e m p l a t e <int DIM , t y p e n a m e T >
i n l i n e T d o t _ p r o d u c t ( T * a , T * b ) {
r e t u r n D o t P r o d u c t < DIM , T >:: r e s u l t ( a , b ) ; }
Application: Loop Unrolling
# i n c l u d e < i o s t r e a m >
# i n c l u d e " l o o p _ u n r o l l i n g . h "
int m a i n () {
int a [3] = { 1 , 2 , 3};
int b [3] = { 5 , 6 , 7};
std :: c o u t < < " d o t _ p r o d u c t <3 >( a , b ) = "
< < d o t _ p r o d u c t <3 >( a , b ) < < ’ \ n ’;
std :: c o u t < < " d o t _ p r o d u c t <3 >( a , a ) = "
< < d o t _ p r o d u c t <3 >( a , a ) < < ’ \ n ’;
}
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 10 / 51
Loop Unrolling for Random Access Container
// P r i m a r y t e m p l a t e
t e m p l a t e <int DIM , t y p e n a m e T , t e m p l a t e<t y p e n a m e U ,t y p e n a m e= std :: a l l o c a t o r < U > > c l a s s vect >
s t r u c t D o t P r o d u c t {
s t a t i c T r e s u l t (c o n s t vect < T > & a , c o n s t vect < T > & b ) {
r e t u r n a [ DIM - 1 ] * b [ DIM -1] +
D o t P r o d u c t < DIM -1 , T , vect >:: r e s u l t ( a , b ) ; }
};
// P a r t i a l s p e c i a l i z a t i o n as s t o p p i n g c r i t e r i o n t e m p l a t e <t y p e n a m e T , t e m p l a t e<t y p e n a m e
U ,t y p e n a m e= std :: a l l o c a t o r < U > > c l a s s vect >
s t r u c t D o t P r o d u c t <1 , T , vect >
{
s t a t i c T r e s u l t (c o n s t vect < T > & a , c o n s t vect < T > & b )
Loop Unrolling for Random Access Container
// For s i m p l i f i c a t i o n
t e m p l a t e <int DIM , t y p e n a m e T , t e m p l a t e<t y p e n a m e U ,t y p e n a m e= std :: a l l o c a t o r < U > > c l a s s vect >
i n l i n e T d o t _ p r o d u c t (c o n s t vect < T > & a , c o n s t vect < T > & b ) {
r e t u r n D o t P r o d u c t < DIM , T , vect >:: r e s u l t ( a , b ) ; }
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 12 / 51
Application: Loop Unrolling for Random Access Container
# i n c l u d e < i o s t r e a m >
# i n c l u d e < vector >
# i n c l u d e " l o o p _ u n r o l l i n g 2 . h "
int m a i n () {
std :: vector <double> a (3 ,3.0) ; std :: vector <double> b (3 ,5.0) ;
std :: c o u t < < " d o t _ p r o d u c t <3 >( a , b ) = "
< < d o t _ p r o d u c t <3 >( a , b ) < < ’ \ n ’;
std :: c o u t < < " d o t _ p r o d u c t <3 >( a , a ) = "
< < d o t _ p r o d u c t <3 >( a , a ) < < ’ \ n ’;
}
constexpr
# include< i o s t r e a m >
int x1 = 7;
c o n s t e x p r int x2 = 7;
c o n s t e x p r int x3 = x1 ; // Error , x1 is not a c o n s t e x p r c o n s t e x p r int x4 = x2 ;
c o n s t e x p r int Fac (int n ) {
r e t u r n n <2 ? 1 : n * Fac ( n -1) ; }
int m a i n () {
std :: c o u t < < Fac ( 1 0 ) < < std :: e n d l ; }
•
C++11 introduces a simple alternative to template metaprogramming:
expressions that are already evaluated at compile time.
•
In a
constexpronly variables or functions which are
constexprthemselves may be used.
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 14 / 51
constexpr
It must be possible to evaluate a
constexprat compile time:
v o i d f (int n ) {
c o n s t e x p r int x = Fac ( n ) ; // Error , n isn ’ t k n o w n at // t i m e of t r a n s l a t i o n
int f10 = Fac ( 1 0 ) ; // C o r r e c t
}
int m a i n () {
c o n s t int ten = 10;
int f10 = Fac ( 1 0 ) ; // A l s o c o r r e c t
}
constexpr
This will work even for objects of classes whose constructor is simple enough to be defined as
constexpr:
s t r u c t P o i n t {
int x , y ;
c o n s t e x p r P o i n t (int xx , int yy ) : x ( xx ) , y ( yy ) {}
};
c o n s t e x p r P o i n t o r i g o (0 ,0) ; c o n s t e x p r int z = o r i g o . x ;
c o n s t e x p r P o i n t a [] = { P o i n t (0 ,0) , P o i n t (1 ,1) , P o i n t (2 ,2) };
c o n s t e x p r x = a [ 1 ] . x ; // x b e c o m e s 1
• constexpr
functions may not have the return type
voidand neither variables nor functions may be defined within them (this also applies to
constexprconstructors).
•
The function body can only contain declarations and a single
returnstatement.
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 16 / 51
Example: Numbers with Units
Template Metaprogramming Example: Numbers with Units
•
When performing calculations with physical quantities errors may occur.
•
The worst case scenario is comparing apples and oranges.
•
The aim is the construction of a class which allows calculating with units.
•
The implementation uses template metaprogramming. All calculations (except the conversion for input and output) are as fast as without units.
•
The necessary tests are performed at compile time and automatically
optimized out.
Example: Numbers with Units
Units: Unit Class
We first introduce a template class for units:
t e m p l a t e<int M , int K , int S >
s t r u c t U n i t {
e n u m { m = M , kg = K , s = S };
};
u s i n g M = Unit <1 ,0 ,0 >; // M e t e r s u s i n g Kg = Unit <0 ,1 ,0 >; // K i l o g r a m u s i n g S = Unit <0 ,0 ,1 >; // S e c o n d s
u s i n g MpS = Unit <1 ,0 , -1 >; // M e t e r per s e c o n d ( m / s )
u s i n g M p S 2 = Unit <1 ,0 , -2 >; // M e t e r per s e c o n d s q u a r e d ( m /( s * s ) )
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 18 / 51
Example: Numbers with Units
Enum Classes
e n u m { RED , GREEN , B L U E }; // C e n u m
e n u m c l a s s C o l o r { RED , GREEN , B L U E }; // C + + 1 1 e n u m
•
In C enums are simply integer values with a name.
•
The same name may be used only once.
•
Such an enum can be used in exactly the same places where any other integer value can be used.
•
C++11 introduces enum classes, so that each enum class is its own type and has its own name and thus namespace. This way both problems described above are solved.
•
Such enums can only be cast to integer explicitly, and therefore C enums
remain useful for template metaprogramming.
Example: Numbers with Units
Enum Classes
# include< i o s t r e a m >
e n u m c l a s s T i m e I n t e g r a t i o n : u n s i g n e d c h a r { EE = 2 , IE = 4 , CN = 8 , B D F 2 = 1 6 } ;
// u s e s int as i n t e r n a l d a t a t y p e
e n u m c l a s s S p a t i a l I n t e g r a t i o n { CCFV , FCFV , FE , DG };
t e m p l a t e< T i m e I n t e g r a t i o n T , S p a t i a l I n t e g r a t i o n S >
v o i d D o T i m e S t e p () {
// e x p l i c i t c o n v e r s i o n to int p o s s i b l e
std :: c o u t < < (u n s i g n e d int) T < < " " < < (int) S < < std :: e n d l ; }
int m a i n () {
// s c o p e has to be i n c l u d e d
D o T i m e S t e p < T i m e I n t e g r a t i o n :: CN , S p a t i a l I n t e g r a t i o n :: FE >() ; T i m e I n t e g r a t i o n ti = T i m e I n t e g r a t i o n :: IE ;
ti = 1; // not p o s s i b l e , no i m p l i c i t c o n v e r s i o n
S p a t i a l I n t e g r a t i o n si = ti ; // not p o s s i b l e , w r o n g t y p e }
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 20 / 51
Example: Numbers with Units
Units: Helper Classes
In order to calculate with units, we require some helper classes. We build template functions with
usingdeclarations.
t e m p l a t e<t y p e n a m e U1 , t y p e n a m e U2 >
s t r u c t U p l u s {
u s i n g t y p e = Unit < U1 :: m + U2 :: m , U1 :: kg + U2 :: kg , U1 :: s + U2 :: s >;
};
t e m p l a t e<t y p e n a m e U1 , t y p e n a m e U2 >
u s i n g U n i t _ p l u s = t y p e n a m e Uplus < U1 , U2 >:: t y p e ; t e m p l a t e<t y p e n a m e U1 , t y p e n a m e U2 >
s t r u c t U m i n u s {
u s i n g t y p e = Unit < U1 :: m - U2 :: m , U1 :: kg - U2 :: kg , U1 :: s - U2 :: s >;
};
t e m p l a t e<t y p e n a m e U1 , t y p e n a m e U2 >
u s i n g U n i t _ m i n u s = t y p e n a m e Uminus < U1 , U2 >:: t y p e ;
Example: Numbers with Units
Quantities
Now we can introduce a class in which the values are stored together with their units. Since the units are only used as a template parameter, this only affects the class type but does not require memory. In order to remain as flexible as possible, the data type is also a template parameter.
t e m p l a t e<t y p e n a m e U , t y p e n a m e V =double>
s t r u c t Q u a n t i t y { V val ;
e x p l i c i t c o n s t e x p r Q u a n t i t y ( V d ) : val { d } {}
t e m p l a t e<t y p e n a m e V2 >
c o n s t e x p r Q u a n t i t y ( Q u a n t i t y < U , V2 > d ) : val {s t a t i c _ c a s t< V >( d . val ) } {}
};
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 22 / 51
Example: Numbers with Units
Explicit Conversion
•
Constructors with one argument are used by C++ for automatic type conversion.
•
This isn’t always desired. In this example it shouldn’t be possible to add or subtract a number without unit to/from one with unit.
•
In C++11, constructors can be made
explicit.
•
In this case the constructor will only be used when it is explicitly called, for
example with
Quantity<M>(2.73).
Example: Numbers with Units
Quantities: Addition and Subtraction
We can now calculate with quantities. For addition and subtraction, the unit of the two operands and the result have to be the same. All data types which have an appropriate
operator+or
operator-can be used.
t e m p l a t e<t y p e n a m e U , t y p e n a m e V1 , t y p e n a m e V2 >
a u t o o p e r a t o r+( Q u a n t i t y < U , V1 > x , Q u a n t i t y < U , V2 >
y ) - > Q u a n t i t y < U , d e c l t y p e ( x . val + y . val ) >
{
r e t u r n Q u a n t i t y < U , d e c l t y p e ( x . val + y . val ) >( x . val + y . val ) ; }
t e m p l a t e<t y p e n a m e U , t y p e n a m e V1 , t y p e n a m e V2 >
a u t o o p e r a t o r-( Q u a n t i t y < U , V1 > x , Q u a n t i t y < U , V2 >
y ) - > Q u a n t i t y < U , d e c l t y p e ( x . val - y . val ) >
{
r e t u r n Q u a n t i t y < U , d e c l t y p e ( x . val - y . val ) >( x . val - y . val ) ; }
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 24 / 51
Example: Numbers with Units
Quantities: Multiplication and Division
During multiplication the units are added componentwise, while for division they are subtracted.
t e m p l a t e<t y p e n a m e U1 , t y p e n a m e U2 , t y p e n a m e V1 , t y p e n a m e V2 >
a u t o o p e r a t o r*( Q u a n t i t y < U1 , V1 > x , Q u a n t i t y < U2 , V2 >
y ) - > Q u a n t i t y < U n i t _ p l u s < U1 , U2 > , d e c l t y p e ( x . val * y . val ) >
{
r e t u r n
Q u a n t i t y < U n i t _ p l u s < U1 , U2 > , d e c l t y p e ( x . val * y . val ) >( x . val * y . val ) ; }
t e m p l a t e<t y p e n a m e U1 , t y p e n a m e U2 , t y p e n a m e V1 , t y p e n a m e V2 >
a u t o o p e r a t o r/( Q u a n t i t y < U1 , V1 > x , Q u a n t i t y < U2 , V2 >
y ) - > Q u a n t i t y < U n i t _ m i n u s < U1 , U2 > , d e c l t y p e ( x . val / y . val ) >
{
r e t u r n
Example: Numbers with Units
Quantities: Multiplication by Values without Unit
The unit remains the same when a quantity is multiplied by a value without unit.
t e m p l a t e<t y p e n a m e U ,t y p e n a m e V1 , t y p e n a m e V2 >
a u t o o p e r a t o r*( Q u a n t i t y < U , V1 > x , V2 y ) - > Q u a n t i t y < U , d e c l t y p e ( x . val * y ) >
{
r e t u r n Q u a n t i t y < U , d e c l t y p e ( x . val * y ) >( x . val * y ) ; }
t e m p l a t e<t y p e n a m e U ,t y p e n a m e V1 , t y p e n a m e V2 >
a u t o o p e r a t o r*( V1 y , Q u a n t i t y < U , V2 >
x ) - > Q u a n t i t y < U , d e c l t y p e ( x . val * y ) >
{
r e t u r n Q u a n t i t y < U , d e c l t y p e ( x . val * y ) >( x . val * y ) ; }
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 26 / 51
Example: Numbers with Units
Quantities: Division using Values without Unit
If a quantity is divided by a number without unit the same as for multiplication holds. If the quantity is the divisor, the signs of the components of the unit have to be switched.
t e m p l a t e<t y p e n a m e U ,t y p e n a m e V1 , t y p e n a m e V2 >
a u t o o p e r a t o r/( Q u a n t i t y < U , V1 > x , V2 y ) - > Q u a n t i t y < U , d e c l t y p e ( x . val / y ) >
{
r e t u r n Q u a n t i t y < U , d e c l t y p e ( x . val / y ) >( x . val / y ) ; }
t e m p l a t e<t y p e n a m e U ,t y p e n a m e V1 , t y p e n a m e V2 >
a u t o o p e r a t o r/( V1 y , Q u a n t i t y < U , V2 >
x ) - > Q u a n t i t y < U n i t _ n e g a t e < U > , d e c l t y p e ( y / x . val ) >
{
r e t u r n Q u a n t i t y < U n i t _ n e g a t e < U > , d e c l t y p e ( y / x . val ) >( y / x . val ) ;
Example: Numbers with Units
Custom Literals
It would be nice if it was possible to define more literals in addition to the builtin ones, e.g.
" Hi ! "s // string , not ‘ ‘ zero - t e r m i n a t e d a r r a y of c h a r ’ ’
1.2 i // i m a g i n a r y n u m b e r
1 2 3 . 4 5 6 7 8 9 1 2 3 4 df // d e c i m a l f l o a t i n g p o i n t ( IBM ) 1 0 1 0 1 0 1 1 1 0 0 0 1 0 1 b // b i n a r y n u m b e r
123 s // s e c o n d s
1 2 3 . 5 6 km // k i l o m e t e r s
1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 x // e x t e n d e d - p r e c i s i o n
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 28 / 51
Example: Numbers with Units
Custom Literals
This is possible in C++11, e.g. for complex numbers and strings (their suffix has to start with a
’_’, however):
c o n s t e x p r complex <double> o p e r a t o r" " _i (l o n g d o u b l e d ) //
i m a g i n a r y l i t e r a l {
r e t u r n {0 , d }; // r e t u r n s the a p p r o p r i a t e c o m p l e x n u m b e r }
std :: s t r i n g o p e r a t o r" " _s (c o n s t c h a r* p , s i z e _ t n ) //
std :: s t r i n g l i t e r a l {
r e t u r n s t r i n g ( p , n ) ; }
This can be used as follows:
t e m p l a t e<c l a s s T > v o i d f (c o n s t T &) ;
f (" H e l l o ") ; // h a n d s c o n s t c h a r * to f u n c t i o n
Example: Numbers with Units
Custom Literals
C strings may also be passed directly:
B i g n u m o p e r a t o r" " _x (c o n s t c h a r* p ) {
r e t u r n B i g n u m ( p ) ; }
v o i d f ( B i g n u m ) ;
f ( 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 _x ) ;
but not always:
std :: s t r i n g o p e r a t o r" " _S (c o n s t c h a r* p ) ; // t h i s d o e s n ’ t w o r k std :: s t r i n g b l u b = " one two "_S ; // E r r o r : no a p p l i c a b l e l i t e r a l
o p e r a t o r
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 30 / 51
Example: Numbers with Units
Raw String Literals
If one wants to use a backslash in a string, then it has to be written as
\\. This makes the string difficult to read, especially in the newly introduced regular expressions:
s t r i n g s = " \\ w \ \ \ \ \ \ w "; // H o p e f u l l y g e t t i n g t h i s e x a m p l e r i g h t ...
In a raw string literal, each character is simply written directly as such:
std :: s t r i n g s = R" (\ w \\\ w ) "; // I ’ m p r e t t y s u r e I got t h a t r i g h t std :: s t r i n g p a t h = R" ( c :\ P r o g r a m m e \ b l u b \ b l o b . exe ) ";
The first proposal for the introduction of raw string literals has been motivated by the following example:
" ( ’ ( ? : [ ^ \ \ \ \ ’ ] | \ \ \ \ . ) * ’ | \ " ( ? : [ ^ \ \ \ \ \ " ] | \ \ \ \ . ) * \ " ) | "
// Are the f i v e b a c k s l a s h e s c o r r e c t or not ?
Example: Numbers with Units
Raw String Literals
A raw string literal starts with
R"(and ends with
)".
R" ( "q u o t e d s t r i n g" ) " // the s t r i n g is " q u o t e d s t r i n g "
Should it happen that the combination
"(or
)"occurs in the string, then an arbitrary combination of characters can be inserted between the parenthesis and the quotation marks to make the delimiter unique:
R" * * * ( "q u o t e d s t r i n g c o n t a i n i n g the u s u a l t e r m i n a t o r (" ]) ") ***"
// the s t r i n g is "q u o t e d s t r i n g c o n t a i n i n g the u s u a l t e r m i n a t o r (" ]) "
Short version of the above regular expression:
" ’ ( [ ^ \ \ \ \ ’ ] | \ \ \ \ . ) * ’ | \ " ( [ ^ \ \ \ \ \ " ] | \ \ \ \ . ) *\" "
Equivalent raw string literal:
R" ( ’ ( [ ^ \ \ ’ ] | \ \ . ) * ’ | \ " ( [ ^ \ \ \ " ] | \ \ . ) * \ " ) "
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 32 / 51
Example: Numbers with Units
Regular Expressions
As in many other programming languages, regular expressions can also be used in C++11:
# include< regex >
# include< i o s t r e a m >
# include< string >
int m a i n () {
std :: r e g e x n a m e _ r e ( R" - -(([ a - zA - Z ]+) \ s +([ a - zA - Z ]+) ) - - ") ; std :: s t r i n g n a m e =" T o r s t e n W i l l ";
if( r e g e x _ m a t c h ( n a m e . b e g i n () , n a m e . end () , n a m e _ r e ) ) std :: c o u t < < " H e l l o " < < n a m e < < std :: e n d l ; e l s e
std :: c o u t < < " Who are you ? " < < std :: e n d l ; }
Example: Numbers with Units
String to Number and Number to String Conversion
# include< string >
# i n c l u d e" p r i n t . h "
int m a i n () {
std :: s t r i n g s V a l = " -2.47 3 . 1 4 1 5 1 e 3 0 0 42 3 7 6 8 5 7 6 8 9 4 0 3 0 xFF ";
s i z e _ t n e x t =0;
f l o a t f V a l = std :: s t o f ( sVal ,& n e x t ) ; s V a l = s V a l . s u b s t r ( n e x t ) ;
d o u b l e d V a l = std :: s t o d ( sVal ,& n e x t ) ; s V a l = s V a l . s u b s t r ( n e x t ) ;
l o n g d o u b l e l d V a l = std :: s t o l d ( sVal ,& n e x t ) ; s V a l = s V a l . s u b s t r ( n e x t ) ;
int i V a l = std :: s t o i ( sVal ,& n e x t ) ; s V a l = s V a l . s u b s t r ( n e x t ) ;
l o n g l V a l = std :: s t o l ( sVal ,& n e x t ) ; s V a l = s V a l . s u b s t r ( n e x t ) ;
u n s i g n e d l o n g u l V a l = std :: s t o u l ( sVal ,& next , 1 6 ) ; P r i n t f (" % d , % d , % d , % s , % g , % s \ n ", iVal , lVal , ulVal ,
std :: t o _ s t r i n g ( f V a l ) , dVal , std :: t o _ s t r i n g ( l d V a l ) ) ; }
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 34 / 51
Example: Numbers with Units
Literals for Quantities with Floating Point Numbers
Now we can define a large amount of personal literals. First for quantities which are using floating point numbers as data type:
c o n s t e x p r Q u a n t i t y < M ,l o n g double> o p e r a t o r" " _m (l o n g d o u b l e v a l u e ) {
r e t u r n Q u a n t i t y < M ,l o n g double> { v a l u e };
}
c o n s t e x p r Q u a n t i t y < Kg ,l o n g double> o p e r a t o r" " _kg (l o n g d o u b l e v a l u e )
{
r e t u r n Q u a n t i t y < Kg ,l o n g double> { v a l u e };
}
c o n s t e x p r Q u a n t i t y < S ,l o n g double> o p e r a t o r" " _s (l o n g d o u b l e v a l u e ) {
r e t u r n Q u a n t i t y < S ,l o n g double> { v a l u e };
}
Example: Numbers with Units
Literals for Quantities with Floating Point Numbers
c o n s t e x p r Q u a n t i t y < Kg ,l o n g double> o p e r a t o r" " _g (l o n g d o u b l e v a l u e ) {
r e t u r n Q u a n t i t y < Kg ,l o n g double> { v a l u e / 1 0 0 0 } ; }
c o n s t e x p r Q u a n t i t y < Kg ,l o n g double> o p e r a t o r" " _mg (l o n g d o u b l e v a l u e )
{
r e t u r n Q u a n t i t y < Kg ,l o n g double> { v a l u e / 1 0 0 0 0 0 0 } ; }
c o n s t e x p r Q u a n t i t y < S ,l o n g double> o p e r a t o r" " _ m i n (l o n g d o u b l e v a l u e )
{
r e t u r n Q u a n t i t y < S ,l o n g double> { 6 0 * v a l u e };
}
c o n s t e x p r Q u a n t i t y < S ,l o n g double> o p e r a t o r" " _h (l o n g d o u b l e v a l u e ) {
r e t u r n Q u a n t i t y < S ,l o n g double> { 3 6 0 0 * v a l u e };
}
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 36 / 51
Example: Numbers with Units
Literals for Quantities with Floating Point Numbers
c o n s t e x p r Q u a n t i t y < S ,l o n g double> o p e r a t o r" " _d (l o n g d o u b l e v a l u e ) {
r e t u r n Q u a n t i t y < S ,l o n g double> { 8 6 4 0 0 * v a l u e };
}
c o n s t e x p r Q u a n t i t y < S ,l o n g double> o p e r a t o r" " _ms (l o n g d o u b l e v a l u e ) {
r e t u r n Q u a n t i t y < S ,l o n g double> { v a l u e / 1 0 0 0 } ; }
c o n s t e x p r Q u a n t i t y < S ,l o n g double> o p e r a t o r" " _us (l o n g d o u b l e v a l u e ) {
r e t u r n Q u a n t i t y < S ,l o n g double> { v a l u e / 1 0 0 0 0 0 0 } ; }
c o n s t e x p r Q u a n t i t y < S ,l o n g double> o p e r a t o r" " _ns (l o n g d o u b l e v a l u e ) {
Example: Numbers with Units
Literals for Quantities with Integers
Then also for quantities that use integer data types:
c o n s t e x p r Q u a n t i t y < M ,u n s i g n e d l o n g long> o p e r a t o r" " _m (u n s i g n e d l o n g l o n g v a l u e )
{
r e t u r n Q u a n t i t y < M ,u n s i g n e d l o n g long> { v a l u e };
}
c o n s t e x p r Q u a n t i t y < Kg ,u n s i g n e d l o n g long> o p e r a t o r" " _kg (u n s i g n e d l o n g l o n g v a l u e )
{
r e t u r n Q u a n t i t y < Kg ,u n s i g n e d l o n g long> { v a l u e };
}
c o n s t e x p r Q u a n t i t y < S ,u n s i g n e d l o n g long> o p e r a t o r" " _s (u n s i g n e d l o n g l o n g v a l u e )
{
r e t u r n Q u a n t i t y < S ,u n s i g n e d l o n g long> { v a l u e };
}
And so on, as above for floating point numbers.
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 38 / 51
Example: Numbers with Units
Quantities: Squaring and Comparison
We also want to be able to square quantities and compare them.
t e m p l a t e<t y p e n a m e U , t y p e n a m e V >
Q u a n t i t y < U n i t _ p l u s < U , U > , V > s q u a r e ( Q u a n t i t y < U , V > x ) {
r e t u r n Q u a n t i t y < U n i t _ p l u s < U , U > , V >( x . val * x . val ) ; }
t e m p l a t e<t y p e n a m e U ,t y p e n a m e V >
b o o l o p e r a t o r==( Q u a n t i t y < U , V > x , Q u a n t i t y < U , V > y ) {
r e t u r n x . val == y . val ; }
t e m p l a t e<t y p e n a m e U ,t y p e n a m e V >
b o o l o p e r a t o r!=( Q u a n t i t y < U , V > x , Q u a n t i t y < U , V > y ) {
Example: Numbers with Units
Quantities: Larger and Smaller
Checks for inequality should even be possible for different data types when the unit matches:
}
t e m p l a t e<t y p e n a m e U ,t y p e n a m e V1 , t y p e n a m e V2 >
b o o l o p e r a t o r<( Q u a n t i t y < U , V1 > x , Q u a n t i t y < U , V2 > y ) {
r e t u r n x . val < y . val ; }
t e m p l a t e<t y p e n a m e U ,t y p e n a m e V1 , t y p e n a m e V2 >
b o o l o p e r a t o r>( Q u a n t i t y < U , V1 > x , Q u a n t i t y < U , V2 > y ) {
r e t u r n x . val > y . val ; }
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 40 / 51
Example: Numbers with Units
Quantities: Output
We would like to be able to display quantities with the correct units. This can be achieved with a simple function and an overloaded output operator:
std :: s t r i n g s u f f i x (int u , c o n s t c h a r* x ) {
std :: s t r i n g suf ; if ( u ) {
suf += x ;
if (1 < u ) suf += ’ 0 ’ + u ; if ( u <0) {
suf += ’ - ’;
suf += ’ 0 ’ - u ; }
}
r e t u r n suf ; }
t e m p l a t e<t y p e n a m e U , t y p e n a m e V >
Example: Numbers with Units
Quantities: Application Example
Now we can calculate with our quantities:
# i n c l u d e" u n i t s . h "
int m a i n () {
Q u a n t i t y < M ,double> x { 1 0 . 5 } ; Q u a n t i t y < S ,int> y { 2 } ;
Q u a n t i t y < MpS ,double> v = x / y ; v = 2* v ;
a u t o d i s t a n c e = 10 _m ;
Q u a n t i t y < S ,double> t i m e = 20 _s ; a u t o s p e e d = d i s t a n c e / t i m e ;
Q u a n t i t y < MpS2 ,double> a c c e l e r a t i o n = d i s t a n c e / s q u a r e ( t i m e ) ; std :: c o u t < < " S p e e d = " < < s p e e d < < " A c c e l e r a t i o n = " < <
a c c e l e r a t i o n < < std :: e n d l ; }
Output:
Velocity = 0.5ms-1 Acceleration = 0.025ms-2
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 42 / 51
Variadic Templates: C++ Printf
C++ Printf
The function
printf()has been a simple C function in C++, but in C++11 this isn’t necessarily the case any more. The application looks as follows:
# i n c l u d e" p r i n t . h "
int m a i n () {
c o n s t c h a r * pi = " pi ";
P r i n t f (" The v a l u e of % s is a b o u t % g ( u n l e s s you l i v e in % s ) .\ n ", pi , 3 . 1 4 1 5 9 , " I n d i a n a ") ;
c o n s t std :: s t r i n g n a m e =" S t e f a n ";
int age = 2 4 ; f l o a t g r a d e = 1 . 3 ; P r i n t f (
R" ( The s t u d e n t % s , % d y e a r s old , has r e c e i v e d the g r a d e % g . He is a m o n g the top 1 %%.
) ", name , age , g r a d e ) ; }
Variadic Templates: C++ Printf
Variadic Templates
The easiest case of
printf()is the one where no arguments other than the format string exist:
# include< i o s t r e a m >
# include< t y p e _ t r a i t s >
# include< s t d e x c e p t >
v o i d P r i n t f (c o n s t c h a r * s ) {
if ( s == n u l l p t r ) r e t u r n;
w h i l e (* s ) {
if (* s ==’ % ’ && *++ s !=’ % ’) // m a k e s u r e t h a t t h e r e a r e n ’ t any // a r g u m e n t s . %% is a n o r m a l % in a // f o r m a t s t r i n g
t h r o w std :: r u n t i m e _ e r r o r (" i n v a l i d f o r m a t : m i s s i n g a r g u m e n t s ") ;
std :: c o u t < < * s ++;
} }
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 44 / 51
Variadic Templates: C++ Printf
Variadic Templates
We now have to treat the case of
printf()with several arguments. This requires a variable number of template arguments:
t e m p l a t e<t y p e n a m e T , t y p e n a m e... Args > // n o t e the " . . . "
v o i d P r i n t f (c o n s t c h a r* s , T value , A r g s ... a r g s ) {
w h i l e ( s && * s ) {
if (* s ==’ % ’) { s w i t c h ( * + + s ) {
c a s e ’ % ’:
b r e a k; c a s e ’ s ’:
if (! I s _ C _ s t y l e _ s t r i n g < T >() && ! I s _ s t r i n g < T >() ) t h r o w std :: r u n t i m e _ e r r o r (" Bad P r i n t f () f o r m a t ") ; b r e a k;
c a s e ’ d ’:
Variadic Templates: C++ Printf
Variadic Templates
c a s e ’ g ’:
if (! std :: i s _ f l o a t i n g _ p o i n t < T >() )
t h r o w std :: r u n t i m e _ e r r o r (" Bad P r i n t f () f o r m a t ") ; b r e a k;
d e f a u l t:
t h r o w std :: r u n t i m e _ e r r o r (" U n k n o w n P r i n t f () f o r m a t ") ; }
std :: c o u t < < v a l u e ;
r e t u r n( P r i n t f (++ s , a r g s . . . ) ) ; // h e r e a g a i n n o t e the " . . . "
}
std :: c o u t < < * s ++;
}
t h r o w std :: r u n t i m e _ e r r o r (" E x t r a a r g u m e n t s p r o v i d e d to P r i n t f ") ; }
•
Using variadic templates, only the first element of the argument list is visible in each function call.
Tcan be a different type for each call. The remainder may then be passed to the function again.
•
The ellipses after
typename, after the template type in the argument list and after the corresponding variable name in the next function call are important.
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 46 / 51
Variadic Templates: C++ Printf
Variadic Templates
While the predicates
is_integral<T>()and
is_floating_point<T>are predefined, the predicates
Is_C_style_string<T>()and
Is_string<T>()have to be defined:
t e m p l a t e<t y p e n a m e T >
b o o l I s _ C _ s t y l e _ s t r i n g () {
r e t u r n std :: is_same < T ,c o n s t c h a r* >() || std :: is_same < T ,c h a r* >() ; }
t e m p l a t e<t y p e n a m e T >
b o o l I s _ s t r i n g () {
r e t u r n std :: is_same < T ,c o n s t std :: string >() ||
std :: is_same < T , std :: string >() ; }
Variadic Templates: C++ Printf
Tuples
Another use of variadic templates are tuples, a generalization of pairs to any number of components.
•
Their type can be generated automatically with the help of
autoand
std::make_tuple
.
•
The auxiliary function
std::get<i>returns the i-th component of a tuple.
# include< tuple >
# include< string >
int m a i n () {
std :: tuple < std :: string ,int> t2 (" M u e l l e r ",123) ;
a u t o t = std :: m a k e _ t u p l e ( std :: s t r i n g (" M a y e r ") ,10 , 1 . 2 3 ) ; // t is of t y p e tuple < string , int , double >
std :: s t r i n g s = std :: get <0 >( t ) ; int x = std :: get <1 >( t ) ;
d o u b l e d = std :: get <2 >( t ) ; }
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 48 / 51
Variadic Templates: C++ Printf
External Templates
In projects that consist of many individual files templates are often instantiated in several places. This does not happen if they are declared with the keyword
extern, for example in the header file extern.h:
# include< vector >
e x t e r n t e m p l a t e c l a s s std :: vector <int>;
int b l u b ( std :: vector <int>& a ) {
r e t u r n a [ 0 ] ; }
Variadic Templates: C++ Printf
Externe Templates
and also not in the main program extern.cc
# i n c l u d e" e x t e r n . h "
e x t e r n t e m p l a t e c l a s s std :: vector <int>;
int m a i n () {
std :: vector <int> x (5 ,5.) ; int y = b l u b ( x ) ;
}
Ole Klein (IWR) Object-Oriented Programming 30. Juni 2015 50 / 51
Variadic Templates: C++ Printf
Externe Templates
The actual instantiation is done explicitly in a well-defined location, in this example in the file extern2.cc:
# include< vector >
t e m p l a t e c l a s s std :: vector <int>;