Skip to main content
gamedev.net
🔒 Locked

Template parameter problem (C++)

Started by Endar 14 replies 1.8k views
Original Post
Endar
Endar
Okay, I'm having a little bit of trouble with template parameters. Take a little looksee, focusing mostly on the CFuntionWrapper template params

/*	Use boost pre-processor stuff to generate the elements of an array, because we'll only know
	at compile time how many params are needed. This is because I'm using boost::function which uses
	templates and it's easier to work like this than just put in 30 template parameters, most of which
	will end up as nothing.
	When the macro "WRITE_ARR_ELEMENTS" is called,  depending on the input it will print
	array elements. Eg. an input of (4,vertex) will expand to 
	"vertex[0] , vertex[1] , vertex[2] , vertex[3]" during the preprocessor stage.
 */
#define WRITE_ELEMENT(z, n, arr)	 BOOST_PP_COMMA_IF(n) BOOST_PP_CAT(arr[, BOOST_PP_CAT(n, ]))
#define WRITE_ARR_ELEMENTS(n, input)	 BOOST_PP_REPEAT(n, WRITE_ELEMENT, input )

/**
 * Concrete class for the function wrapper.
 * NOTE:: the "BOOST_PP_ENUM_PARAMS(N, T)" preprocessor macro will insert T0, T1, T2, etc, up to the
 * limit 'N', using the preprocessor ## to stick stuff together.
 */
template < BOOST_PP_ENUM_PARAMS(N, class T) >
class CFunctionWrapper	:	public IFunctionWrapper
{
private:

	/** Boost function wrapper
	 * Note: to use the <T0,T1,T2,T3> we need to use boost::function2.
	 * If we want to use the < int (float i, char c) > form, we need boost::function
	 */
	boost::function2< BOOST_PP_ENUM_PARAMS(N, T) > m_function;

public:

	/**
	 * Call the function.
	 * \param s A reference to a std::string object to store in any error messages
	 * caused when calling the function
	 * \param input A const reference to a util::array<boost::variant> object to store the input arguments.
	 * \param output A reference to a variant that will store the return value.
	 * \return 'true' if the call was successful, else 'false'
	 */
	bool call(	std::string& s, 
				const echo::util::array< console_variant_type >& input,
				console_variant_type& output )
	{
		// not enough function arguments
		if( input.getSize() != N ){
			s = "Error: not enough function arguments.";	// assign error message
			return false;					// return error
		}

		// call the function
		output = m_function( WRITE_ARR_ELEMENTS(N, input) );


		return true;
	}

};



My trouble is that I can't figure out how I'm going to be able to pass in the number 'N'. I'm not looking to do that at runtime, I'm sure that it needs to be done at compile time, and that's fine, no problem there. I just can't figure out how I'm going to be able to do it.
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Fruny
Fruny
Given you're using it in with BOOST_PP macros, the only way you can "pass" it is to #define it. Defining it before you include the header that contains the template definition, then undefining it is probably the best solution. If you want to generate the template several times with different N, there are tricks in BOOST_PP to do that.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Endar
Endar
But it all has to be done with constants, right? For every template class that has a different number of template params, I'll have to have a preprocessor statement, won't I?

Hmmmm. It might be time to think about doing this another way, without all the boost preprocessor stuff.
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Fruny
Fruny
Quote:
Original post by Endar
But it all has to be done with constants, right? For every template class that has a different number of template params, I'll have to have a preprocessor statement, won't I?


You can use BOOST_PP_REPEAT or BOOST_PP_ITERATE to have it done automatically.

With BOOST_PP_REPEAT, you generate your class definition as a macro, which would take N as a (macro) parameter, and BOOST_PP_FOR will call the macro a number of times, with the appropriate values of N.

With BOOST_PP_ITERATE, you would put your class in a header file (without include guards) and BOOST_PP_ITERATE would include that header a number of times, defining N (or other iteration variable) accordingly prior to inclusion.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Fruny
Fruny
With BOOST_PP_REPEAT

#include <boost/preprocessor/repetition/repeat.hpp>#define DEFINE_CFunctionWrapper(Z, N, TEXT)      \ template < BOOST_PP_ENUM_PARAMS(N, class T) >    \ class CFunctionWrapper : public IFunctionWrapper \ {                                                \    // stuff                                      \ };// Generate CFunctionWrapper for N = 0 to 9BOOST_PP_REPEAT(10, DEFINE_CFunctionWrapper, dummy)


With BOOST_PP_ITERATE

CFunctionWrapper.h#include <boost/preprocessor/iteration/iterate.hpp>#define BOOST_PP_FILENAME_1 "CFunctionWrapper.inc"#define BOOST_PP_ITERATION_LIMITS (0, 9)#include BOOST_PP_ITERATE()CFunctionWrapper.inc#define N BOOST_PP_ITERATION()template < BOOST_PP_ENUM_PARAMS(N, class T) >class CFunctionWrapper : public IFunctionWrapper{   // stuff};#undef N


Or, the same, as one self-including file:

CFunctionWrapper.h#if !BOOST_PP_IS_ITERATING   #ifndef INCLUDE_CFUNCTIONWRAPPER_H   #define INCLUDE_CFUNCTIONWRAPPER_H   #include <boost/preprocessor/iteration/iterate.hpp>   #define BOOST_PP_FILENAME_1 "CFunctionWrapper.h"   #define BOOST_PP_ITERATION_LIMITS (0, 9)   #include BOOST_PP_ITERATE()   #endif#else   #define N BOOST_PP_ITERATION()   template < BOOST_PP_ENUM_PARAMS(N, class T) >   class CFunctionWrapper : public IFunctionWrapper   {      // stuff   };   #undef N#endif


Of course, if you don't like heavy preprocessor magic ...
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Endar
Endar
I have nothing against it, but I've found that the boost::preprocessor docs page is a little harder to understand that I thought.

But, thanks, I'm going to work on the preprocessor stuff some more before giving it up.
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Fruny
Fruny
In fact, you probably want BOOST_PP_LOCAL_ITERATE() rather than BOOST_PP_REPEAT.

#include <boost/preprocessor/iteration/local.hpp>#define BOOST_PP_LOCAL_MACRO(N)                  \ template < BOOST_PP_ENUM_PARAMS(N, class T) >    \ class CFunctionWrapper : public IFunctionWrapper \ {                                                \    // stuff                                      \ };// Generate CFunctionWrapper for N = 0 to 9#define BOOST_PP_LOCAL_LIMITS (0, 9)#include BOOST_PP_LOCAL_ITERATE()


It might be faster than using file iteration with BOOST_PP_ITERATE.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Endar
Endar
Hmmmm.

Note: please don't ask or complain about the formatting. I've fixed it about 4 times and every single time GD makes it exacly as you see it. I've given up trying to fix it.

#define BOOST_PP_LOCAL_MACRO(n)			\ template < BOOST_PP_ENUM_PARAMS(n, class T) >	\ class CFunctionWrapper				\ {public:						\ 	int i;					\ };						\ /**/


This is what I'm using, but it's only repeating the first line, the template parameter line.

Here's the relevant excerpt of the source after the compiler's (VC++ 2005 express) preprocessing stage:

Note: in the first bit, I don't know why, but it gets rid of the '\' where there is a blank line. Probably because there are no tokens for parsing between the previous '\' and the next token.
class CFunctionWrapper		\ {				\ public:				\ 	int i;			\ };				\ 																#line 1 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"#line 15 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"#line 17 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"#line 28 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"#line 29 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"#line 33 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"        template <  > \ #line 36 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"        template <  class T0 > \ #line 39 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"        template <  class T0 , class T1 > \ #line 42 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"        template <  class T0 , class T1 , class T2 > \ #line 45 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"        template <  class T0 , class T1 , class T2 , class T3 > \ #line 48 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"        template <  class T0 , class T1 , class T2 , class T3 , class T4 > \ #line 51 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"        template <  class T0 , class T1 , class T2 , class T3 , class T4 , class T5 > \ #line 54 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"        template <  class T0 , class T1 , class T2 , class T3 , class T4 , class T5 , class T6 > \ #line 57 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"        template <  class T0 , class T1 , class T2 , class T3 , class T4 , class T5 , class T6 , class T7 > \ #line 60 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"        template <  class T0 , class T1 , class T2 , class T3 , class T4 , class T5 , class T6 , class T7 , class T8 > \ #line 63 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"#line 66 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"#line 69 "c:\\program files\\boost library\\boost\\preprocessor\\iteration\\detail\\local.hpp"


So, I'm not really sure where to go from here.
I'm not sure how to even debug something like this. If I look at the boost preprocessor source I can't understand half of it. I just don't have much experience at all except with simple preprocessor stuff.

Edit: code formatting fixed

[Edited by - Endar on April 14, 2006 2:22:42 AM]
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Fruny
Fruny
In the code I posted, I added a space after the trailing backslash because, as you found out yourself, if you don't, the forum software will run the lines together.

Make sure the backslash is actually the last character on the line in your code, but add a space when posting here. [smile]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Endar
Endar
AHA!!

Thanks Fruny. All fixed and much easier to read now.
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Fruny
Fruny
That should also solve your bug, by the way. Macros must be defined in a single line, so the backslashes are used to escape the carriage return and preserve readability.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Endar
Endar
This should be the last thing [grin]

Okay, I've got it working, but I've found that I had to put class BOOST_PP_CAT(CFunctionWrapper, n) for the class name, so with 1 template param it's "CFunctionWrapper1", then "CFunctionWrapper2", etc, etc.

This is because you can't have overloaded class templates, right? As soon as you declare it with whatever number of template params, it's set in stone, right?

Edit:: Am I also right in thinking that you can't have comments in a preprocessor macro?
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Fruny
Fruny
Quote:
Original post by Endar
This is because you can't have overloaded class templates, right? As soon as you declare it with whatever number of template params, it's set in stone, right?


Correct. Works just fine for function templates though.

You can generate a front-end using default template parameters. It will require having a preprocessor constant indicating the maximum number of template parameters you can handle, so that it can be changed easily.

class IFoo{protected:   // A unique type used as a placeholder   struct undef;};// Automatically generated by what you currently havetemplate<class T0> class Foo0 : public IFoo { /**/ };template<class T0, class T1> class Foo1 : public IFoo { /**/ };template<class T0, class T1, class T2> class Foo2 : public IFoo { /**/ };template<class T0, class T1, class T2, class T3> class Foo3 : public IFoo { /**/ };// The primary template is a special case // without arguments after the class name and// with default parameters in the parameter list.template<class T0, class T1=IFoo::undef, class T2=IFoo::undef, class T3=IFoo::undef>class Foo : public Foo3<T0,T1,T2,T3> { /**/ };// The partial specializations can be generated, up to N-1.template<class T0, class T1, class T2>class Foo<T0,T1,T2,IFoo::undef> : public Foo2<T0,T1,T2> { /**/ };template<class T0, class T1>class Foo<T0,T1,IFoo::undef,IFoo::undef> : public Foo1<T0,T1> { /**/ };template<class T0>class Foo<T0,IFoo::undef,IFoo::undef,IFoo::undef> : public Foo0<T0> { /**/ };int main(){   Foo<int> a1;   Foo<int,int> a2;   Foo<int,int,int> a3;   Foo<int,int,int,int> a4;}


Note that since you're wrapping functions, you may want to have the result type separate from the Tn parameters:

template class CFunctionWrapper2 { /**/ };

[Edited by - Fruny on April 14, 2006 5:13:55 AM]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Fruny
Fruny
Oh, and you may want to have a look at this thread.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Fruny
Fruny
Quote:
Original post by Endar
Edit:: Am I also right in thinking that you can't have comments in a preprocessor macro?


Well, you can't use C++-style // comments, since the macro is all in one line.
C-style /* comments */ should be fine.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Endar
Endar
Fruny, you have been so helpful to me, so I rated you up.

It didn't do much, I think you got a whole 2 points. [grin]

It's compiling properly the the preprocessing stage output looks good, so I'm going to go to bed before I break it again.

Thanks for the links, that's going to be the first thing I look at when I turn the computer on tomorrow.
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper

Topic Locked

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

Sign in to reply to this topic.