Topic: recursive function templates
Author: gjditchfield@watmsg.waterloo.edu (Glen Ditchfield)
Date: Wed, 11 Mar 1992 21:22:07 GMT Raw View
This program is inspired by an example in Donahue & Demers' "Data Types are
Values", in TOPLAS v. 7 #3.
----------------------------------------
template<class T> void f(T t_p, long l) {
struct s { T t_f } temp = {t_p};
if (l > 0)
f(temp, l - 1);
}
extern long random();
void main() {
f(3.14, random());
}
----------------------------------------
The template facility seems to have been designed to allow implemen-
tation by macro expansion, but that looks hopeless here. When a template
function is generated from f for some type T1, another instance must
immediately be generated for the type "struct containing T1", and there is
no static way to put a useful upper bound on the number of generations
needed. I don't see anything in the ARM that makes constructs like f
illegal. What have I missed?
Glen Ditchfield gjditchfield@violet.uwaterloo.ca Office: DC 2517
Dept. of Computer Science, U of Waterloo, Waterloo, Ontario, Canada, N2L 3G1
Get a life -- before it gets you!
Author: jamshid@ut-emx.uucp (Jamshid Afshar)
Date: 16 Mar 92 10:40:09 GMT Raw View
In article <1992Mar11.212207.20275@watmath.waterloo.edu>
gjditchfield@watmsg.waterloo.edu (Glen Ditchfield) writes:
>...
>template<class T> void f(T t_p, long l) {
> struct s { T t_f } temp = {t_p};
> if (l > 0)
> f(temp, l - 1);
Cool!
>}
>extern long random();
>void main() {
> f(3.14, random());
>}
>... and there is
>no static way to put a useful upper bound on the number of generations
>needed.
You're right, in your example there isn't a way to make it stop
instantiating template functions. You can stop template expansion by
`specializing', but you can't specialize for local types. Try
something like like the following instead:
//---begin
#include <iostream.h>
template<class M> struct S {
M m;
S(const M& a_m) : m(a_m) {}
};
template<class T> void f(T t, long l) {
S<T> tmp(t); // BC++ erroneously(?) broke on `S<T> tmp = { t };'
cout << &t << '\t' << l << '\t' << &tmp << endl;
if (l>0)
f(tmp, l-1);
}
// a specialization of `f()'
void f(S< S< S< S< S<double> > > > > t, long l) {
cout << &t << '\t' << l << '\t' << "STOP" << endl;
}
main() {
double d = 3.141569;
f(d, 5L); // go from 5 to 0
}
//---end
>I don't see anything in the ARM that makes constructs like f illegal.
I also can't find anything explicitly making it illegal. Another
situation that could make a C++ compiler go into an infinite loop is:
class Test {
public:
// or instead loop through another class defining `Test& operator->()'
Test& operator->() { return *this; }
int i;
};
main() { Test t; t->i = 0; return 0; }
I don't know if ANSI will, or even how exactly they would, specify
these kinds of things as having undefined results. Would compilers be
required to diagnose them (like ANSI C requires for some constructs),
or could the compiler simply hang (ie, undefined compile-time
results)? Is there anything analogous to this in ANSI C?
Btw, the appended has probably already been discovered by countless
others, but I think it's really neat and it helped me find a couple of
bizarre Borland C++ 3.0 bugs, so try it with your compilers.
Jamshid Afshar
jamshid@emx.utexas.edu
//---begin fact.cpp ---
// `Factorial<n>::compute()' is a cute way to compute at compile-time
// the factorial of a constant long `n'.
// Btw, compute() must be member func since template function parameters
// can only be types, not expressions. Lippman's says removing this
// restriction is a likely ANSI C++ proposal, but the outcome is not clear.
#include <iostream.h>
template<unsigned long N> struct Factorial {
static unsigned long compute() { return N*Factorial<N-1>::compute(); }
};
struct Factorial<0UL> { // BC++ requires `L'
static unsigned long compute() { return 1; }
};
// I think you should be able to just define the following instead
// of the entire class, but Borland C++ 3.0 run out of memory.
// inline unsigned long Factorial<0UL>::compute() { return 1; }
main() {
// BC++ starts freaking out sometimes if J>10
const unsigned long J = 10; // must be compile-time constant, of course
unsigned long f = 1, i=J;
while (i>0) f*=i--;
cout << "factorial(" << J << ") is " << f << endl;
cout << J << "! is " << Factorial<J>::compute() << endl;
return 0;
}
//---end fact.cpp ---