Differentiating inherited objects
I'm in over my head on this project I'm working on, and it's been driving me crazy for the past few days. I'll try to explain this as best as I can, without being too vague.
This project relies on multiple data types, each of which is encapsulated in its own class. Although these data objects are all different, they are all of the same base type, which is not a valid type by itself, so it will likely be an abstract base class (although I don't know if I can make it an ABC.)
At some point during the program, I will be instantiating different derived types based on user input, and these will be assigned to a pointer of the base class. This is necessary due to the mechanism I'm using to keep track of these objects, and work with them. I need some way to diferentiate the derived types, since the only way I can reference them is through a pointer of the base class.
I tried something as follows:
Code:
// base.hpp
enum EDataType { UnknownType, TypeA, TypeB };
class Base
{
public:
EDataType GetDataType() { return _dataType; }
protected:
EDataType _dataType;
};
// typea.hpp
#include "base.hpp"
class DerivedA : public Base
{
public:
DerivedA() { _dataType = TypeA; }
// ...more stuff here...
};
// typeb.hpp
#include "base.hpp"
class DerivedB : public Base
{
public:
DerivedB() { _dataType = TypeB; }
// ...more stuff here...
};
Now there are several problems with this approach.
1) "Base" is not actually an abstract base class, so there's nothing preventing the user from instantiating it if they want to.
2) This is not easily extensible. In order to add new types, I would have to manually expand the enumeration in the base file, since C++ does not support enumeration inheritance/extension.
I thought I'd try a different approach: each class will have an abstract GetType() function which will return some unique identifier, possibly a string, as follows:
Code:
// base.hpp
class Base
{
public:
virtual static string GetType() = 0;
};
The idea here being is that now Base is an ABC, and all derived classes will implement GetType() and return a unique identifier. But of course I cannot declare a function to be both virtual and static.
So I'm kind of stuck. Ultimately I need a way to differentiate between classes, as my code is something as follows:
Code:
Base* data;
.......
if (dataOne->GetType() == /* something here */)
{
....
}
else if (dataOne->GetType() == /* etc */)
{
...
}
As I mentioned at the beginning, I'm using pointers to the base class due to the way I'm passing data around. If I need to change around the architecture of my program to make this easier I'd be willing to do so.
Can I just use RTTI? I've read about checking the result of dynamic_cast, but honestly that seems a bit clunky. There must be an easy way of doing this, by checking the return of some identifier function in each class.
Re: Differentiating inherited objects
Quote:
Originally Posted by
JovianGhost
I need some way to diferentiate the derived types
The question to you is why do you need to differentiate between types? Doing so indicates a design flaw.
Very few designs require outright knowledge of derived types. The reason being that if you add another type, you defeat the whole purpose of polymorphism (not having to change code to accommodate the new type).
So why exactly do you need to know the derived types? Once you tell us that, then there will probably be alternatives (observer pattern or some other pattern) that accomplishes your goal.
Regards,
Paul McKenzie
Re: Differentiating inherited objects
Quote:
Originally Posted by
Paul McKenzie
The question to you is why do you need to differentiate between types? Doing so indicates a design flaw.
The reason being that each type contains its own unique internal structure, and therefore need to be processed as such.
This program is a converter. It converts between two different datatypes, so depending on what the datatypes are, the conversion happens in a different way.
I have a class called Converter, which specifies two members of type Base*, as follows...
Code:
class Converter
{
public:
bool Convert();
Base *sourceData, *targetData;
};
Each of the two public member variabels sourceData and targetData are set to point to the datasets, then the function Convert will handle the rest. It contains logic on how to perform the conversion depending on the types of sourceData and targetData. Because this program will support conversion between many different data types (which I should add, are more than just a simple value, but may consist of a complex data structure) I figured this would be the easiest way to do it. That way I don't have to write overloaded Convert() functions for every possible combination of data types.
Re: Differentiating inherited objects
Hm, my last post didn't go through, so I'll post again.
I'm writing a conversion program, that will convert between different data types. In the example I gave, I only showed two, but in reality there will be many more datatypes. Each of these datatypes is more than just a simple value; some can be complex data containers.
In order to facilitate conversion between different datatypes, I wrote a class as follows:
Code:
class Converter
{
public:
bool Convert(); // Perform the conversion in this function
Base *sourceData, *destData;
};
Then what I do is assigned sourceData and destData to the source and destination classes, respectively, and call the Convert function. This function will then decide how to perform the conversion based on the types of sourceData and destData. I figured this is easier than writing overloaded functions for every possibly pair of data types (of which there will be many.)
If you can suggest a different/better architecture, I'm all ears.
Re: Differentiating inherited objects
Quote:
Originally Posted by
JovianGhost
Code:
class Converter
{
public:
bool Convert(); // Perform the conversion in this function
Base *sourceData, *destData;
};
Both sourceData and destData will point to Base objects. So what are you really trying to convert? I thnk you should show more detail, as converting two types that are derived from the same parent on the surface seems amiss to me.
Usually conversion means taking two distinct things and changing one into the other. For example, converting an integer to a double, a non-wide string to a wide-string, etc. where both entities share no base class (their behaviour may be alike, but still different types. For example std::vector<T> where vector has the same operations, regardless of T).
Also conversion has to make logical, real world sense. You wouldn't try to convert a car into an insect. So again, you should give more detail as what you're trying to convert, what properties are converted to the other, etc.
And in any event, you have to write all of those different combinations of code anyway, given your description.
Regards,
Paul McKenzie
Re: Differentiating inherited objects
I don't have much detail, but can't you make convert_to part of the base interface? Or at least, code it in terms of the base's interface?
The idea of polymorphism is that you are not supposed to know the type you are working on. For example, suppose "convert_to" is part of your interface, and you give me a base, to convert. I'd just call "myBase->convert", and not give a flying **** of myBase's actual type.
Re: Differentiating inherited objects
Why not create a set of overloaded 'convert' functions, one for each pair of types. New types would just require a few new 'convert' overloads. Whether they were related by a base class or not would be irrelevant.
EDIT:
Or are you only given pointers to the base to work with?
Re: Differentiating inherited objects
Thanks for the replies everyone.
Quote:
Originally Posted by
Paul McKenzie
Both sourceData and destData will point to Base objects. So what are you really trying to convert? I thnk you should show more detail, as converting two types that are derived from the same parent on the surface seems amiss to me.
As I mentioned earlier, each derived class is a complex container with data in it. When I specify both the source and destination, Convert() will figure out what to do. Because use of the nature of the data, conversions can be quite complex, sometimes converting to another intermediate type.
Quote:
And in any event, you have to write all of those different combinations of code anyway, given your description.
Yes, but I can greatly reduce the number of combination by performing the conversion through an intermediate type, which will be different than the two types specified. So I do not have to (and prefer not to) write a Convert() for every pair, because if I have N coordinate types and add a new one, I'm going to have to write N more overloaded Convert() functions, most of which will essentially all do the same thing (convert through another intermediate type.)
Quote:
Originally Posted by
monarch_dodra
I don't have much detail, but can't you make convert_to part of the base interface? Or at least, code it in terms of the base's interface?
The idea of polymorphism is that you are not supposed to know the type you are working on. For example, suppose "convert_to" is part of your interface, and you give me a base, to convert. I'd just call "myBase->convert", and not give a flying **** of myBase's actual type.
I would still need to know what the types are, because I have to handle them in a specific way, given a specific type. So whether the Convert() function is part of the base class, or a member of a different class, I'm still stuck with the same problem.
[QUOTE=JohnW@Wessex;1972047]Why not create a set of overloaded 'convert' functions, one for each pair of types. New types would just require a few new 'convert' overloads. Whether they were related by a base class or not would be irrelevant.[quote]
As I mentioned to Paul above: if I have N types, and I want to add another one, then I have to write N more Convert() functions, all of which will be doing almost the exact same thing.
Quote:
EDIT:
Or are you only given pointers to the base to work with?
I only used pointers because the idea was that Base would be an abstract data type, since I do not want the ability to instantiate it, since it's a very generic type of the derived types. Although at the moment the only reason I even have Base* is so I have one single type of class that can be specified as members in the Converter class. So I don't NEED it to be an ABC.
I'll try to give more info.
I have, for example, something as follows:
Code:
// base.hpp
class Base
{
// generic stuff here, maybe nothing, as this class may just be a placeholder
};
// typea.hpp
#include "base.hpp"
class DerivedA : public Base
{
public:
double val1, val2, val3;
QString val4;
};
// typeb.hpp
#include "base.hpp"
#include "foo.hpp"
class DerivedB : public Base
{
public:
double val5, val6, val7, val8, val9, val10;
unsigned int val11;
Foo val12;
};
This is just an example, but represents the gist of what I have.
At first glance it looks like the classes are very similar, but trust me when I say they need to be treated very differently. Each class is a complex data container, and what I'm doing is taking the values in one class (sourceData), calculating appropriate values for the other class (targetData) and populating the members in targetData accordingly.
I do not want to write a ConvertTo() function for each derived type, because then this program because more complex: every time I add a new data type, I have to open up each of the derived types and add a ConvertTo() function for them. This can get confusing, which is why I wanted to keep all the convert routines separate, in a Converter class, so these types are merely data containers and nothing else.
My idea is that I start off with 2 data types, then when I add a third one, I just create the class structure, inheriting from Base, then I open up my Convert function and add the logic in there. Same for other classes: the class itself is just a data container, and the Convert function is where all the magic happens.
As I mentioned before, most conversions will go through an intermediate type, for example:
TypeA <-> Type B
TypeC (future type) -> TypeA ->TypeB
TypeB -> TypeA -> TypeD (future type)
Now this is not exactly how it may work, but it's the gist of it.
I hope I'm being clear. This is my first real, big project and I would really like to get it working.
Re: Differentiating inherited objects
Code:
class intermediate_type;
class base_type
{
virtual convert_to_intermediate(intermediate_type&) const;
virtual convert_from_intermediate(const intermediate_type&);
};
void convert(const base_type& in, base_type& out)
{
intermediate_type inter;
in.convert_to_intermediate(inter); //virtual call
out.convert_from_intermediate(inter); //virtual call
}
Something like this? Now, every time you add a class, you just have to override 2 functions.
Re: Differentiating inherited objects
Quote:
Originally Posted by
JovianGhost
Yes, but I can greatly reduce the number of combination by performing the conversion through an intermediate type, which will be different than the two types specified.
Will it be the same intermediate type for each?
Here, let me show you a design I made for converting between different encodings of Unicode. Any std::string I assume is UTF-8, any icu::UnicodeString I assume is UTF-16, and any std::vector<int> I assume is UTF-32.
I didn't want to write 9 different conversions, so I intermediately converted everything to UTF-16. Not the most efficient approach, but it cut down on code.
Code:
typedef boost::variant<std::string*, UnicodeString*, std::vector<CodePoint>* > UnicodeValue;
typedef boost::variant<const std::string*, const UnicodeString*, const std::vector<CodePoint>* > UnicodeConstValue;
struct UnicodeInput
{
UnicodeInput(const std::string &utf8)
: val(&utf8) {}
UnicodeInput(const UnicodeString &utf16)
: val(&utf16) {}
UnicodeInput(const std::vector<CodePoint> &utf32)
: val(&utf32) {}
const UnicodeConstValue& variant() const
{ return val; }
private:
UnicodeConstValue val;
};
struct UnicodeOutput
{
UnicodeOutput(std::string &utf8)
: val(&utf8) {}
UnicodeOutput(UnicodeString &utf16)
: val(&utf16) {}
UnicodeOutput(std::vector<CodePoint> &utf32)
: val(&utf32) {}
UnicodeValue& variant()
{ return val; }
private:
UnicodeValue val;
};
inline UnicodeString UTF8toUnicodeString(const char *encoding, int len = -1)
{
if (len < 0)
len = (int)strlen(encoding);
UErrorCode err = U_ZERO_ERROR;
UConverter* conv = ucnv_open("UTF8",&err);
UnicodeString str(encoding,len,conv,err);
ucnv_close(conv);
return str;
}
inline UnicodeString UTF8toUnicodeString(const std::string &encoding)
{
return UTF8toUnicodeString(encoding.c_str(),static_cast<int>(encoding.length()));
}
inline std::string UnicodeStringtoUTF8(const UnicodeString &str)
{
std::string utf8str;
//const UChar *buf = str.getBuffer();
char buffer[5];
int i;
for (i = 0; i < str.length(); i++)
{
putUTF8(buffer,str[i]);
utf8str += buffer;
}
return utf8str;
}
inline std::vector<CodePoint> UnicodeStringtoUTF32(const UnicodeString &utf16)
{
std::vector<CodePoint> utf32;
utf32.reserve(utf16.length());
for (int i = 0; i < utf16.length(); i++)
{
if (i == utf16.getChar32Start(i))
utf32.push_back(utf16.char32At(i));
}
if (!utf32.empty() && utf32.back() == 0)
utf32.pop_back();
return utf32;
}
inline UnicodeString UTF32toUnicodeString(const std::vector<CodePoint> &utf32)
{
UnicodeString utf16;
for (unsigned int i = 0; i < utf32.size(); i++)
{
utf16 += utf32[i];
}
return utf16;
}
class ToUnicodeString:
public boost::static_visitor<UnicodeString>
{
public:
UnicodeString operator()(const UnicodeString *utf16) const
{ return *utf16; }
UnicodeString operator()(const std::string *utf8) const
{ return UTF8toUnicodeString(*utf8); }
UnicodeString operator()(const std::vector<CodePoint> *utf32) const
{ return UTF32toUnicodeString(*utf32); }
};
class FromUnicodeString:
public boost::static_visitor<>
{
const UnicodeString *utf16;
public:
FromUnicodeString(const UnicodeString &utf16_str)
: utf16(&utf16_str) {}
void operator()(std::string *utf8_out) const
{ *utf8_out = UnicodeStringtoUTF8(*utf16); }
void operator()(UnicodeString *utf16_out) const
{ *utf16_out = *utf16; }
void operator()(std::vector<CodePoint> *utf32_out) const
{ *utf32_out = UnicodeStringtoUTF32(*utf16); }
};
inline void convertUnicode(UnicodeInput in, UnicodeOutput out)
{
boost::apply_visitor(FromUnicodeString(boost::apply_visitor(ToUnicodeString(),in.variant())),out.variant());
}
Re: Differentiating inherited objects
Quote:
Originally Posted by
monarch_dodra
Code:
class intermediate_type;
class base_type
{
virtual convert_to_intermediate(intermediate_type&) const;
virtual convert_from_intermediate(const intermediate_type&);
};
void convert(const base_type& in, base_type& out)
{
intermediate_type inter;
in.convert_to_intermediate(inter); //virtual call
out.convert_from_intermediate(inter); //virtual call
}
Something like this? Now, every time you add a class, you just have to override 2 functions.
Problem with that is that I may not always convert to the intermediate class. I may go directly from one class to another.
Quote:
Originally Posted by
Lindley
Will it be the same intermediate type for each?
No, not always.
Quote:
Here, let me show you a design I made for converting between different encodings of Unicode. Any std::string I assume is UTF-8, any icu::UnicodeString I assume is UTF-16, and any std::vector<int> I assume is UTF-32.
I didn't want to write 9 different conversions, so I intermediately converted everything to UTF-16. Not the most efficient approach, but it cut down on code.
<snip>
That will work assuming you're always converting everything to UTF-16. I may not always be converting everything to the intermediate type, as I mentioned above. In fact, I may have more than one intermediate type, and even more than one conversion step. It might become complicated depending on the source and destination data types, which is why I would prefer to let a separate converter class handle it, rather than writing an implicit conversion routine for each data type, or even the base type.
One idea I had was to have each datatype return a unique identifier, as follows:
Code:
// base.hpp
class Base
{
public:
virtual QString GetType() = 0;
};
// typea.hpp
#include "base.hpp"
class DerivedA : public Base
{
public:
QString GetType() { return "DERIVED_A"; }
};
// typeb.hpp
#include "base.hpp"
class DerivedB : public Base
{
public:
QString GetType() { return "DERIVED_B"; }
};
The problem here is that it's not robust; I'll be doing string comparisons which is prone to error.
So I thought I might have a static function that returns the same type, as follows:
Code:
class DerivedA : public Base
{
public:
QString GetType() { return "DERIVED_B"; }
static QString ThisType() { return "DERIVED_B"; }
};
This way in my code I can just write
Code:
if (sourceData->GetType() == DerivedA::ThisType())
But this method is also a bit clunky, and would require each new type to implement the ThisType() function. Not elegant at all.
Re: Differentiating inherited objects
I assume this is all a part of the implementation detail and that such a rare need is for your own purpose only, otherwise not only you're forcing your user to know the guts of what is supposed to be hidden, you're defeating the whole purpose of polymorphism and encapsulation.
One way you might want to look at is using macro. This short macro example uses the class name as the identifier and you don't need to inherit if you don't want to.
[code]
Code:
#include <iostream>
#include <string>
namespace demo
{
std::ostream& os = std::cout;
struct CGObject
{
// ...
explicit
CGObject(const std::string& n)
: name(n)
{
}
// some func
bool operator()(const CGObject& other) const
{
return name == other.name;
}
// id type
const std::string name;
};
#define BEGIN_STRUCT_(name) struct name : public CGObject { \
name() : CGObject( #name ) {}
#define END_STRUCT };
BEGIN_STRUCT_(How) /* some func... */ END_STRUCT
BEGIN_STRUCT_(Wow) END_STRUCT
}
int main()
{
using namespace demo;
How how, cow;
Wow wow;
os << how(cow) << cow(wow);
}
I find using macro this way more useful than the RTTI provided by the language when I layout a set of routines but this is only done in the pre-testing environment and prototyping and generalizing a complex hierarchy only. I repeat, only for prototyping!
Re: Differentiating inherited objects
Quote:
Originally Posted by
JovianGhost
So I'm kind of stuck. Ultimately I need a way to differentiate between classes, as my code is something as follows:
Code:
Base* data;
.......
if (dataOne->GetType() == /* something here */)
{
....
}
else if (dataOne->GetType() == /* etc */)
{
...
}
This is the kind of code you want to avoid in OO programming. It breaks the so called Open/Closed principle which states that when a new subtype is added you shouldn't have to modify existing code in numerous places.
Fortunately your design situation is quite common so there's a design pattern called Visitor you can use to handle it. Visitor builds on a mechanism called a double dispatch. Some languages even support it natively but not C++. It's not that intuitive so it takes a while to get used to it.
The double dispatch is based on the fact that a subtype itself knows what type it is. Instead of asking a subtype object what type it is you pass in a so called visitor object which allows the subtype object to reveal its type by calling a specific method in the visitor object. Each subtype object will call a different method.
One could say that the Visitor design pattern is the OO way of avoiding not typesafe selection based on subtype and downcasting.
Here's an example,
Code:
class Visitor { // base class of visitor objects
public:
virtual ~Visitor(){}
virtual void visit(class DerivedA*) =0;
virtual void visit(class DerivedB*) =0;
};
//
class Base {
public:
virtual ~Base(){}
virtual void accept(Visitor*) =0; // double dispatch method accepting a visitor object
};
//
class DerivedA : public Base {
public:
virtual void accept(Visitor* v) { // double dispatch allows DerivedA to reveal its type
v->visit(this); // calls the visit method associated with DerivedA
}
};
//
class DerivedB : public Base {
public:
virtual void accept(Visitor* v) { // double dispatch allows DerivedB to reveal its type
v->visit(this); // calls the visit method associated with DerivedB
}
}
// define concrete Visitor object (there can be many different)
class Visitor_1 : public Visitor {
public:
virtual void visit(DerivedA* d) {
/ d points to a DerivedA object - do something based on that
}
virtual void visit(DerivedB* d) {
/ d points to a DerivedB object - do something based on that
}
};
// usage
Visitor* v = new Visitor_1(); // concrete visitor object
Base* b = .........; // unknown object derived from Base
//
b->accept(v); // no if-else chain or switch necessary to reveal the subtype of b
Re: Differentiating inherited objects
Quote:
Originally Posted by
JovianGhost
That will work assuming you're always converting everything to UTF-16. I may not always be converting everything to the intermediate type, as I mentioned above. In fact, I may have more than one intermediate type, and even more than one conversion step.
Well, in my design boost::variants are used for the input and output, while a concrete type (icu::UnicodeString) is used for the intermediate type. But there's no reason why one couldn't use a *different* variant for the first intermediate type, and then pass that on to another object in a similar manner.....it would take careful diagramming of each possible sequence of type conversions, but the same basic principle could be applied to a much more complex case. All you have to do is decide where the input stops doing "downward" type conversions and the output starts doing "upward" type conversions. It doesn't necessarily need to be a funnel through a single type, but it would be nice if at some point the funnel got fairly narrow.
If you got tired of typing out all those variants, you could use boost::any instead. That sacrifices compile-time type checking in favor of runtime checking, though. (Not that variant compile errors are particularly easy to diagnose anyway.....they're some of the most verbose template errors I've ever seen.)
Of course, you could implement a similar idea in a totally different manner. The main point is that you should find a minimal spanning tree or forest through all the various types. Then converting a type to any type already in the tree will allow you to reach any other type in the same tree (though perhaps additional routes through the tree may be added for efficiency in some cases).
Re: Differentiating inherited objects
Quote:
Originally Posted by
nuzzle
This is the kind of code you want to avoid in OO programming. It breaks the so called Open/Closed principle which states that when a new subtype is added you shouldn't have to modify existing code in numerous places.
That's exactly what I want to avoid, which is why my original idea wasn't a very good one.
Quote:
Fortunately your design situation is quite common so there's a design pattern called Visitor you can use to handle it. Visitor builds on a mechanism called a double dispatch. Some languages even support it natively but not C++. It's not that intuitive so it takes a while to get used to it.
The double dispatch is based on the fact that a subtype itself knows what type it is. Instead of asking a subtype object what type it is you pass in a so called visitor object which allows the subtype object to reveal its type by calling a specific method in the visitor object. Each subtype object will call a different method.
One could say that the Visitor design pattern is the OO way of avoiding not typesafe selection based on subtype and downcasting.
Here's an example,
<snip>
I don't see the advantage in that; it still looks like I have to expand the Visitor_1 class every time I need a new subtype. Or maybe I'm just not understanding the concept.