Template Metaprograms

Started by
4 comments, last by sQuid 20 years, 11 months ago
I''m writing a Tensor class with an element access operator (). () takes as many arguments as the dimension of the Tensor. So for a 2-tensor we''d have
    
inline float& operator(int e1, int e2);
  
whilst for a 3-tensor it would be
      
inline float& operator(int e1, int e2, int e3);
    
and so on. I would like to template (or otherwise) the class so the number of arguments to () can be determined and the appropriate function defined automatically. Is this possible and can anyone provide some ideas on how to do it? thanking you ...
Advertisement
Afaik, there is no way to actually specify number of arguments in a function. But here is some code (sort of borrowed from Alexandrescu...) but sort of fakes it. Not sure if that will work for you...


  template < int N >class IntArgList {public:	int head;	IntArgList<N-1> tail;	enum { size = N };	int operator[](int i) {		assert(i >= 0 && "Error i is negative in operator[]");		assert(i < N && "Error i is greater than N-1 in operator[]");		if(i == 0) { return head; }		return tail[i-1]; 	}};template < >class IntArgList< 2 > {public:	int head;	int tail;	enum { size = 2 };	int operator[](int i) {		assert(i >= 0 && "Error i is negative in operator[] for IntArgList<2>");		assert(i < 2 && "Error i is greater than N-1 in operator[] for IntArgList<2>");		if(i == 0) { return head; }		return tail;	}};template < >class IntArgList< 1 > {public:	int head;	enum { size = 1 };	int operator[](int i) {		assert(i == 0 && "Error i is negative in operator[] for IntArgList<1>");		return head;	}};template < int N >class tensor {	float& operator(const IntArgList<N>& args) {... }};  


Regards,
Jeff

[edited by - rypyr on May 5, 2003 12:55:44 AM]
Well, one way would be to use BOOST_PP_ (www.boost.org) to do an specialization for the operator() from say 0 to 15 for the whole tensor class. Ugly. But the only "perfect" way IMHO.

  #define MAKE_TENSOR(z,n,data) \template<> struct tensor<n> { \inline float operator()(BOOST_PP_ENUM_PARAMS(n,float e) ) {\.... \} \};BOOST_PP_REPEAT(15,MAKE_TENSOR,_);  
I was pretty intrigued by this so I did a little more thinking...


  #include <iostream>#include <assert.h>#include <stdio.h>#include <stdarg.h>template< int N >class IntArgList {public:	int head;	IntArgList< N-1 > tail;	int& operator[](int i) { return const_cast<int&>(argAt(i)); }	int operator[](int i) const { return argAt(i); }	const int& argAt(int i) const {		assert(i >= 0 && "Error i is < 0 in IntArgList<N>");		assert(i < N && "Error i is > N-1 in IntArgList<N>");		if(i == 0) {			return head;		}		return tail.argAt(i-1);	}	IntArgList<N>() : head(0) {}	IntArgList<N>(int h, ...) : head(h) {		va_list vl;		va_start( vl, h );		for(int loop = 0; loop < N-1; ++loop) {			tail[loop] = va_arg( vl, int );		}		va_end( vl );	}};template< >class IntArgList< 2 > {public:	int head;	int tail;	int& operator[](int i) { return const_cast<int&>(argAt(i)); }	int operator[](int i) const { return argAt(i); }	const int& argAt(int i) const {		assert(i >= 0 && "Error i is < 0 in IntArgList<N>");		assert(i < 2 && "Error i is > N-1 in IntArgList<2>");		if(i == 0) {			return head;		}		return tail;	}	IntArgList<2>() : head(0), tail(0) {}	IntArgList<2>(int h, int t) : head(h), tail(t) {}};template< >class IntArgList< 1 > {public:	int head;	int& operator[](int i) { return const_cast<int&>(argAt(i)); }	int operator[](int i) const { return argAt(i); }	const int& argAt(int i) const {		assert(i == 0 && "Error i is != 0 in IntArgList<1>");		return head;	}	IntArgList<1>() : head(0) {}	IntArgList<1>(int h) : head(h) {}};template< int N >class IntVector { // replace this with approp. tensor codepublic:	int fields[N];	void initialize( const IntArgList<N>& args ) {		for(int loop = 0; loop < N; ++loop ) {			fields[loop] = args[loop];		}	}	void log() {		for(int loop = 0; loop < N; ++loop ) {			std::cout << "Field #" << loop << " = " << fields[loop] << std::endl;		}	}};// this pragma enforces the user to use the right # of arguments#pragma warning( error: 4002 )#define ARG1(x1) IntArgList<1>(x1)#define ARG2(x1,x2) IntArgList<2>(x1,x2)#define ARG3(x1,x2,x3) IntArgList<3>(x1,x2,x3)#define ARG4(x1,x2,x3,x4) IntArgList<4>(x1,x2,x3,x4)#define ARG5(x1,x2,x3,x4,x5) IntArgList<5>(x1,x2,x3,x4,x5)#define ARG6(x1,x2,x3,x4,x5,x6) IntArgList<6>(x1,x2,x3,x4,x5,x6)#define ARG7(x1,x2,x3,x4,x5,x6,x7) IntArgList<7>(x1,x2,x3,x4,x5,x6,x7)#define ARG8(x1,x2,x3,x4,x5,x6,x7,x8) IntArgList<8>(x1,x2,x3,x4,x5,x6,x7,x8)// generate more if you need them...int main(int argc, char* argv[]){	IntVector<3> vec3;	vec3.initialize(ARG3(4,8,16,32)); // generates a compiler error	vec3.initialize(ARG4(4,8,16,32)); // generates a compiler error	vec3.initialize(ARG3(4,8,16)); // OK!	vec3.log();	return 0;}  


If you find the use of the macros ugly, you can still use:

vec3.initialize(IntArgList<3>(4,8,16));

However, you won''t get the benefit of the compiler error if you do this:

vec3.initialize(IntArgList<3>(4,8,16,32)); // compiles without warning

This is one case where I found the preprocessor very useful in "enforcing" my rules.

Now I realize that the above approach may just be overkill, so you could do something like this:


  template< int N >class Tensor {  float operator()(int x1, ...) {    // grab the variable arguments, similar to     // my IntArgList<N> constructor approach above  }};template< >class Tensor<1> {  float operator()(int x1) {  }};  


The benefit of this second approach is that you don''t create any temporary objects (for the IntArgList), it will compile much smaller. The disadvantage to this approach is that there is nothing preventing someone from doing:

Tensor<3> tens3;
float f = tens3(4, 8, 16, 32, 64, 128);

Although I suppose you could use the similar ARGx macro approach above without any extra runtime cost.

Regards,
Jeff
Thanks for the replies. I will have to think about them a little, but it looks like what I was wanting.

Chris.
Wow, I am a silly person. I''m not sure why I wanted to use template recursion so much with my IntArgList template class above...the obvious way to implement this is:


  template< unsigned int N > class IntArgList {public:  int args[N];  int& operator[](int i) { return args[i]; } // syntactic sugar};  


Regards,
Jeff

This topic is closed to new replies.

Advertisement