Skip to main content
GameDev.net gamedev.net
🔒 Locked

problems with template metaprogramming

Started by exorcist_bob Aug 20, 2006 at 9:41 AM 13 replies 5.6k views
Original Post
exorcist_bob
exorcist_bob
Hello, I am having some problems with template metaprogramming in vc++. I am trying to get the following to compile:

#include <iostream>
#include <tchar.h>

#pragma inline_depth(255)
#pragma inline_recursion(on)

using namespace std;

template<double R> struct Sine
{
	enum { MaxTerms = 10 };
	static inline double sin()
	{
		return R * Series<R, 0, MaxTerms>::val();
	}
};

template <double R, int I, int MaxTerms> struct Series
{
	enum
	{
		Continue = I + 1 != MaxTerms,
		NxtI = ( I + 1 ) * Continue,
		NxtMaxTerms = MaxTerms * Continue
	};

	static inline double val()
	{
		return 1 - R*R/(2.0 * I + 2.0)/(2.0 * I + 3.0)*Series<R*Continue, NxtI, NxtMaxTerms>::val();
	};
};

template <> struct Series<0.0, 0, 0>
{
	static inline double val() { return 1.0; };
};

#define SineVal( r ) Sine<r>::sin()


int _tmain(int argc, _TCHAR* argv[])
{
	char ch;
	double val = 5.0;
	cout << SineVal(val) << endl;
	cin >> ch;
	return 0;
}

But i'm getting the following errors:


error C2993: 'double' : illegal type for non-type template parameter 'R'
error C2993: 'double' : illegal type for non-type template parameter 'R'


PS This came straight from Game Programming Gems, and was compiled using visual c++ 2005 express edition. Thanks for any assistance, exorcist_bob
deffer
deffer
1. template struct Sine { ... };
I don't believe any current compiler is supporting non-integral values as template parameters. That is: int, char, bool - yes, but float, double - nope.
EDIT: See Section 14.1 4 of The standard.
EDIT2: Section 14.1 7 of The standard explicitly disallows floating point values as template parameters.

2. #define SineVal( r ) Sine::sin()
In expression: SineVal(val), val has to be a compile-time expression. Like: enum { val=5 }, or #define val 5. If 1. wasn't in the way, you could get away with SineVal(5.0).
SiCrane
SiCrane
MSVC 6 will allow for double and float non-type template arguments. For standard C++, you can use references to const double as non-type template arguments. It gets pretty ugly in instantiation for it to work, but compilers will generally expand the expressions out at compile time.
Limitz
Limitz
I dont really get what you are doing here,

Template parameters are not allowed to have a type (except class) is my understanding. This doesn't look like good C++ programming to me. Anyone?

Greetings.
exorcist_bob
exorcist_bob
I can't make those const, since I'm returning values. But if I make them references, I get these errors:

1>codetester.cpp(34) : error C2440: 'specialization' : cannot convert from 'double' to 'double &'
1>codetester.cpp(45) : error C2440: 'specialization' : cannot convert from 'double' to 'double &'
1>codetester.cpp(45) : error C2440: 'specialization' : cannot convert from 'int' to 'double &'
1>codetester.cpp(45) : see reference to class template instantiation 'Sine' being compiled
1> with
1> [
1> R=0
1> ]
1>codetester.cpp(45) : error C2973: 'Sine' : invalid template argument 'int'
1>(16) : see declaration of 'Sine'

Thanks for the help!
exorcist_bob
SiCrane
SiCrane
Quote:
Original post by Limitz
Template parameters are not allowed to have a type (except class) is my understanding. This doesn't look like good C++ programming to me. Anyone?


No, C++ allows several types of non-type template arguments including, roughly in order of most frequently encountered to least frequently encountered: constant integral expressions, enumerations, objects or functions with external linkage, addresses of objects and functions and pointer to members.
EkEk
EkEk
i think the template should recieve const variable.
so I think this is wrong.
	double val = 5.0;   // should be const double val = 5.0;	cout << SineVal(val) << endl;

as far as i remember, template is generate on compile time. and if you don't use const variable, then it will compile error.
I think metaprogramming is not used for run time calculation, instead it is used for defining constants.
exorcist_bob
exorcist_bob
Quote:
Original post by EkEk
i think the template should recieve const variable.
so I think this is wrong.
*** Source Snippet Removed ***
as far as i remember, template is generate on compile time. and if you don't use const variable, then it will compile error.
I think metaprogramming is not used for run time calculation, instead it is used for defining constants.


Just got done with that, thanks! However, I'm getting the same errors.

Here's the updated source:

#include <iostream>#include <tchar.h>#include <cmath>#pragma inline_depth(255)#pragma inline_recursion(on)using namespace std;template<double& R> struct Sine{	enum { MaxTerms = 10 };	static inline double sin()	{		return R * Series<R, 0, MaxTerms>::val();	}};template <double& R, int I, int MaxTerms> struct Series{	enum	{		Continue = I + 1 != MaxTerms,		NxtI = ( I + 1 ) * Continue,		NxtMaxTerms = MaxTerms * Continue	};	static inline double val()	{		return 1 - R*R/(2.0 * I + 2.0)/(2.0 * I + 3.0)*Series<R*Continue, NxtI, NxtMaxTerms>::val();	};};template <> struct Series<0.0, 0, 0>{	static inline double val() { return 1.0; };};#define SineVal( r ) Sine<r>::sin()int _tmain(int argc, _TCHAR* argv[]){	char ch;	cout << Sine<5.0>::sin() << endl;	cout << sin(5.0) << endl;	cin >> ch;	return 0;}


1>------ Build started: Project: CodeTester, Configuration: Debug Win32 ------1>Compiling...1>CodeTester.cpp1>f:\cross-platform object-oriented development engine\codetester\codetester.cpp(35) : error C2440: 'specialization' : cannot convert from 'double' to 'double &'1>f:\cross-platform object-oriented development engine\codetester\codetester.cpp(44) : error C2440: 'specialization' : cannot convert from 'double' to 'double &'1>f:\cross-platform object-oriented development engine\codetester\codetester.cpp(44) : error C2440: 'specialization' : cannot convert from 'int' to 'double &'1>        f:\cross-platform object-oriented development engine\codetester\codetester.cpp(44) : see reference to class template instantiation 'Sine<R>' being compiled1>        with1>        [1>            R=01>        ]1>f:\cross-platform object-oriented development engine\codetester\codetester.cpp(44) : error C2973: 'Sine' : invalid template argument 'int'1>        f:\cross-platform object-oriented development engine\codetester\codetester.cpp(17) : see declaration of 'Sine'1>Build log was saved at "file://f:\Cross-Platform Object-Oriented Development Engine\CodeTester\Debug\BuildLog.htm"1>CodeTester - 4 error(s), 0 warning(s)========== Build: 0 succeeded, 1 failed, 13 up-to-date, 0 skipped ==========


Thanks,
exorcist_bob
SiCrane
SiCrane
Reference non-type template paramters must be bound to variables with external linkage. 5.0 is a double literal, which is not a variable with external linkage.
double five = 5.0;int _tmain(int argc, _TCHAR* argv[]){  // blah blah blah  cout << Sine<five>::sin() << endl;  // blah blah blah
Conner McCloud
Conner McCloud
I'm confused as to why you aren't just writing a function.

CM
deffer
deffer
A few surprises:

1.
template
struct Series

Had to make it a pointer or vc2005ee was complaining that it doesn't like references in template specializations. Strange days...

2.
extern const double dd;
const double dd = 5;

Without the first line the compiler wasn't convinced that the "dd" variable really had external linkage. Strange days...


Other than that, I made it to work. And that's the real twist for me.
SiCrane
SiCrane
const variables have implicit internal linkage, thus you need the extern to give it external linkage.
nmi
nmi
Why not use the compiler's ability to evaluate constant expressions in conjunction with inlining ? For instance this program:
#include <stdio.h>static inlinedouble my_sin(double d, int n = 10){    double result = 1.0;    const double dd = d * d;    for (int i = n; i > 0; i--)    {        result *= dd;        result /= 2*i;        result /= (2*i + 1);        result = 1.0 - result;    }    result *= d;    return result;}double foo(){    return my_sin(0.5, 4);}int main(){    printf("%g\n", foo());}

compiled with gcc 4.1.1 via:
g++ -O3 -fomit-frame-pointer sin.cc -o sin

results in this code:
08048450 <foo()>: 8048450:       dd 05 a8 85 04 08       fldl   0x80485a8 8048456:       c3                      ret08048460 <main>: 8048460:       8d 4c 24 04             lea    0x4(%esp),%ecx 8048464:       83 e4 f0                and    $0xfffffff0,%esp 8048467:       ff 71 fc                pushl  0xfffffffc(%ecx) 804846a:       ba 58 61 4e 74          mov    $0x744e6158,%edx 804846f:       b8 e8 ae de 3f          mov    $0x3fdeaee8,%eax 8048474:       51                      push   %ecx 8048475:       83 ec 18                sub    $0x18,%esp 8048478:       89 54 24 04             mov    %edx,0x4(%esp) 804847c:       89 44 24 08             mov    %eax,0x8(%esp) 8048480:       c7 04 24 b0 85 04 08    movl   $0x80485b0,(%esp) 8048487:       e8 ec fe ff ff          call   8048378 <printf@plt> 804848c:       83 c4 18                add    $0x18,%esp 804848f:       31 c0                   xor    %eax,%eax 8048491:       59                      pop    %ecx 8048492:       8d 61 fc                lea    0xfffffffc(%ecx),%esp 8048495:       c3                      ret


The values 0x744e6158 and 0x3fdeaee8 are the two dwords encoding the double of the result, which is 0.479426.

And this sine function may work as well with input values that are not constant. (If this is not desired you may put const in front of those parameters.)
Zahlman
Zahlman
I assume the allure of TMP is that you can (by my understanding) force a compliant compiler to do your optimization regardless of what the compiler optimizer wants to do.

Either that, or it's some form of masochism :)
EkEk
EkEk
i just read msdn for vs2005.
it seems, now they not allowing floating type for template parameter.

so metaprogramming code in game programming gems not valid anymore on vs2005

Read this for reference

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.