Advanced templates question.

Started by
7 comments, last by BeanDog 20 years, 8 months ago
The member in example 1 below is legal. A class C contains a class C with its n one lower, down until 1 (restriction allows compiling to work). Now, I would also like something like example 2, although it is not legal syntax as it stands. I would like for it to return a class C, unless n is 1 or lower, in which case it would return an int. Any ideas on how to do this? ~BenDilts( void );

template<int n>
class C
{
//example 1:

	C<n<1?1:n-1> m_c;

//example 2:

	n>1?C<n<1?1:n-1>&:int foo()
	{
	}
};
Advertisement
Loki provides a method for doing this, basically it allows you to select a type based on a compile time expression. So you would do like:

typedef typename Select<n<1,C,int>::Result ret_type;ret_type foo(); 


In the above, if n<1 evaluates to true, then C is the result, else int is the result.

You can download loki here. You want the Typemanip.h header, though there is a considerable amount of other Good Stuff in there. Make sure you use the right version of Typemanip.h though, and if you have VC6 I''m not even sure if it will work. The MSVC 1200 (VC6) version of Loki is supposed to be mostly complete, but I''m not sure what all is implemented.

Hope that helps,
desertfox

"Is life so dear, or peace so sweet, as to be purchased at the price of chains and slavery?" - Patrick Henry
template <int n>class C{   C<n - 1> m_c;   C foo();};template <>class C<1>{   int foo();};template <>class C<0>{   int foo();};


[How To Ask Questions|STL Programmer''s Guide|Bjarne FAQ|C++ FAQ Lite|C++ Reference|MSDN]
Arguing on the internet is like running in the Special Olympics: Even if you win, you're still retarded.[How To Ask Questions|STL Programmer's Guide|Bjarne FAQ|C++ FAQ Lite|C++ Reference|MSDN]
With a compiler supporting partial specialisation

template<bool test, class TrueType, class FalseType>struct ifte{  typedef typename TrueType result;};template<class TrueType, class FalseType>struct ifte<false>{  typedef typename FalseType result;};


With a compiler not supporting partial specialisation

namespace detail{  struct ifte_helper_true  {     template<class TrueType, class FalseType>     struct rebind     {        typedef TrueType result;     }  };  struct ifte_helper_false  {     template<class TrueType, class FalseType>     struct rebind     {        typedef FalseType result;     }  };  // MSVC dislikes nested template classes  // so we trick it with toplevel classes  template<bool test>  struct ifte_helper  {     typedef ifte_helper_true helper;  };  template<>  struct ifte_helper<false>  {     typedef ifte_helper_false helper;  };} // namespace detailtemplate<bool test, class TrueType, class FalseType>struct ifte{private:  typedef typename detail::ifte_helper<test>::helper helper;  typedef typename helper::rebind<TrueType, FalseType> rebind;public:  typedef typename rebind::result result;   };


Then you can do

template<int n>class C{   typedef C<n<1?1:n-1> next_type;   typedef typename ifte< n<1, next_type&, int>::result foo_return_type;//example 1:   next_type m_c m_c;//example 2:   foo_return_type foo() {}};


However, for clarity and portability, I''d suggest you use a metaprogramming library that already provide such constructs as I gave you (I typed the example for non-specializing compilers from memory so it can be dead wrong).

Check out the Boost library, in particular boost::mpl and type traits.
Or check out the Loki library, in particular the TypeManip.h header. Andrei Alexandrescu''s book "Modern C++ Design" serves as a reference manual.

[ Start Here ! | How To Ask Smart Questions | Recommended C++ Books | C++ FAQ Lite | Function Ptrs | CppTips Archive ]
[ Header Files | File Format Docs | LNK2001 | C++ STL Doc | STLPort | Free C++ IDE | Boost C++ Lib | MSVC6 Lib Fixes ]
"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

Forgive me but I''m totally gobsmacked as to why (apart from research and play) anyone would need such a construct in the first place. There is such a thing as taking generalization too far - to absurdity in fact!


Oh and apologies from not adding anything to the debate
My point was to make an n-dimensional Array template class. The [] operator would return an n-1 dimensional Array, unless n was 1, when it would return the value.

I will probably go back and rewrite it with the dimension as a template parameter (to simplify class usage syntax), but here's what I came up with without using your suggestions:

template<typename T>class Array{	Array<T> **m_SubArrays;	bool m_bRoot;	int m_iSize;	int m_iIdx;	//the following are the same for all children:	T *m_pVal;	int m_iDim;	int *m_pSize;	Array<T> *m_pParent;	int m_iFac;	int m_iFacTimesIdx;	inline int EvaluateIndex(int i)	{		if(m_bRoot)			return(i*m_iFac);		else			return(m_pParent->EvaluateIndex(i) + m_iFacTimesIdx);	}	T *m_pOneVal;	static Array<T> m_aOneVal;public:	Array(T* pVal) : m_pOneVal(pVal){}	Array(int Dim, int *pSize, T *pVal=NULL, int AllDim=0, int *pAllSize=NULL, Array<T> *pParent=NULL, int iIdx = 0)	{		m_pOneVal = NULL;		m_pParent = pParent;		m_iIdx = iIdx;		int i;		m_bRoot = (pVal==NULL);		if(m_bRoot)		{			int iTotal = 1;			for(i = 0; i < Dim; i++)				iTotal *= pSize[i];			pVal = new T[iTotal];			pAllSize = pSize;			AllDim = Dim;		}		m_iFac = 1;		for(i = AllDim-Dim+1; i < AllDim; i++)			m_iFac *= pAllSize[i];		m_iFacTimesIdx = m_iFac * m_iIdx;		m_iSize = pSize[0];		m_pVal = pVal;		m_iDim = AllDim;		m_pSize = new int[AllDim];		memcpy(m_pSize, pAllSize, sizeof(int)*AllDim);		if(Dim > 1)		{			m_SubArrays = new Array<T>* [pSize[0]];			for(i = 0; i < pSize[0]; i++)				m_SubArrays[i] = new Array<T>(Dim-1, pSize+1, m_pVal, AllDim, pAllSize, this, i);		}		else		{			m_SubArrays = NULL;		}	}	Array<T>& operator[] (int i)	{		if(m_SubArrays)			return(*m_SubArrays[i]);		else		{			m_aOneVal.m_pOneVal = m_pVal+EvaluateIndex(i);			return(m_aOneVal);		}	}	T& operator=(T& val)	{		return *m_pOneVal = val;	}	operator T&()	{		return(*m_pOneVal);	}	virtual ~Array()	{		if(m_bRoot)			delete [] m_pVal;				if(m_SubArrays)		{			for(int i = 0; i < m_iSize; i++)				delete m_SubArrays[i];			delete [] m_SubArrays;		}		delete [] m_pSize;	}};template<typename T>Array<T> Array<T>::m_aOneVal = Array<T>(NULL);


To use it, just do something like this to make, write to, and read from a 10x10x10x10x10 array:

   	int size[5] = {10,10,10,10,10};	Array<int> arr(5, size);	int i;	for(int x = 0; x < 10; x++)	{		for(int y = 0; y < 10; y++)		{			for(int z = 0; z < 10; z++)			{				for(int a = 0; a < 10; a++)				{					for(int b = 0; b < 10; b++)					{						arr[x][y][z][a][b] = x;						i = (int)arr[x][y][z][a][b];					}				}			}		}	}


This way we only do one new and delete.

Edit: It's not optimized at all. The creation of the array takes about 10% longer than manually new-ing a jagged array to the correct size. But it sure is a heck of a lot easier to create :-) And access times are about 15%-20% faster for a 20x20x20x20x20 array (my test case).




~BenDilts( void );

[edited by - BeanDog on August 15, 2003 10:15:30 AM]

[edited by - BeanDog on August 15, 2003 10:23:28 AM]
quote:Original post by Robbo

Forgive me but I''m totally gobsmacked as to why (apart from research and play) anyone would need such a construct in the first place. There is such a thing as taking generalization too far - to absurdity in fact!

Have you read Modern C++ Design?
There are all kinds of wierd classes like this around. Search around for template metaprogramming and you will find all kinds of classes like this that can build a sequence of numbers such as prime numbers or Fib, or do square roots (integer ones any way) at compile time.

This topic is closed to new replies.

Advertisement