Re: inhomogeneous container
Valeriu Catina wrote:
Hi,
I have an array class (the blitz library actually) which looks like this:
template<typename P_numtype, int N_rank>
class Array : public MemoryBlockReference<P_numtype>
, public ETBase<Array<P_numtype,N_rank> >
{
// ... code
};
I would like to create a container class which allows me to store
Array<T,N> objects of different rank N and same data type T. The
container should have [](int i) operators which would return a reference
at i-th array.
My questions would be how could I deduce the returned type of the
operator [](int) and what should I use to store the arrays ?
template<class T_numtype>
class ArrayPool{
public:
Array<T_numtype, N_rank ??? > operator[](int i)
{
// ... return i-th array from pool
}
private:
// ... data
};
Thanks in advance.
Obviously there is no sensible return for operator[] as you've defined
it. Assuming you cannot modify the Array class, probably your best
option is to return a type that exposes the interface of Array<T, N>,
but does not have any template parameters itself, and allows you to
query for N. This requires several stages of indirection, which I will
now describe:
1) Create a class that exposes the same interface as Array<T, N> (and
anything else you'd want to do, such as querying rank), but consists of
only pure virtual functions. This class will also need a "clone"
function, for reasons that will become apparent. In the example code
below this is called ArrayTImplBase.
2) Create a class template (on T and N) that has data member of type
Array<T, N> and inherits from the class created in step 1. This class
should implement the inherited virtual functions by forwarding them to
the data member. In the example code this is called ArrayTImpl.
3) Create a class that exposes the same interface as that in step 1, and
can be constructed from an instance of Array<T, N> (i.e., the
constructor is templated, not the class). This class will use the PIMPL
idiom to manage an object of type ArrayTImpl<T, N> (and therefore an
object of type Array<T, N>). If you want to store these in a standard
container (which was the whole point), then you MUST implement a copy
constructor and operator=. This is why it was necessary to create a
clone function. In the example code this is called ArrayT.
While I haven't done exhaustive testing, I think the following
represents a minimal working example:
#include <cstddef> // for std::size_t
#include <algorithm> // for std::swap
template <typename T, std::size_t N>
class Array
{
public:
int f1()
{
return 1 ;
}
int f2()
{
return 2 ;
}
int f3()
{
return 3 ;
}
} ;
class ArrayTImplBase
{
public:
virtual std::size_t rank() const = 0 ;
virtual ArrayTImplBase * clone() = 0 ;
virtual ~ArrayTImplBase()
{}
virtual int f1() = 0 ;
virtual int f2() = 0 ;
virtual int f3() = 0 ;
} ;
template <typename T, std::size_t N>
class ArrayTImpl : public ArrayTImplBase
{
public:
std::size_t rank() const
{
return N ;
}
ArrayTImpl<T, N> * clone()
{
return new ArrayTImpl(array_) ;
}
ArrayTImpl(const Array<T, N> & array)
: array_(array)
{}
int f1()
{
return array_.f1() ;
}
int f2()
{
return array_.f2() ;
}
int f3()
{
return array_.f3() ;
}
private:
Array<T, N> array_ ;
} ;
template <typename T>
class ArrayT
{
public:
template <std::size_t N>
explicit ArrayT(const Array<T, N> & array)
: pimpl_(new ArrayTImpl<T, N>(array))
{}
ArrayT(const ArrayT & a)
: pimpl_(a.pimpl_->clone())
{}
ArrayT & operator=(const ArrayT & a)
{
ArrayTImplBase * p = a.pimpl_->clone() ;
std::swap(p, pimpl_) ;
delete p ;
}
~ArrayT()
{
delete pimpl_ ;
}
std::size_t rank() const
{
return pimpl_->rank() ;
}
int f1()
{
return pimpl_->f1() ;
}
int f2()
{
return pimpl_->f2() ;
}
int f3()
{
return pimpl_->f3() ;
}
private:
ArrayTImplBase * pimpl_ ;
} ;
--
Alan Johnson