Sign in to follow this  

problems with template metaprogramming

This topic is 4136 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
1. template<double R> 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<r>::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).

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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<R>' 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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.cpp
1>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 compiled
1> with
1> [
1> R=0
1> ]
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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
A few surprises:

1.
template <const double* R>
struct Series<R, 0, 0>

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.

Share this post


Link to post
Share on other sites
Why not use the compiler's ability to evaluate constant expressions in conjunction with inlining ? For instance this program:

#include <stdio.h>

static inline
double 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 ret

08048460 <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.)

Share this post


Link to post
Share on other sites
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 :)

Share this post


Link to post
Share on other sites

This topic is 4136 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this