Designing a generic data holder class

F

freesoft12

Hi,

I have my own general purpose Tree data structure representation in C+
+. I have a bunch of algorithms that create this Tree and then create
some attributes to each of the nodes/edges during the course of the
algorithm. I want the attribute to be attached to the Node/Edge
itself.

The problem I am getting stumped with is the part where I want to
allow any type (vector<string>, map<int,string>,
MySetOfAttributesClass ) to be stored as an attribute in each of the
Tree's Nodes/Edges.

ie.

class Node {
public:
void addNewAttribute(<generic_attribute>);// adds a new attribute
to this Node

unsigned getNumAttributes() const ; // get the total number of
attributes registered
<generic_attribute> getAttribute(unsigned i) ; // get the i'th
attribute

private:

vector< <generic_attribute> > listOfAttributes;
}

Where <generic_attribute> can be any class.

Regards
John
 
S

Stephen Horne

Hi,

I have my own general purpose Tree data structure representation in C+
+. I have a bunch of algorithms that create this Tree and then create
some attributes to each of the nodes/edges during the course of the
algorithm. I want the attribute to be attached to the Node/Edge
itself.

The problem I am getting stumped with is the part where I want to
allow any type (vector<string>, map<int,string>,
MySetOfAttributesClass ) to be stored as an attribute in each of the
Tree's Nodes/Edges.

ie.

class Node {
public:
void addNewAttribute(<generic_attribute>);// adds a new attribute
to this Node

unsigned getNumAttributes() const ; // get the total number of
attributes registered
<generic_attribute> getAttribute(unsigned i) ; // get the i'th
attribute

private:

vector< <generic_attribute> > listOfAttributes;
}

Where <generic_attribute> can be any class.

This kind of thing often results from a kind of
..NET/Java/scripting-language mindset. C++ doesn't fit this pattern,
and you may well need to learn different design patterns, depending on
exactly what you're trying to achieve. That said...


Everything depends on what you mean by "any class".

If the classes all share a base class, you save base-class pointers in
the std::vector. Ideally, you ensure the base class has a sufficiently
large set of (presumably abstract) methods so that you can call
base-class methods rather than manually determining the run-time type.
This is by far the most common case. Common errors include failing to
define a virtual destructor for the base class, and failing to
consider whether the Node class is responsible for deleting the
instances referenced in the std::vector - does it "own" those
instances, or is it just referencing them.

That is...

class Attrib_Base_T
{
public:
virtual ~Attrib_base_T ();
// NEVER miss this destructor off unless you
// REALLY know what you're doing

virtual Attrib_Base_T* Copy () const = 0;
// Explained later

...
};

// Derived attribute classes...

class Node_T
{
public:
~Node_T ();

void addNewAttribute(Attrib_Base_T* p);

Attrib_Base_T* getAttribute (size_t i);

private:

typedef std::vector<Attrib_Base_T*> Attribs_T;

Attribs_T m_Attribs;
};

void Node_T::addNewAttribute(Attrib_Base_T* p)
{
m_Attribs.push_back (p->Copy ());
// If Node owns the instances, it probably wants
// copies - can't use a copy-constructor due to
// the run-time type being unknown, so use a virtual
// "Copy" method.
}

Node_T::~Node_T () // If responsible for deleting items
{
for (Attribs_T::iterator i = m_Attribs.begin ();
i != m_Attribs.end (); ++i ) delete (*i);
}

This isn't a stupid interpretation of "any class" - remember, you can
create of heirarchy of wrapper classes if working with unrelated
classes defined outside your control. The wrappers basically resolve
all the which-kind-of-attribute-am-I-dealing-with issues, ideally by
defining a common interface in the base class for the wrappers.

You might use std::auto_ptr in place of pointers, but beware of the
freaky assignment operator.

If you really mean *any* class, you can store void* pointers, but you
take on some awkward responsibilities and you have more restrictions.
For example, if your node class is responsible for deleting those
instances, you'll need some way of identifying which classes
destructor to call for each instance. Similarly, how do you know which
classes Copy method to call?

If pointers really aren't appropriate, there are ways, but they really
aren't a good idea. The obvious place to look is the union, but you'd
probably want an "unrestricted union" that won't be available until
C++0x.

http://en.wikipedia.org/wiki/C++0x#Unrestricted_unions

And what you *really* need may well well be a "variant record", but
C++ doesn't have that construct. There *may* be a suitable boost
library, but I'd be surprised if it's worth the effort given the
current state of C++.

Just possibly, what you want is the "reflection" ability of scripting
languages (along with languages designed for the Java and .NET virtual
machines) to identify any instances class (via a reference) at
run-time. C++ cannot do that - it isn't a scripting language, and
doesn't have the scripting language overheads that allow reflection.
However, it is worth considering that internally, what scripting
languages do is essentially to use a either a heirarchy of wrapper
classes or variant-record like structures for most/all values.
 
J

John

Everything depends on what you mean by "any class".
I mean exactly that. The algorithms that use my Tree data structure
should be allowed to store any attribute from basic data types
(int,double,...) to composite types (STL, user-defined classes).

I don't want to use void*, as you mentioned, since I like to keep the
type-safety offered by C++. I found something similar to what I want
in the Boost library (http://boost.org), called dynamic_properties &
property_map. They allow generic types to be associated with Nodes/
Edges of the Boost Graph library. I guess I might have to use these
Boost components or write something similar. But generic programming
concepts are something new to me. Anybody have a good book to suggest
about generic programming for C++?
Regards
John
 
S

Stephen Horne

Anybody have a good book to suggest
about generic programming for C++?

http://en.wikipedia.org/wiki/Modern_C++_Design

I haven't actually read it all myself - I *think* I got some ideas
from a borrowed copy once - but it gets recommended a lot.

My impression, though, is that what you need is 'boxing' of basic
types. You should only need a fairly simple wrapper template.

class my_boxing_base_t
{
public:
virtual ~my_boxing_base_t ();

// pure virtual functions here
};

template<typename T> class my_boxed_t
{
private:
T m_Value;

public:
...
};

The real trick is designing the interface - the abstract methods in
the base class. If you want to do full arithmetic without figuring out
the run-time types, for instance, you'll need to cope with the double
dispatch problem, among other things.
 
Z

Zachary Turner

Thanks! I think Boost::Any is what I am looking for.

Regards
John

Definitely. boost is very daunting and has a big learning curve, but
by starting simple you can slowly work your way toward proficiency
with boost. So many things become easier and more pleasant once you
become comfortable with boost. variants are sort of a compile-time
type-safe substitute for virtual functions, inheritance, and abstract
interfaces, whereas boost::any is a type-safe analog to void*. At its
most basic, you can use boost::any as follows:

some_udt foo(3, 7.5, "hello");
boost::any an_int = (int)7;
boost::any a_udt = foo;

int some_int = boost::any_cast<int>(an_int);
float some_float = boost::any_cast<float>(an_int); //throws an
exception, invalid type cast
some_udt udt = boost::any_cast<some_udt>(a_udt);

To get the item out of the boost::any you need to know what type was
put into it, but that's the case with void* as well.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top