Template metaprogramming

  • Thread starter Frederick Gotham
  • Start date
F

Frederick Gotham

I'm trying to get a feel for template metaprogramming, and so I'm trying
to write a compile-time "Raise number to positive integer" algorithm. So
far, I have the following:

template<class T, T base, unsigned exp>
struct IntPosPow {

static T const value =
base * IntPosPow<T,base,exp-1>::value;
};

template<class T, T base>
struct IntPosPow<T,base,0> {

static T const value = 1;

};


#include <iostream>

int main()
{
std::cout <<

(unsigned long)IntPosPow<unsigned long,2,0>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,1>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,2>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,3>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,4>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,5>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,6>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,7>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,8>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,9>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,10>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,11>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,12>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,13>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,14>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,15>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,16>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,17>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,18>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,19>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,20>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,21>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,22>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,23>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,24>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,25>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,26>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,27>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,28>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,29>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,30>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,31>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,32>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,33>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,34>::value << '\n' <<
(unsigned long)IntPosPow<unsigned long,2,35>::value << '\n';
}


Is there any way I can have:

template<class T, T base, T exp> struct IntPosPow {


instead of:

template<class T, T base, unsigned exp> struct IntPosPow {


?
 
I

Ian Collins

Frederick said:
I'm trying to get a feel for template metaprogramming, and so I'm trying
to write a compile-time "Raise number to positive integer" algorithm. So
far, I have the following:

template<class T, T base, unsigned exp>
struct IntPosPow {

static T const value =
base * IntPosPow<T,base,exp-1>::value;
};

template<class T, T base>
struct IntPosPow<T,base,0> {

static T const value = 1;

};


#include <iostream>

int main()
{
std::cout <<

(unsigned long)IntPosPow<unsigned long,2,0>::value << '\n' <<

Why the cast? IntPosPow said:
Is there any way I can have:

template<class T, T base, T exp> struct IntPosPow {


instead of:

template<class T, T base, unsigned exp> struct IntPosPow {
I don't think so. The specialised template won't match.

Considering your task is to "Raise number to positive integer", why
would you want to use anything else?
 
G

gottlobfrege

Ian said:
I don't think so. The specialised template won't match.

Don't specialize, use ?:

template<class T, T base, T exp>
struct IntPosPow {

static T const value = exp ? base * IntPosPow<T,base,exp-1>::value
: base;
};


or something like that.

Tony
 
I

Ian Collins

Don't specialize, use ?:

template<class T, T base, T exp>
struct IntPosPow {

static T const value = exp ? base * IntPosPow<T,base,exp-1>::value
: base;
};
A conditional isn't a compile time expression, or am I missing something?
 
L

Luke Meyers

Ian said:
A conditional isn't a compile time expression, or am I missing something?

You are missing something. The ternary operator can be used to produce
compile-time constants, if its operands are compile-time constants.
Which makes sense, because all the information is available at
compilation time, in every case.

Luke
 
I

Ian Collins

Luke said:
You are missing something. The ternary operator can be used to produce
compile-time constants, if its operands are compile-time constants.
Which makes sense, because all the information is available at
compilation time, in every case.
Good point, I hadn't thought of using the operator in metaprogramming,
I'd always used specialisation.
 
F

Frederick Gotham

posted:
template<class T, T base, T exp>
struct IntPosPow {

static T const value = exp ? base * IntPosPow<T,base,exp-1>::value
: base;
};


Won't work.

When the compiler makes a specialisation where "exp" is 0, it has to make
an another specialisation where "exp" is 0, and another "exp" where
"exp" is 0. It's exactly like:


struct Arb {

enum { base = 3 };
enum { exp = 7 };

static unsigned const value = exp ? base * Arb::value : base;
};


int main()
{
Arb::value;
}


The programmer's explicit specialisation is necessary.
 
G

gottlobfrege

Frederick said:
posted:



Won't work.

When the compiler makes a specialisation where "exp" is 0, it has to make
an another specialisation where "exp" is 0, and another "exp" where
"exp" is 0. It's exactly like:

yeah, when I actually tried it, it crashed the compiler (MSVC 8.0).
The programmer's explicit specialisation is necessary.

Right, but we can still do that without forcing a type, by converting
the T exp into an extra bool param, then specializing on that:


template <typename T, T base, T exp, bool b = !!exp>
struct IntPosPow
{
static T const value = base * IntPosPow<T , base, exp-1,
!!exp>::value;
};

template <typename T, T base, T exp>
struct IntPosPow<T, base, exp, false>
{
static T const value = 1;
};

I think that one even compiles.

- Tony
 
G

gottlobfrege

yeah, when I actually tried it, it crashed the compiler (MSVC 8.0).

Right, but we can still do that without forcing a type, by converting
the T exp into an extra bool param, then specializing on that:


template <typename T, T base, T exp, bool b = !!exp>
struct IntPosPow
{
static T const value = base * IntPosPow<T , base, exp-1,
!!exp>::value;
};

screwed up again (thought I should actually run it and see what I got).
I had that extra !!exp in there when I was trying things out. It
should be just like your original:

template <typename T, T base, T exp, bool b = !!exp>
struct IntPosPow
{
static T const value = base * IntPosPow<T , base, exp-1>::value;
};

Tony
 
F

Frederick Gotham

Tony posted:


Here's the complete working example. (Note that the double inversion
operators were redundant).


template <typename T, T base, T exp, bool b = exp>
struct IntPosPow
{
static T const value = base * IntPosPow<T,base,exp-1>::value;
};

template <typename T, T base, T exp>
struct IntPosPow<T,base,exp,false>
{
static T const value = 1;
};

#include <iostream>

int main()
{
std::cout << IntPosPow<unsigned,2,13>::value;
}
 
G

gottlobfrege

Frederick said:
Tony posted:



Here's the complete working example. (Note that the double inversion
operators were redundant).

Most compilers will give you a warning when you convert int (or
whatever T is) to bool ("potential loss of data" or similar). Even an
explicit (bool) cast doesn't usually quiet the compiler. But !! does.
I suppose it depends on the compiler and warning levels, but I've seen
it enough to just make it a habit. It also makes the intent more
explicit, like saying 'I meant to do that' - ie I really did want to
convert this int to a bool. For people that haven't seen !! I guess it
isn't very communicative, but the idiom is fairly well know, and by
emplying it I hope to make it more well known...



Tony
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,174
Latest member
BlissKetoACV
Top