Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C++ (http://www.velocityreviews.com/forums/f39-c.html)
-   -   heterogenous container class (http://www.velocityreviews.com/forums/t741875-heterogenous-container-class.html)

A 01-12-2011 12:54 AM

heterogenous container class
 
I'd like to create a heterogenous container class of various objects.
I would use std::vector for holding such objects.
Let's assume we have ClassA, ClassB objects

struct TClassA
{
int i;
TClassA() : i(0) {} // default init
TClassA(int ii) : i(ii) {} // init with value
}

struct TClassB
{
float f;
TClassB() : f(0.0) {} // default init
TClassB(float ff) : f(ff) {} // init with value
}

struct TContainer
{
TClassA ca; // holds a
TClassB cb; // holds b
int type; // what is stored, a or b

TContainer(TClassA ainit) : ca(ainit), type(0) {} // init for a
TContainer(TClassB binit) : cb(binit), type(1) {} // init for b
}

// Using the above
std::vector<TContainer> Container;
Container.push_back(TClassA(123)); // Add integer
Container.push_back(TClassB(123.456)); // Add float

//read
if (Container[0].type == 0) int i = Container[0].ca;
if (Container[0].type == 1) float f = Container[0].cb;


The above works but for larger structures it leaves unnecessary memory
footprint because each class holds init functions and container also holds
all of the redundant data.

Can anyone give some clues how to restructure the above so it can be read
and written to similarly easy like the above but without the burden of
redundant constructors in each object e.g for TClassA to hold only "int i"
and not much else...

Any ideas are welcome.



A 01-12-2011 01:58 AM

Re: heterogenous container class
 
> Use a variant.

can you elaborate that a bit?



A 01-12-2011 04:08 AM

Re: heterogenous container class
 
I think I may get rid of the struct TContainer using boost::any. From what
i've seen it is probably the best way of implementing heterogenous
containers. Maybe boost::variant could be used as alternative.

eg.

// Using the above
std::vector<boost::any> Container;
Container.push_back(TClassA(123)); // Add class with integer
Container.push_back(TClassB(123.456)); // Add class with float



Ulrich Eckhardt 01-12-2011 06:14 AM

Re: heterogenous container class
 
A wrote:
> I think I may get rid of the struct TContainer using boost::any. From
> what i've seen it is probably the best way of implementing heterogenous
> containers. Maybe boost::variant could be used as alternative.


Right, both are ways to consider. The main difference is that the overhead
of "any" is bigger, but also you can store really anything in it.

The "variant" knows the possible types and can already allocate storage
for the largest part, constructing objects as they are assigned. "any" has
to allocate according to what comes.

Generally, I'd use "variant", because they have the better interface.
Using "any" is a bit like fiddling with void pointers, you have to do lots
of type casting manually.

Good luck!

Uli


Goran 01-12-2011 07:38 AM

Re: heterogenous container class
 
On Jan 12, 1:54*am, "A" <a...@a.a> wrote:
> Can anyone give some clues how to restructure the above so it can be read
> and written to similarly easy like the above but without the burden of
> redundant constructors in each object e.g for TClassA to hold only "int i"
> and not much else...
>
> Any ideas are welcome.


I think that you need to think bigger about __operations__ your
TContainer should provide (currently it's a simple data storage). It
looks like you need some run-time polymorphism, but it's not clear
from uses you have shown. For example, in

if (Container[0].type == 0) int i = Container[0].ca;
if (Container[0].type == 1) float f = Container[0].cb;

What is really interesting is what happens with i and f later. You
should be able to extract services that TContainer should provide for
that "later" part. But that would require e.g.

std::vector<TContainer*> Container;

If you don't like that, you might use a wrapper that provides said
services, e.g.:

class TContainerHandler
{
public:
TContainerHandler(TClassBase& data) : FData(data) {}
virtual operation1() = 0;
virtual operation2() = 0;
protected:
TClassBase& FData;
};
// derive for TClassA and TClass B.

and then:

TContainerHandler& h = Container[0].type == 0 ?
TContainerHandlerA(Container[0]) : TContainerHandlerB(Container[0]);
// above needs some casting, perhaps doesn't work properly, either
Use(h);

Goran.

Paul 01-12-2011 10:06 AM

Re: heterogenous container class
 

"A" <a@a.a> wrote in message news:igiu59$ab2$1@gregory.bnet.hr...
> I'd like to create a heterogenous container class of various objects.
> I would use std::vector for holding such objects.
> Let's assume we have ClassA, ClassB objects
>
> struct TClassA
> {
> int i;
> TClassA() : i(0) {} // default init
> TClassA(int ii) : i(ii) {} // init with value
> }
>
> struct TClassB
> {
> float f;
> TClassB() : f(0.0) {} // default init
> TClassB(float ff) : f(ff) {} // init with value
> }
>
> struct TContainer
> {
> TClassA ca; // holds a
> TClassB cb; // holds b
> int type; // what is stored, a or b
>
> TContainer(TClassA ainit) : ca(ainit), type(0) {} // init for a
> TContainer(TClassB binit) : cb(binit), type(1) {} // init for b
> }
>
> // Using the above
> std::vector<TContainer> Container;
> Container.push_back(TClassA(123)); // Add integer
> Container.push_back(TClassB(123.456)); // Add float
>
> //read
> if (Container[0].type == 0) int i = Container[0].ca;
> if (Container[0].type == 1) float f = Container[0].cb;
>
>
> The above works but for larger structures it leaves unnecessary memory
> footprint because each class holds init functions and container also holds
> all of the redundant data.
>
> Can anyone give some clues how to restructure the above so it can be read
> and written to similarly easy like the above but without the burden of
> redundant constructors in each object e.g for TClassA to hold only "int i"
> and not much else...
>
> Any ideas are welcome.
>

I did some work on this many years ago, it basically re-allocates anytime
you change the data type of an element. I don't know if this is any use as
it would require some work to incorporate vector like behavior.
Please find some example code that demonstrates the general class design I
began with:

#include <iostream>
#include <vector>
typedef unsigned int UINT;

template<class T1, class T2,class T3>
class bMixedArray{
public:
virtual void Allocate(const T1& rhs){}
virtual void Allocate(const T2& rhs){}
virtual void Allocate(const T3& rhs){}
};

template<class T1, class T2,class T3>
class bDataItem{
private:
bMixedArray<T1,T2,T3>* itsContainer;
public:
virtual ~bDataItem(){}
//bDataItem():itsContainer(0){}
bDataItem(bMixedArray<T1,T2,T3>* p):itsContainer(p){}

virtual bDataItem& IndexMe(){return *this;}

virtual bDataItem& operator=(const T1& rhs){
if(itsContainer)
itsContainer->Allocate(rhs);
return *this;}
virtual operator T1(){return 0;}

virtual bDataItem& operator=(const T2& rhs){
if(itsContainer)
itsContainer->Allocate(rhs);
return *this;}
virtual operator T2(){return 0;}

virtual bDataItem& operator=(const T3& rhs){
if(itsContainer)
itsContainer->Allocate(rhs);
return *this;}
virtual operator T3(){return 0;}
};

template<class T,class T1,class T2,class T3>
class DataItem:public bDataItem<T1, T2,T3>{
private:
T itsData;
public:
~DataItem(){}
//DataItem(){}
DataItem(bMixedArray<T1,T2,T3>* p):bDataItem<T1,T2,T3>(p){}
DataItem& operator=(const T& rhs){itsData = rhs; return *this;}
operator T(){return itsData;}
DataItem<T,T1,T2,T3>& IndexMe(){return *this;}
};



template<class T1, class T2, class T3>
class MixedArray:public bMixedArray<T1,T2,T3>{
private:
bDataItem<T1,T2,T3>** itsArray;
UINT itsSize;
UINT lastIndexed;
public:
MixedArray(UINT size):itsSize(size),lastIndexed(0){
itsArray= new bDataItem<T1,T2,T3>*[size];
for(UINT i=0; i<itsSize; i++)
itsArray[i] = 0;
}

~MixedArray(){
for(UINT i=0; i<itsSize; i++){
delete itsArray[i];}
delete [] itsArray;
}

void Allocate(const T1& rhs){
delete itsArray[lastIndexed];
itsArray[lastIndexed] = new DataItem<T1,T1,T2,T3>(this);
*(itsArray[lastIndexed]) = rhs;
}
void Allocate(const T2& rhs){
delete itsArray[lastIndexed];
itsArray[lastIndexed] = new DataItem<T2,T1,T2,T3>(this);
*(itsArray[lastIndexed]) = rhs;
}
void Allocate(const T3& rhs){
delete itsArray[lastIndexed];
itsArray[lastIndexed] = new DataItem<T3,T1,T2,T3>(this);
*(itsArray[lastIndexed]) = rhs;
}

bDataItem<T1,T2,T3>& operator[](UINT i){
if(i<itsSize){
if(itsArray[i]){
return itsArray[i]->IndexMe();
}else{
itsArray[i] = new bDataItem<T1,T2,T3>(this);
lastIndexed = i;
return *(itsArray[i]);
}
}
}
};


int main(){
MixedArray<char*, int , float> myArray(100);//Template parameters define
allowed types.

myArray[0] = "hello\0";
myArray[0] = 5;
myArray[0] = 7.56f;

myArray[99] = "This is element 99\0"


std::cout<< sizeof(myArray)<<std::endl;

std::vector<int> v(100);
std::cout<< sizeof(v);


return 0;
}


This are some restrictions in this class desgin , the fact that you have a
limited amount of parameter types, 3 in this case. Also it lacked
compatability with the std lib.
I began a more advanced version which uses a different template organisation
but it is quite complicated. I willl post some code in a sep post to give
you the general idea.


Paul 01-12-2011 10:22 AM

Re: heterogenous container class
 

"Paul" <pchristor@yahoo.co.uk> wrote in message
news:X2fXo.74859$Zo4.50520@newsfe14.ams2...
>

Please find the incomplete and buggy code I mentioned in my previous post.
The intention of this class was that a container class would be created to
contain an array of bData pointers.
I'm basically posting this to show the different template design I found
necessary for an indefinite list of type parameters and compatability with
the STL.

#include <iostream>

template<class T1=int, class T2=char, class T3=double, class T4=bool>
class traits{
public:
struct Empty{};
template<class H,class T>struct LN{
typedef H head;
typedef T tail;
};
template<class t1=Empty,class t2=Empty,class t3=Empty,class t4=Empty>struct
List{
typedef LN<t1,LN<t2,LN<t3,LN<t4,Empty> > > > type;
};
typedef typename List<T1,T2,T3,T4>::type type_list;

template<class T>
struct size_of{
enum{value = sizeof(T)};
};

template<int I>struct get_type{
typedef Empty type;
};
template<>struct get_type<1>{
typedef T1 type;
};
template<>struct get_type<2>{
typedef T2 type;
};
template<>struct get_type<3>{
typedef T3 type;
};
template<>struct get_type<4>{
typedef T4 type;
};


template<class T, class U>struct is_same{
enum{value = 0};
};
template<class T>struct is_same<T,T>{
enum{value = 1};
};

template<typename T, typename L>struct is_member{
enum{value= traits::is_same<T, typename L::head>::value ||
traits::is_member<T, typename L::tail>::value};
};
template<typename T>struct is_member<T, typename traits::Empty>{
enum{value= false};
};

template<class T, class L>struct index{
enum{ value = is_member<T,L>::value + traits::index<T, typename
L::tail>::value };
};
template<class T>struct index<T, typename traits::Empty>{
enum{value = 0};
};
template<class L>struct index<typename L::head, L>{
enum{value = 1};
};

template<class L>
struct length{
enum{ value = not_empty<L::head>::value + length<typename L::tail>::value};
};
template<>
struct length<typename traits::Empty>{
enum{value=0};
};

template<typename L>struct not_empty{
enum{value = 1};
};
template<>struct not_empty<typename traits::Empty>{
enum{value = 0};
};

typedef typename List<int,char,unsigned,long>::type int_types;

};


//template<class T1>class bData;
//template<class T1,class T2>class Data;

template<class _traits=traits<> >
class bData{
public:
bData(){std::cout<<"bData()\n";}
bData(const bData& rhs){std::cout<<"bData(const bData&)\n";}

virtual ~bData(){std::cout<<"~bData()\n";}
virtual bData& operator=(const bData&
rhs){std::cout<<"bData::operator=(const bData&)\n";return *this;}

typedef typename _traits::type_list::head t1;
typedef typename _traits::type_list::tail node2;
typedef typename node2::tail node3;
typedef typename node3::tail node4;
typedef typename node2::head t2;
typedef typename node3::head t3;
typedef typename node4::head t4;

virtual bData& operator=(const t1&){std::cout<<"bData::operator=(const
t1&)\n"; return *this;}
virtual bData& operator=(const t2&){std::cout<<"bData::operator=(const
t2&)\n"; return *this;}
virtual bData& operator=(const t3&){std::cout<<"bData::operator=(const
t3&)\n"; return *this;}
virtual bData& operator=(const t4&){std::cout<<"bData::operator=(const
t4&)\n"; return *this;}
virtual operator t1()const{std::cout<<"bData::operator t1\n"; return t1();}
virtual operator t2()const{std::cout<<"bData::operator t2\n"; return t2();}
virtual operator t3()const{std::cout<<"bData::operator t3\n"; return t3();}
virtual operator t4()const{std::cout<<"bData::operator t4\n"; return t4();}

virtual int size_of(){return 0;}
};

template<class T,class _traits=traits<> >
class Data:public bData<_traits>{
public:
Data(){std::cout<<"Data()\n";}
Data(const T& d){itsData = d; std::cout<<"Data(T&)\n";}

~Data(){std::cout<<"~Data()\n";}
Data(const Data& rhs){itsData = rhs.itsData; std::cout<<"Data(cost
Data&)\n";}

Data& operator=(const T& rhs){itsData=rhs;
std::cout<<"Data::operator=(const T&)\n"; return *this;}
Data& operator=(const Data& rhs){itsData=rhs.itsData;
std::cout<<"Data::operator=(const Data&)\n"; return *this;}
Data& operator=(const bData<_traits>& rhs){itsData = rhs;
std::cout<<"Data::operator=(const bData&)\n"; return *this;}
operator T()const{std::cout<<"Data::operator T\n"; return itsData;}

int size_of(){return _traits::size_of<T>::value;}
private:
T itsData;
};




Stuart Redmann 01-12-2011 03:28 PM

Re: heterogenous container class
 
On 12 Jan., 01:54, "A" <a...@a.a> wrote:
> I'd like to create a heterogenous container class of various objects.
> I would use std::vector for holding such objects.
> Let's assume we have ClassA, ClassB objects
>
> struct TClassA
> * * {
> * * int i;
> * * TClassA() : i(0) {} * * * *// default init
> * * TClassA(int ii) : i(ii) {} // init with value
> * * }
>
> struct TClassB
> * * {
> * * float f;
> * * TClassB() : f(0.0) {} * * * *// default init
> * * TClassB(float ff) : f(ff) {} // init with value
> * * }
>
> struct TContainer
> * * {
> * * TClassA ca; * *// holds a
> * * TClassB cb; * *// holds b
> * * int * * type; *// what is stored, a or b
>
> * * TContainer(TClassA ainit) : ca(ainit), type(0) {} // init for a
> * * TContainer(TClassB binit) : cb(binit), type(1) {} // init for b
> * * }
>
> // Using the above
> std::vector<TContainer> Container;
> Container.push_back(TClassA(123)); * * * *// Add integer
> Container.push_back(TClassB(123.456)); * *// Add float
>
> //read
> if (Container[0].type == 0) int * i = Container[0].ca;
> if (Container[0].type == 1) float f = Container[0].cb;
>
> The above works but for larger structures it leaves unnecessary memory
> footprint because each class holds init functions and container also holds
> all of the redundant data.
>
> Can anyone give some clues how to restructure the above so it can be read
> and written to similarly easy like the above but without the burden of
> redundant constructors in each object e.g for TClassA to hold only "int i"
> and not much else...
>
> Any ideas are welcome.


Why don't you just use a union?

#include <vector>

struct TClassA
{
int i;
} ;


struct TClassB
{
float f;
} ;

struct TContainer
{
union
{
TClassA ca; // holds a
TClassB cb; // holds b
} c;
int type; // what is stored, a or b


TContainer(int ainit) : type(0)
{
c.ca.i = ainit;
} // init for a
TContainer(float binit) : type(1) // init for b
{
c.cb.f = binit;
}
} ;



// Using the above
int main ()
{
std::vector<TContainer> Container;
Container.push_back(TContainer (123)); // Add integer
Container.push_back(TContainer (123.456f)); // Add float


//read
if (Container[0].type == 0) int i = Container[0].c.ca.i;
if (Container[0].type == 1) float f = Container[0].c.cb.f;
}

Regards,
Stuart

Ulrich Eckhardt 01-12-2011 07:04 PM

Re: heterogenous container class
 
Stuart Redmann wrote:
> On 12 Jan., 01:54, "A" <a...@a.a> wrote:
>> I'd like to create a heterogenous container class of various objects.
>> I would use std::vector for holding such objects.

[...]
> Why don't you just use a union?


You can only use unions on PODs.

> struct TClassA
> {
> int i;
> } ;
>
>
> struct TClassB
> {
> float f;
> } ;
>
> struct TContainer
> {
> union
> {
> TClassA ca; // holds a
> TClassB cb; // holds b
> } c;



These two are PODs, so a union would work. Otherwise, to explain why a
union doesn't work, ask yourself the question who's constructor and
destructor should be called if such a union is created or destroyed.

Take a look at Boost.Variant. It has a variant template which a) takes
care of properly creating and destroying any object type and b) keeps
track of which type is currently used.

Uli


A 01-13-2011 03:47 AM

Re: heterogenous container class
 
the reply from paul is a bit of overkill for me.

boost::variant is just fine for this purpose. I simply define all possible
classes used eg.

std::vector< boost::variant<TClassA, TClassB> > var;

var.push_back(TClassA(123));
var.push_back(TClassB(123.456f));

Why fiddling with unions and all that stuff. I also found later that variant
is good for this purpose and it can also detect some errors in compiletime
due the fact it has predefined possible classes.

reading is also quite easy:

TClassA a = boost::get<TClassA> (var);

it also gives compile error if you do
TClassB b = boost::get<TClassA> (var);

which is great...

The only thing I haven't found yet is how to check the type of
boost::variant before reading it with boost::get ... perhaps I just didn't
look hard enough... Anyone knows?




All times are GMT. The time now is 02:36 AM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.