Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Template M.P. -- why won't this work?

3 views
Skip to first unread message

Carlos Moreno

unread,
Sep 5, 2005, 7:10:50 AM9/5/05
to

I'm struggling with this meta-loop I'm trying to do (basically,
I'm trying to write a "simple" example of a loop unrolled by meta-
programming).

Instead of the typical approach of going down from an int N all
the way to 1 and provide the specialized version for N=1, I want
a loop that goes from N to M (where N and M are arbitrary integer
values -- known at compile-time, of course, and with M > N)

So, I create a wrapper class that uses N and M as non-type params.
But it won't compile. Here's the code (I'm omiting the #include
and the using namespace):

template <int N, int M>
class metaloop
{
template <int X, typename T>
struct pass
{
static void go ()
{
cout << X << '\t' << X*X << endl;
metaloop<N,M>::pass<X+1,T>::go();
}
};

template <typename T>
struct pass<M,T>
{
static void go ()
{}
// M is excluded (following the usual semi-open
// loops -- as in for (i = N; i < M ... )
};

public:
static void go ()
{
pass<N,int>::go();
}
};

int main()
{
metaloop<2,10>::go();

return 0;
}


The compiler complains at the point of pass<501,int>. It fails to grab
the specialization pass<M,T>, which in this case would be pass<10,int>,
and instead, it takes the same generic one, which invokes pass<11,int>,
which invokes pass<12,int>.... At 501, the compiler apparently reaches
its recursion limit and reports the error.

Why is that? What am I missing?


Thanks,

Carlos
--

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Gleb Alexeev

unread,
Sep 6, 2005, 6:48:27 AM9/6/05
to
I don't have direct answer to your question but IMHO you seem to
overcomplicate things given your task.
Maybe solution below is what you need?

template <int N, int M>
class metaloop {

public:
static void go () {

std::cout << N << std::endl;
metaloop<N+1, M>::go();
}
};
template <int M>
class metaloop<M,M> {


public:
static void go () {
}

};

Frank Chang

unread,
Sep 6, 2005, 6:56:42 AM9/6/05
to
Carlos, I made the following changes. This should run OK:

template <int N, int M>
class metaloop
{

public:


template <int X, typename T>
struct pass
{
static void go ()
{
cout << X << '\t' << X*X << endl;

metaloop<N+1,M>::pass<X+1,T>::go();

}
};

template <typename T>
struct pass<M,T>
{
static void go ()
{}
// M is excluded (following the usual semi-open
// loops -- as in for (i = N; i < M ... )
};

static void go ()
{
pass<N,int>::go();
}
};

// here I hard code a explicit template specialization
// to stop the infinite recursion you are experiencing.
// It must use hard-coded integers

template<>
class metaloop<10, 10> {
public:

template <int X, typename T>
struct pass
{
static void go ()
{

cout << "end of recursion" << endl;

}
};
};

Thank you.

Greg Herlihy

unread,
Sep 6, 2005, 6:54:25 AM9/6/05
to

It is not possible to specialize a class template nested inside a class
template that is itself not specialized. Furthermore the nested class
template pass is declared private which will make it inaccessible from
the other instantiations of metaloop used in the loop.

When writing template meta-programs, always strive for simplicity
(don't laugh). In this case, the nested template only complicates the
implementation of the recursive go routine. I would suggest using just
one class template to implement the metaloop:

using std::cout;

template <int Counter, int Limit>
class MetaLoop
{
public:
static void Go()
{
cout << Counter << '\t' << Counter*Counter << std::endl;

MetaLoop<Counter+1, Limit >::Go();
}
};

template <int Limit>
class MetaLoop<Limit, Limit>
{
public:
static void Go()
{
}
};

int main()
{
MetaLoop<2, 10>::Go();

return 0;
}

Greg

Carl Barron

unread,
Sep 6, 2005, 7:06:13 AM9/6/05
to
Carlos Moreno <moreno_at_mo...@xx.xxx> wrote:

inner classes as template params maybe. but the easiest solution
is to remove the looping from pass and let it handle the body of the
loop then a specialsation of the looping template is easy. Here is
one solution:

#include <iostream>

template <int X,typename T>


struct pass
{
static void go()

{ // just do one item, looping below.
std::cout << X*X << '\n';
}
};

template <int Start,int Fini,typename T>
struct do_loop
{
static void go()
{
pass<Start,T>::go();
do_loop<Start+1,Fini,T>::go();
}
};

template <int Start,typename T>
struct do_loop<Start,Start,T>
{
static void go()
{
pass<Start,T>::go();
}
};

int main()
{
do_loop<4,10,int>::go();

Marc Mutz

unread,
Sep 6, 2005, 7:05:52 AM9/6/05
to
Carlos Moreno wrote:

> metaloop<N,M>::pass<X+1,T>::go();

Not related to your OQ, but for gcc 4.0, I need to write
this as
typedef
typename metaloop<N,M>::template pass<X+1,T> next_pass;
next_pass::go();

Marc

Carlos Moreno

unread,
Sep 6, 2005, 12:14:10 PM9/6/05
to
Greg Herlihy wrote:

> It is not possible to specialize a class template nested inside a class
> template that is itself not specialized. Furthermore the nested class
> template pass is declared private which will make it inaccessible from
> the other instantiations of metaloop used in the loop.

Oh, d'oh!! This last detail was an oversight -- I was thinking in
terms of the outer class, and saw it as it was that outer class the
one that had to access the other instantiations... (I think the
compiler would have recognized that the class was there and would
have told me that it was private -- if there wasn't the other problem,
that is)

> When writing template meta-programs, always strive for simplicity
> (don't laugh).

Don't worry. I see that the irony is not lost on you :-)

> In this case, the nested template only complicates the
> implementation of the recursive go routine.

The responsibility of the outer class was to create the specialized
version for the upper limit, so that I didn't have to do it explicitly
(that would completely defy the purpose of the exercise I was doing).

> template <int Limit>
> class MetaLoop<Limit, Limit>

Neat trick!! I had not seen a way to "automatically" specialize the
upper limit -- the first solution that came to mind was obviously
specializing metaloop<10>, but then what's the point of writing
such thing, if I need to re-write the whole thing each time that
I want to use it? That's why the next solution that I could think
of involved the nested classes, to have one "middle-man" automate
that part for me.

Thanks for the trick!

Carlos
--

graeme prentice

unread,
Sep 7, 2005, 10:01:35 AM9/7/05
to
On 6 Sep 2005 06:54:25 -0400, Greg Herlihy wrote:

>
>It is not possible to specialize a class template nested inside a class
>template that is itself not specialized.

This applies only to explicit specializations. Carlos's code uses a
partial specialization - which is perfectly legal. See 14.5.4.3/2 for
an example.

FWIW - VC7.1 locks up compiling this code, consuming 98% CPU and
increasing memory, requiring task manager to kill it.

Graeme

Frank Chang

unread,
Sep 7, 2005, 1:14:27 PM9/7/05
to
Graeme, Using the following template class in MSVC7.1, It compiles it
less than a second , VC7.1 does not lock up and does not requires
increasing memory. Is this the template class you are using? Thank you.

template <int N, int M>
class metaloop
{

public:


template <int X, typename T>
struct pass
{
static void go ()
{
cout << X << '\t' << X*X << endl;

metaloop<N+1,M>::pass<X+1,T>::go();
}
};

template <typename T>
struct pass<M,T>
{
static void go ()
{}
// M is excluded (following the usual semi-open
// loops -- as in for (i = N; i < M ... )
};


public:
static void go ()
{
pass<N,int>::go();
}
};

template<int M>


class metaloop<M, M> {
public:

template <int X, typename T>
struct pass
{
static void go ()
{

cout << "end of recursion" << endl;
}
};
}


int _tmain(int argc, _TCHAR* argv[])
{
metaloop<2, 1000>::go();

graeme prentice

unread,
Sep 8, 2005, 5:16:53 AM9/8/05
to
On 7 Sep 2005 13:14:27 -0400, Frank Chang wrote:

>Graeme, Using the following template class in MSVC7.1, It compiles it
>less than a second , VC7.1 does not lock up and does not requires
>increasing memory. Is this the template class you are using? Thank you.


No, it locks up using Carlos's code. Your code has syntax errors but if
I correct them, it compiles without locking up. If I change the
following line back to what Carlos had, it locks up.

// no lock up


metaloop<N+1,M>::pass<X+1,T>::go();

// locks up VC7.1


metaloop<N,M>::pass<X+1,T>::go();

Graeme

Frank Chang

unread,
Sep 8, 2005, 10:09:23 AM9/8/05
to
Graeme, I believe you are stating that :

metaloop<N,M>::pass<X+1,T>::go­(); locks up VC7.1

This is because of infinite recursion.I experienced the same problem.

But metaloop<N+1,M>::pass<X+1,T>::­go();
does not lock , because we advance N -> N + 1 , therefore eventually
N=M and the recursion stops because the compiler sees the explicit
template specialization : template<int M>
class metaloop<M, M>

Please tell me what the compiler errors were and how you fixed
them. Thank you.

Graeme Prentice

unread,
Sep 9, 2005, 8:34:26 PM9/9/05
to
On 8 Sep 2005 10:09:23 -0400, Frank Chang wrote:

>Graeme, I believe you are stating that :
>
> metaloop<N,M>::pass<X+1,T>::go­(); locks up VC7.1

yep, that's what I said, (except for the mysterious minus sign)


>
>This is because of infinite recursion.I experienced the same problem.

No it's not. It's because of a bug in VC7.1. There's no infinite
recursion in Carlos's code as long as N is less than M (and this could
be protected with the help of boost-static-assert). The nested classes
could be a bit expensive memory-wise though, depending on the compiler's
optimisation skills.


>
>But metaloop<N+1,M>::pass<X+1,T>::­go();
> does not lock , because we advance N -> N + 1 , therefore eventually
>N=M and the recursion stops because the compiler sees the explicit
>template specialization : template<int M>
> class metaloop<M, M>
>
> Please tell me what the compiler errors were and how you fixed
>them. Thank you.

IIRC, you lost a semicolon, didn't include <iostream> and <ostream>, and
accessed members of namespace std that weren't visible. (Also _tmain
and _TCHAR are non standard).

Graeme

Frank Chang

unread,
Sep 12, 2005, 10:52:00 AM9/12/05
to
Graeme, Sorry for the syntax errors from the code posted my email. I
compiled and tested everything and got no VC7.1 errors or warnings and
it ran fine. I admit my cut and paste ignored include files and using
namespace std.


// Carlos code which locks up VC7.1
// metaloop<N,M>::pass<X+1,T>::go();

I agree infinite recursion may be not be the correct semantics.
Maybe I should say infinite loop, As the template instantion
progresses, N never changes and each succesive template instantiation
uses the same N, Therefore N never reaches M and an "infinite loop"
occurs. I checked this out with my VC7.1 debugger. But when I use:

// no lock up if change Carlos code to
// metaloop<N+1,M>::pass<X+1,T>::go();

As the template instantiation progresses , N -> N+1 ->N+2
...........->M, so no infinite loop occurs. Thank you.

0 new messages