Archived

This topic is now archived and is closed to further replies.

BeanDog

Advanced templates question.

Recommended Posts

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()
	{
	}
};

Share this post


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

Share this post


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


template<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 ]

Share this post


Link to post
Share on other sites

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!

Share this post


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

Share this post


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

Share this post


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

Share this post


Link to post
Share on other sites