469,071 Members | 1,914 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,071 developers. It's quick & easy.

Heterogeneous Container: avoid ifs and casts?

I defined a base class in order to put heterogeneous values into a
standard container: All values that I store in the container are
derived from my base class.

Now when I iterate over the elements of the container and want my
original values back - can I avoid excessive casting and if
statements? (see my sample code below.) Can I make it more elegant?

#include <iostream>
#include <vector>
using namespace std;

class Value{
public:
virtual ~Value(){}
};

template< typename T >
class SpecVal: public Value{
private:
T m_t;
public:
SpecVal (T const& t = T()): m_t(t) {}
T &getValue() {return m_t;}
};

void processString(const string &s){
cout << "String: " << s << endl;
}

void processFloat(const float &f){
cout << "Float: " << f << endl;
}

int main(){
vector<Value *> v;
v.push_back(new SpecVal<float>(3.5));
v.push_back(new SpecVal<string>("test"));
for(vector<Value *>::iterator i = v.begin(); i != v.end(); ++i){
if(SpecVal<float>* f = dynamic_cast<SpecVal<float>*>(*i)){
processFloat(f->getValue());
}else if(SpecVal<string>* s = dynamic_cast<SpecVal<string>*>(*i)){
processString(s->getValue());
}
delete *i;
}
}
Jul 22 '05 #1
8 2204
Markus Dehmann wrote:
I defined a base class in order to put heterogeneous values into a
standard container: All values that I store in the container are
derived from my base class. Now when I iterate over the elements of the container and want my
original values back - can I avoid excessive casting and if
statements? (see my sample code below.) Can I make it more elegant?
Generally, you need to read news:comp.object 's endless threads about
downcasting.

Specifically...
class Value{
public:
virtual ~Value(){}
virtual void stream(ostream & o) const = 0;
};


ostream & operator<<(ostream &o, Value const & val)
{
val.stream(o);
return o;
}

Now provide a custom implementation of stream() for each derived type.

The point of OO programming in general is to push interfaces up into
abstract class, and implementations down into concrete classes (and out into
delegates). So anything you plan to do generically to all Values deserves a
virtual method in the base class.

--
Phlip
http://industrialxp.org/community/bi...UserInterfaces
Jul 22 '05 #2
Markus Dehmann wrote:
I defined a base class in order to put heterogeneous values into a
standard container: All values that I store in the container are
derived from my base class.

Now when I iterate over the elements of the container and want my
original values back - can I avoid excessive casting and if
statements? (see my sample code below.) Can I make it more elegant?
The only way to make it more elegant is to define polymorphically
accessible interface and use it. I realise that it's not always
possible with straight types like 'int' and 'string', but do you
really expect so different types like your 'SpecVal<>', to somehow
usefully co-exist in the same container?

Depending on the purpose of this exercise, you might consider these
couple of opportunities. First solution, store the "type information"
in the class itself, so that you don't use the 'dynamic_cast<
SpecVal<string>* >()' ugliness, but instead use, say,

switch (i->getType()) {
case TYPE_STRING:
...
case TYPE_INT:

and so on. Of course, you will limit yourself to the number of
pre-defined type values and will have to let the base class know
what the derived classes are up to, which is against some principles
of OOD. Second, you could define 'toString' member in the 'Value'
class and always make your derived classes convert their stored
values to a string. That way you keep the straight interface, but
you incur the penalty of converting the values back and forth.

In any case, without knowing what precisely you're going to do with
the stored values, it's rather impossible to give perfect advice,
only guesses.

#include <iostream>
#include <vector>
using namespace std;

class Value{
public:
virtual ~Value(){}
};

template< typename T >
class SpecVal: public Value{
private:
T m_t;
public:
SpecVal (T const& t = T()): m_t(t) {}
T &getValue() {return m_t;}
};

void processString(const string &s){
cout << "String: " << s << endl;
}

void processFloat(const float &f){
cout << "Float: " << f << endl;
}

int main(){
vector<Value *> v;
v.push_back(new SpecVal<float>(3.5));
v.push_back(new SpecVal<string>("test"));
for(vector<Value *>::iterator i = v.begin(); i != v.end(); ++i){
if(SpecVal<float>* f = dynamic_cast<SpecVal<float>*>(*i)){
processFloat(f->getValue());
}else if(SpecVal<string>* s = dynamic_cast<SpecVal<string>*>(*i)){
processString(s->getValue());
}
delete *i;
}
}

Victor
Jul 22 '05 #3
On Tue, 01 Jun 2004 19:19:50 GMT, Phlip <ph*******@yahoo.com> wrote:
Markus Dehmann wrote:
I defined a base class in order to put heterogeneous values into a
standard container: All values that I store in the container are
derived from my base class.

Now when I iterate over the elements of the container and want my
original values back - can I avoid excessive casting and if
statements? (see my sample code below.) Can I make it more elegant?


Generally, you need to read news:comp.object 's endless threads about
downcasting.

Specifically...
class Value{
public:
virtual ~Value(){}


virtual void stream(ostream & o) const = 0;
};


ostream & operator<<(ostream &o, Value const & val)
{
val.stream(o);
return o;
}

Now provide a custom implementation of stream() for each derived type.


Thanks. Actually, I don't want to do anything with streams. I really want
the original float or string etc value back. Insofar, my code example was
misleading:

void processFloat(const float &f){
cout << "Float: " << f << endl;
}

It should be sth like:
float processFloat(const float &f){
return f * 10.0; // do sth with float
}
Jul 22 '05 #4
"Markus Dehmann" <ma*******@gmx.de> wrote in
news:op**************@news.cis.dfn.de:
On Tue, 01 Jun 2004 19:19:50 GMT, Phlip <ph*******@yahoo.com> wrote:
Markus Dehmann wrote:
I defined a base class in order to put heterogeneous values into a
standard container: All values that I store in the container are
derived from my base class.

Now when I iterate over the elements of the container and want my
original values back - can I avoid excessive casting and if
statements? (see my sample code below.) Can I make it more elegant?


Generally, you need to read news:comp.object 's endless threads about
downcasting.

Specifically...
class Value{
public:
virtual ~Value(){}


virtual void stream(ostream & o) const = 0;
};


ostream & operator<<(ostream &o, Value const & val)
{
val.stream(o);
return o;
}

Now provide a custom implementation of stream() for each derived
type.


Thanks. Actually, I don't want to do anything with streams. I really
want the original float or string etc value back. Insofar, my code
example was misleading:

void processFloat(const float &f){
cout << "Float: " << f << endl;
}

It should be sth like:
float processFloat(const float &f){
return f * 10.0; // do sth with float
}


Did you consider using the visitor pattern?

E.g. (derived from original example)

#include <vector>
#include <string>
#include <iostream>
#include <algorithm>

using std::vector;
using std::string;
using std::ostream;
using std::cout;
using std::for_each;

// need a forward decl.
template <class T> class SpecVal;

// visitor ABC
class Visitor {
public:
virtual ~Visitor() { }
virtual void operator()(SpecVal<float>&) const = 0;
virtual void operator()(SpecVal<string>&) const = 0;
};

// value ABC
class Value {
public:
virtual ~Value() { }
virtual void apply(Visitor const&) = 0;
};

// concrete value
template <class T>
class SpecVal : public Value {
T m_t;
public:
SpecVal(T const& t = T()) : m_t(t) { }
T& get() { return m_t; }
virtual void apply(Visitor const& v) { v(*this); }
};

// visitor implementation
class PrintVisitor : public Visitor {
ostream& out;
public:
PrintVisitor(ostream& out) : out(out) { }
virtual void operator()(SpecVal<float>& val) const
{ cout << "float:" << val.get() << '\n'; }
virtual void operator()(SpecVal<string>& val) const
{ cout << "string:" << val.get() << '\n'; }
};
int main() {
vector<Value*> v;

v.push_back(new SpecVal<float>(3.5f));
v.push_back(new SpecVal<string>("test"));

PrintVisitor visitor(cout);

vector<Value*>::iterator it(v.begin());
for (; it != v.end(); ++it)
(*it)->apply(visitor);

return 0;
}

--
:: bartekd [at] o2 [dot] pl

Jul 22 '05 #5
In article <qF*****************@newssvr32.news.prodigy.com> ,
"Phlip" <ph*******@yahoo.com> wrote:
Markus Dehmann wrote:
I defined a base class in order to put heterogeneous values into a
standard container: All values that I store in the container are
derived from my base class.

Now when I iterate over the elements of the container and want my
original values back - can I avoid excessive casting and if
statements? (see my sample code below.) Can I make it more elegant?


Generally, you need to read news:comp.object 's endless threads about
downcasting.

Specifically...
class Value{
public:
virtual ~Value(){}


virtual void stream(ostream & o) const = 0;
};


ostream & operator<<(ostream &o, Value const & val)
{
val.stream(o);
return o;
}

Now provide a custom implementation of stream() for each derived type.

The point of OO programming in general is to push interfaces up into
abstract class, and implementations down into concrete classes (and out into
delegates). So anything you plan to do generically to all Values deserves a
virtual method in the base class.


I agree with Philip, however there is another alternitive in this
case... 'boost::any' <www.boost.org> is probably worth looking into.
Jul 22 '05 #6
On Tue, 01 Jun 2004 19:03:32 -0400, "Markus Dehmann"
<ma*******@gmx.de> wrote:
Thanks. Actually, I don't want to do anything with streams. I really want
the original float or string etc value back.


But you want the value back in order to do something with it. Ideally,
put the operations in the base class interface. You can also use the
likes of the visitor pattern if the set of operations you want to
perform on Value types is unknown, but the set of Value subclasses is
known an finite.

If you're putting these values in a container together, there must be
something that you want to do to all of them. Value as written has no
behaviour - it is little better than using void* really. Give it the
behaviour you need from Values. What operations will you perform on
them?

Tom
--
C++ FAQ: http://www.parashift.com/c++-faq-lite/
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Jul 22 '05 #7
On Tue, 01 Jun 2004 12:04:54 -0700, Markus Dehmann wrote:
I defined a base class in order to put heterogeneous values into a
standard container: All values that I store in the container are
derived from my base class.

Now when I iterate over the elements of the container and want my
original values back - can I avoid excessive casting and if
statements? (see my sample code below.) Can I make it more elegant?

#include <iostream>
#include <vector>
using namespace std;

class Value{
public:
virtual ~Value(){}
[quoted text muted]


template< typename T >
class SpecVal: public Value{
private:
T m_t;
public:
SpecVal (T const& t = T()): m_t(t) {}
T &getValue() {return m_t;}
[quoted text muted]


void processString(const string &s){
cout << "String: " << s << endl;
[quoted text muted]


void processFloat(const float &f){
cout << "Float: " << f << endl;
[quoted text muted]


int main(){
vector<Value *> v;
v.push_back(new SpecVal<float>(3.5));
v.push_back(new SpecVal<string>("test"));
for(vector<Value *>::iterator i = v.begin(); i != v.end(); ++i){
if(SpecVal<float>* f = dynamic_cast<SpecVal<float>*>(*i)){
processFloat(f->getValue());
}else if(SpecVal<string>* s = dynamic_cast<SpecVal<string>*>(*i)){
processString(s->getValue());
}
delete *i;
}
[quoted text muted]

How about this: implement an abstract class Value, with a
virtual void process() function. Then derive any type you need
from Value, and override the process() function. In more detail:

class Value
{
// private/protected members
public:
virtual void process() = 0;
// more public members.
};

class StringValue : public Value
{

public:
void process() { // Your processString() function here. };
};

class FloatValue : public Value
{

public:
void process() { // Your processFloat() function here. };
};

Then, in main()

vector<Value*> v;

// push back some Value* in v as needed;
// Initialize each one to the desired type:

v[0]=new StringValue("test");
v[1]=new FloatValue(3.5);

Then

v[0]->process(); // process string
v[1]->process(); // process float

That's the way I'd do it.

Jul 22 '05 #8
Typos, etc. corrected below.

bartek <sp******************@o2.pl> wrote in
news:Xn**********************************@153.19.2 51.200:
Did you consider using the visitor pattern?

E.g. (derived from original example)

#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
<algorithm> is not actually used...

using std::vector;
using std::string;
using std::ostream;
using std::cout;
using std::for_each;
std::for_each as well...
// need a forward decl.
template <class T> class SpecVal;

// visitor ABC
class Visitor {
public:
virtual ~Visitor() { }
virtual void operator()(SpecVal<float>&) const = 0;
virtual void operator()(SpecVal<string>&) const = 0;
};

// value ABC
class Value {
public:
virtual ~Value() { }
virtual void apply(Visitor const&) = 0;
};

// concrete value
template <class T>
class SpecVal : public Value {
T m_t;
public:
SpecVal(T const& t = T()) : m_t(t) { }
T& get() { return m_t; }
virtual void apply(Visitor const& v) { v(*this); }
};

// visitor implementation
class PrintVisitor : public Visitor {
ostream& out;
public:
PrintVisitor(ostream& out) : out(out) { }
virtual void operator()(SpecVal<float>& val) const
{ cout << "float:" << val.get() << '\n'; }
Of course, I made a typo in the above line ... should be:

{ out << "float:" << val.get() << '\n'; }

virtual void operator()(SpecVal<string>& val) const
{ cout << "string:" << val.get() << '\n'; }
Ditto...
{ out << "string:" << val.get() << '\n'; }
};
int main() {
vector<Value*> v;

v.push_back(new SpecVal<float>(3.5f));
v.push_back(new SpecVal<string>("test"));

PrintVisitor visitor(cout);

vector<Value*>::iterator it(v.begin());
for (; it != v.end(); ++it)
(*it)->apply(visitor);

return 0;
}


--
:: bartekd [at] o2 [dot] pl

Jul 22 '05 #9

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

1 post views Thread by kj | last post: by
16 posts views Thread by Sensei | last post: by
21 posts views Thread by aaragon | last post: by
2 posts views Thread by Arash Partow | last post: by
6 posts views Thread by rn5a | last post: by
7 posts views Thread by ademirzanetti | last post: by
1 post views Thread by CARIGAR | last post: by
reply views Thread by zhoujie | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.