I am looking for some design advices for the following problem:
I am using boost geometry, I have a couple of custom geometry types compatible with boost geometry (via traits), but most of the types I am using are typedefs.
class MyPoint
{
// custom stuff
};
// declare traits for MyPoint for use wih boost geometry here
class MyTaggedPoint : public MyPoint
{
// more custom stuff
};
// declare traits for MyTaggedPoint for use wih boost geometry here
// example typedefs
typedef boost::geometry::model::polygon<MyPoint> Polygon;
typedef boost::geometry::model::polygon<MyTaggedPoint> TaggedPolygon;
My problem is when I want to serialize/deserialize my geometries.
Let's say all geometries are stored in a binary field in a database. If I would have a base geometry class, I would probably just write g->type() (4 bytes) and call g->save(some_outputstream) and write all of that to the binary field. Then when reading the binary field I would simply read the bytes and cast to appropriate geometry type.
But Boost geometries do not have a common base class.
How do you guys usually approach serialization when there are multiple types that can be stored as binary and you do not have a shared base class ?
I was thinking of maybe having a Serializer class, that returns a boost.Any and then the geometry can be casted afterward with the type that would be stored in the (de)serializer? But then the serializer would need a save method for each geometry types ? ex: Save(myPolygon), Save(myPoint)
Any ideas/experiences?
Boost's serialization supports non-invasive serialization if you do not wish to reimplement the wheel. You may even be able to find library support for their geometry types somewhere. The interface is somewhat complicated due to XML concerns unfortunately.
To serialize objects to and from bytes, you ultimately need 2 functions for EACH type you have to support (primatives, objects, etc.). These are "Load()" and "Store()".
Ideally, you use a fixed interface for the bytes- a iostream, char*, some buffer object- etc.
For the sake of readability let's call it "ByteBuffer", since logically that's what its role is.
We now have something like template functions for the Serializable concept:
template<typename T>
ByteBuffer Store(const T& object) { // BUT, What goes here...? }
template<typename T>
T Load(const ByteBuffer& bytes);
Okay, this isn't going to work for anything other than the primitive types- even if we made these "visitors" or something they literally have to know every detail about the object's internals to do their job. Furthermore, "Load()" is logically a constructor (really, a FACTORY since it could easily fail). We've got to associate these with the actual objects.
To make Serializable a base class, we need to use the "curiously recurring template" pattern. To do this, we require all derived classes to have a constructor of the form:
T(const ByteBuffer& bytes);
To check for errors, we can provide a protected flag "valid" in the base class that derived constructors can set. Note that your object has to support factory-style construction anyway for Load() to work well with it.
Now we can do this right, providing "Load" as a factory:
template<typename T>
class Serializable // If you do reference-counting on files & such, you can add it here
{
protected:
bool valid;
// Require derived to mark as valid upon load
Serializable() : valid(false) {}
virtual ~Serializable() { valid = false; }
public:
static T Load(const ByteBuffer& bytes); // calls a "T(bytes)" constructor
// Store API
virtual ByteBuffer Store() = 0; // Interface details are up to you.
};
Now, just derive from the base class like so and you can pick up everything you need:
class MyObject : public Serializable<MyObject>
{
protected:
// .. some members ...
MyObject(const ByteBuffer& bytes)
{
//... Actual load logic for this object type ...
// On success only:
valid = true;
}
public:
virtual ByteBuffer Store() {
//... store logic
}
};
What's cool is that you can call "MyObject::Load()" and it'll do exactly what you expect. Futhermore, "Load" can be made into the ONLY way to build the object, allowing you clean APIs for read-only files and such.
Extending this to full File APIs takes a little more work, namely adding a "Load()" that can read from a larger buffer (holding other things) and "Store()" that appends to an existing buffer.
As a side note, do NOT use boost's APIs for this. In a good design serializable objects should map 1-to-1 to packed structures of primitive types on disk- that's the only way the resulting files are really going to be usable by other programs or on other machines. Boost gives you a horrible API that mostly enables you to do things you'll regret later.
Related
Wondering whether anyone can help identify a more elegant design approach - or potentially identifying shortcomings of the following design.
Currently, I have an abstract Response class that derives from a serializable JSON Object.
//objects.h
struct Object
{
[[nodiscard]] std::string serialize() const;
virtual void deserialize(const Poco::JSON::Object::Ptr &payload) = 0;
[[nodiscard]] virtual Poco::JSON::Object::Ptr to_json() const = 0;
};
// response.h
class Response : public Object
{
public:
std::unique_ptr<Data> data;
std::unique_ptr<Links> links;
};
Where both Data and Links member variables are abstract base classes - in which their respective set of subclasses contain various STL containers.
Now the problem I'm facing is one of class design - and how to avoid downcasting each member variable depending on the derived Response (and to identify a more clean hierarchy/design). For instance...
ResponseConcreteA response_a;
response_a.deserialize(object_a);
auto data_a = static_cast<DataConcreteA *>(response_a.data.get());
ResponseConcreteB response_b;
response_b.deserialize(object_b);
auto data_b = static_cast<DataConcreteB *>(response_b.data.get());
The seemingly obvious solution is to abandon polymorphic member variables and substitute them for the respective concrete types. However - my concern is that this is a deviation from the inherent relationship of a Response having Data & Links members which are each a particular polymorphic type.
One important thing to note is that the concrete types attributed to Data & Links are determined at compile time - there is no necessity for the derived classes to change at any point. There respective construction(s) is governed by the following preprocessed template:
#define DECLARE_RESPONSE_TYPE(type_name, data_name, links_name \
struct type_name final : public Response \
{ \
type_name() \
{ \
data.reset(new data_name()); \
links.reset(new links_name()); \
} \
~type_name() = default; \
void deserialize(const Poco::JSON::Object::Ptr &payload) override; \
Poco::JSON::Object::Ptr to_json() const override; \
};
Is there a more appropriate approach to avoid these polymorphic member variables in my design where constant downcasting is required (despite the fact that derived object pointed to is known at compile time). Thanks!
(I’m adapting one of my recent Reddit comments that answered basically the same question.)
in general
Don’t model serialization with inheritance! It’s a cross-cutting concern you want to attach to arbitrary types. Inheritance is the wrong tool for that. Some problems with the approach:
You force everything serializable to become a full-fledged polymorphic class with all the related overhead.
You need control over the type you want to serialize, which means you cannot use 3rd party types without wrapping them.
Because serialization is cross-cutting you’ll likely run into the inheritance diamond problem at some point.
Fundamental types cannot be serialized that way. You cannot make int derive from Serializable.
Pattern matching is a more flexible approach. In a nutshell, you template your serialization framework and depend on certain functions being available for serializable types. Quick, dirty and naive example:
struct Something {
// ...
};
// If these two functions exist a type has serialization support
void serialize(const Something&, SerializedDataStream&);
Something deserialize(SerializedDataStream&);
Now you can make anything serializable without touching the type at all. That’s vastly more flexible than inheritance, but probably makes the serialization framework somewhat trickier to implement. Additionally supporting (de)serialization member functions is a good idea for more complex types that need access to their private data to (de)serialize properly.
Have a look at Boost Serialization or Cereal for real world examples of the pattern matching approach.
in your particular situation
To serialize larger nested structures you split up the serialization functionality. Each type has to know how to serialize itself, but that’s as far as it goes. Serializing a complex member is delegated to that member, because it too has to know how to serialize itself. That way you build the final JSON step by step.
One important thing to note is that the concrete types attributed to Data & Links are determined at compile time
The obvious solution is to turn Response into a template.
template <typename Data, typename Links>
class Response // note: no more base class
{
public:
Data data;
Links links;
};
// externalized serialization functions
void serialize(const Response&, JSONDataStream&);
Response deserialize(JSONDataStream&);
That way you have the correct types available to find the correct overload of the serialization functions for Data and Links, and delegation boils down to simply call them. Whether that approach is feasible depends on the larger context. Retrofitting a template into a project that relies on polymorphism can lead to ripple effects throughout the whole code base. In other words, it can be a really expensive change.
The alternative is similar to what you’re already doing. Response itself still uses the pattern-matching approach to serialization. But you keep the polymorphism for Data and Links including the overriden virtual serialization functions. In each concrete derived type we’re back to the original idea of “each type knows how to serialize itself”. If the concrete Data and Links classes need to be serialized in other contexts (not as members of Response), too, implement the pattern-matching functions for them and call those from the overriden member functions. Otherwise serialization can happen directly in those member functions.
class Data
{
public:
virtual ~BaseData() = default;
void deserialize(const Poco::JSON::Object::Ptr &payload) = 0;
Poco::JSON::Object::Ptr to_json() const = 0;
//...
};
class ConcreteData
{
public:
~BaseData() override = default;
void deserialize(const Poco::JSON::Object::Ptr &payload)
{
// ...
}
Poco::JSON::Object::Ptr to_json() const
{
// ...
}
}
// ------
Poco::JSON::Object::Ptr Response::to_json() const
{
// ...
auto serializedData = data->to_json();
// ...
}
I have this problem again and again... and still have not a satisfactory answer...
Especially when I put the class into a container, later on I need to record more information on every element in the container during a specific processing, but after processing I do not need the extra information anymore....
I often found some libraries try to solve the above situation by defining a void* in their data structure to provide user-defined data structure extension. Just the same described in this Q&A.
But it produces memory / resource handling problem... and other problems that I feel this approach is error-prone.
In the modern day of object-oriented programming, I am thinking of
using inheritance & polymorphism. Use base class's pointer in the container, but then I have to add derived class's accessor into the base class. It is kind of strange...
is there any other better ways to extend a class's property while maintain container comparability in C++?
The best way to store extra data about a object without actually compromising the integrity of the object itself is to store a pair of data in the container instead.
struct User { ... };
struct ExtraData { ... };
typedef std::pair<User, ExtraData> UserAndExtraData;
Now I can create a container type in C++ which stores both pieces of information together without compromising the independence of either type.
std::vector<UserAndExtraData> vector;
I would look into the Decorator Pattern. You can decorate your objects while processing them then throw the decorated objects away. If there is a lot of shared data you can also look into the FlyWeight pattern.
"User" could be extended by template parameters. for example,
template <typename... Extra>
struct User : Extra...
{
...
};
struct ExtraData {...};
struct ExtraExtraData {...};
using ExtraUser = User<ExtraData>;
using MoreExtraUser = User<ExtraData, ExtraExtraData>;
In the modern day of object-oriented programming, I am thinking of
using inheritance & polymorphism. Use base class's pointer in the
container, but then I have to add derived class's accessor into the
base class. It is kind of stange...
you don't need to put a pointer to your derived class in your base class when using inheritance. You just need to cast to the derived class. the problem is getting your data into the derived objects when it's stored in the base objects - you can only cast them if they were created as the derived type, even if your collection holds them as the base type. (if they are created as the derived type, then just cast!)
So if you have a collection of BaseC, you can create a new class DerivedC that has a copy constructor that takes a BaseC. You can copy your BaseC object into it, perform your processing on the DerivedC objects and then copy these back into a BaseC object for storage. This uses the Flyweight pattern. Note that if you have a collection of BaseC objects, you cannot just pretend they are DerivedC classes as they will not have the storage to hold all the data members, you need to create new DerivedC objects.
Alternatively, create a new class just for processing that contains a (smart pointer) reference to your base class objects, copy the reference in, perform the processing, delete the processing objects when you're done.
If your objects are in a vector, then a simple approach is to make a parallel vector:
void doSomething(const vector<MyObject>& my_objects)
{
vector<ExtraData> extra_data;
int n_objects = extra_data.size();
extra_data.reserve(n_objects);
for (int i=0; i!=n_objects; ++i) {
extra_data.push_back(calcExtraData(my_objects[i]));
}
// now use my_objects[i] and extra_data[i] together.
// extra data goes away when the function returns.
}
You don't have to modify your original objects, and it is very efficient.
If you have some other container type, you can use a map:
void doSomething(const set<MyObject>& my_objects)
{
map<MyObject*,ExtraData> extra_data;
set<MyObject>::const_iterator i=my_objects.begin(), end=my_objects.end();
for (;i!=end;++i) {
extra_data[&*i] = calcExtraData(*i);
}
// now use extra_data[&obj] to access the extra data for obj.
// extra data goes away when the function returns.
}
this isn't as efficient as with vectors, but you still don't have to modify your original classes.
However, it becomes more difficult to maintain the parallel structures if the underlying container can change during the processing.
One simple option is to add a type parameter representing the "extra data"...
template<class ExtraDataType>
struct MyExtensibleContainer
{
...
ExtraDataType extra;
};
Perhaps if you indicate why this solution isn't sufficient, the true requirements will come through.
Example for int and void*:
struct IntOrVoid
{
};
struct IntOrVoid1 : IntOrVoid
{
int x;
};
struct IntOrVoid2 : IntOrVoid
{
void* x;
};
typedef shared_ptr<IntOrVoid> PIntOrVoid;
then use MyExtensibleContainer<PIntOrVoid>
or altenatively:
union IntOrVoid
{
int x_int;
void* x_voidp;
};
then use MyExtensibleContainer<IntOrVoid>
The problem you are describing has nothing to do with adding an "extra" data type. The problem you are describing has to do with holding a variant type that can have one of many hetrogeneous types. There are many ways to do this, it is a much more general problem.
My reason for asking the question:
I am using a large framework not of my own design. I need to use several "user information" classes which are unrelated as far as the code is concerned. They do not derive from any common base class, and I do not have access to the source code to recompile.
These information classes work like this: there are classes A, B, C, etc. These classes each have an information class, Ainfo, Binfo, etc. associated with them. Because the user (i.e. me) needs to attach different informations to a given object of a given class (meaning I might have two different classes deriving from Ainfo that I want to attach to an object of A), and there is only one information slot, I want to make an information object that can old other various information objects. That way, I can just add my information into this fake information-object-which-is-a-container-for-other-information-objects.
The problem arises in that I would like to do this for Ainfo, Binfo, Cinfo, Dinfo etc. So I would like to write a mixin or something that just adds the container functionality to any of the plain old info classes.
The problem is that the information classes Ainfo, Binfo, etc. require different constructor arguments.
So the question:
Is it possible to pass a vector of types into the constructor of the mixin? That way I could have a variable list of appropriate constructor parameters passed in? Can you assign a type to a variable outside of a template argument? Can you cast with this variable?
or
Is it possible to inherit from a specific object? Could I for example create a new Ainfo object using the correct constructor, then do the mixin on that specific object. This would be like the usage of the decorator pattern, except I have no common interface. (the object being decorated is the interface)
or
am I just going to have to bite the bullet and write 15,000 (exaggeration :) ) classes which are exactly the same, but inherit from a different base class and contain a different type of object?
Summary:
I need to add a container feature to several different classes while maintaining the interface of each class and utilizing their argument-taking constructors. I would like to not duplicate code.
Thanks in advance. Sorry for totally butchering terminology.
It sounds to me like what you want is a Boost.Variant. It's like a C++-style union. It is strongly typed (so that you always know what you actually stored in it), and it has a powerful visitation mechanism that makes it easy to map many different types to a single operation.
For example, you can do this:
typedef boost::variant<Ainfo, Binfo, Cinfo> CommonInfo;
//In a function.
CommonInfo someInfo = Ainfo();
You can then write visitor functors that can be used to call members of the info objects.
class DoThingInfoVisitor : boost::static_visitor<>
{
void operator()(Ainfo &info) {info.DoThing()}
void operator()(Binfo &info) {info.DoThing2()}
void operator()(Cinfo &info) {info.StepA(); info.StepB();}
};
Armed with this object, if you want to do whatever this DoThing means for any CommonInfo type:
CommonInfo someInfo = Ainfo();
boost::apply_visitor( times_two_visitor(), someInfo );
This will call the Ainfo version, since that's what happens to be stored in someInfo. If it had stored Binfo, then you could use that. You can build a suite of these visitors; they can return values, take parameters (though you'll need to store them in the functor), and various other tricks you can learn from the docs.
If it's not doable in templates, and you can't hack it with the preprocessor, then you're gonna have to do it by hand. C++ doesn't contain any type manipulation at run-time, typeid() and dynamic_cast is all you've got.
This may be a bit of an oversimplification, but if nothing else it should help to clarify your question. Using templates, you can easily generate classes that derive from your info classes. The following classes illustrate this concept.
class Ainfo {
std::string _a;
public:
void setContent(const std::string& A);
const char * print() const; // prints _a
};
class Binfo {
std::string _b;
public:
void setContent(const std::string& B);
const char * print() const; // prints _b
};
template<class Tinfo>
class Info : public Tinfo {
};
You could then use this template as follows.
Info<Ainfo> my_info;
my_info.setContent("test");
std::cout << my_info.print();
UPDATE: If you also want to override the template's constructor, try using a member template.
template<class Tinfo>
class Info : public Tinfo {
public:
template<typename arg>
Info(arg rhs) : Tinfo(rhs) { }
};
Using this, you can compile and run the following.
Info<Ainfo> my_info("Testing...");
std::cout << my_info.print();
I could be wrong, but I have a feeling that we're getting pretty close now...
I'm building an hierarchy of objects that wrap primitive types, e.g integers, booleans, floats etc, as well as container types like vectors, maps and sets. I'm trying to (be able to) build an arbitrary hierarchy of objects, and be able to set/get their values with ease. This hierarchy will be passed to another class (not mentioned here) and an interface will be created from this representation. This is the purpose of this hierarchy, to be able to create a GUI representation from these objects.To be more precise, i have something like this:
class ValObject
{
public:
virtual ~ValObject() {}
};
class Int : public ValObject
{
public:
Int(int v) : val(v) {}
void set_int(int v) { val = v);
int get_int() const { return val; }
private:
int val;
};
// other classes for floats, booleans, strings, etc
// ...
class Map : public ValObject {}
{
public:
void set_val_for_key(const string& key, ValObject* val);
ValObject* val_for_key(const string& key);
private:
map<string, ValObject*> keyvals;
};
// classes for other containers (vector and set) ...
The client, should be able to create and arbitrary hierarchy of objects, set and get their values with ease, and I, as a junior programmer, should learn how to correctly create the classes for something like this.
The main problem I'm facing is how to set/get the values through a pointer to the base class ValObject. At first, i thought i could just create lots of functions in the base class, like set_int, get_int, set_string, get_string, set_value_for_key, get_value_for_key, etc, and make them work only for the correct types. But then, i would have lots of cases where functions do nothing and just pollute my interface. My second thought was to create various proxy objects for setting and getting the various values, e.g
class ValObject
{
public:
virtual ~ValObject() {}
virtual IntProxy* create_int_proxy(); // <-- my proxy
};
class Int : public ValObject
{
public:
Int (int v) : val(v) {}
IntProxy* create_int_proxy() { return new IntProxy(&val); }
private:
int val;
};
class String : public ValObject
{
public:
String(const string& s) : val(s) {}
IntProxy* create_int_proxy() { return 0; }
private:
string val;
};
The client could then use this proxy to set and get the values of an Int through an ValObject:
ValObject *val = ... // some object
IntProxy *ipr = val->create_int_proxy();
assert(ipr); // we know that val is an Int (somehow)
ipr->set_val(17);
But with this design, i still have too many classes to declare and implement in the various subclasses. Is this the correct way to go ? Are there any alternatives ?
Thank you.
Take a look at boost::any and boost::variant for existing solutions. The closest to what you propose is boost::any, and the code is simple enough to read and understand even if you want to build your own solution for learning purposes --if you need the code, don't reinvent the wheel, use boost::any.
One of the beauties of C++ is that these kinds of intrusive solutions often aren't necessary, yet unfortunately we still see similar ones being implemented today. This is probably due to the prevalence of Java, .NET, and QT which follows these kinds of models where we have a general object base class which is inherited by almost everything.
By intrusive, what's meant is that the types being used have to be modified to work with the aggregate system (inheriting from a base object in this case). One of the problems with intrusive solutions (though sometimes appropriate) is that they require coupling these types with the system used to aggregate them: the types become dependent on the system. For PODs it is impossible to use intrusive solutions directly as we cannot change the interface of an int, e.g.: a wrapper becomes necessary. This is also true of types outside your control like the standard C++ library or boost. The result is that you end up spending a lot of time and effort manually creating wrappers to all kinds of things when such wrappers could have been easily generated in C++. It can also be very pessimistic on your code if the intrusive solution is uniformly applied even in cases where unnecessary and incurs a runtime/memory overhead.
With C++, a plethora of non-intrusive solutions are available at your fingertips, but this is especially true when we know that we can combine static polymorphism using templates with dynamic polymorphism using virtual functions. Basically we can generate these base object-derived wrappers with virtual functions on the fly only for the cases in which this solution is needed without pessimizing the cases where this isn't necessary.
As already suggested, boost::any is a great model for what you want to achieve. If you can use it directly, you should use it. If you can't (ex: if you are providing an SDK and cannot depend on third parties to have matching versions of boost), then look at the solution as a working example.
The basic idea of boost::any is to do something similar to what you are doing, only these wrappers are generated at compile-time. If you want to store an int in boost::any, the class will generate an int wrapper class which inherits from a base object that provides the virtual interface required to make any work at runtime.
The main problem I'm facing is how to
set/get the values through a pointer
to the base class ValObject. At first,
i thought i could just create lots of
functions in the base class, like
set_int, get_int, set_string,
get_string, set_value_for_key,
get_value_for_key, etc, and make them
work only for the correct types. But
then, i would have lots of cases where
functions do nothing and just pollute
my interface.
As you already correctly deduced, this would generally be an inferior design. One tell-tale sign of inheritance being used improperly is when you have a lot of base functions which are not applicable to your subclasses.
Consider the design of I/O streams. We don't have ostreams with functions like output_int, output_float, output_foo, etc. as being directly methods in ostream. Instead, we can overload operator<< to output any data type we want in a non-intrusive fashion. A similar solution can be achieved for your base type. Do you want to associate widgets with custom types (ex: custom property editor)? We can allow that:
shared_ptr<Widget> create_widget(const shared_ptr<int>& val);
shared_ptr<Widget> create_widget(const shared_ptr<float>& val);
shared_ptr<Widget> create_widget(const shared_ptr<Foo>& val);
// etc.
Do you want to serialize these objects? We can use a solution like I/O streams. If you are adapting your own solution like boost::any, it can expect such auxiliary functions to already be there with the type being stored (the virtual functions in the generated wrapper class can call create_widget(T), e.g.
If you cannot be this general, then provide some means of identifying the types being stored (a type ID, e.g.) and handle the getting/setting of various types appropriately in the client code based on this type ID. This way the client can see what's being stored and deal set/get values on it accordingly.
Anyway, it's up to you, but do consider a non-intrusive approach to this as it will generally be less problematic and a whole lot more flexible.
Use dynamic_cast to cast up the hierarchy. You don't need to provide an explicit interface for this - any reasonable C++ programmer can do that. If they can't do that, you could try enumerating the different types and creating an integral constant for each, which you can then provide a virtual function to return, and you can then static_cast up.
Finally, you could consider passing a function object, in double-dispatch style. This has a definite encapsulation advantage.
struct functor {
void operator()(Int& integral) {
...
}
void operator()(Bool& boo) {
...
}
};
template<typename Functor> void PerformOperationByFunctor(Functor func) {
if (Int* ptr = dynamic_cast<Int*>(this)) {
func(*ptr);
}
// Repeat
}
More finally, you should avoid creating types where they've basically been already covered. For example, there's little point providing a 64bit integral type and a 32bit integral type and ... it's just not worth the hassle. Same with double and float.
What is a common practice for the storage of a list of base class pointers each of which can describe a polymorphic derived class?
To elaborate and in the interest of a simple example lets assume that I have a set of classes with the following goals:
An abstract base class whose purpose is to enforce a common functionality on its derived classes.
A set of derived classes which: can perform a common functionality, are inherently copyable (this is important), and are serializable.
Now alongside this required functionality I want to address the following key points:
I want the use of this system to be safe; I don't want a user to have undefined errors when he/she erroneously casts a base class pointer to the wrong derived type.
Additionally I want as much as possible the work for copying/serializing this list to be taken care of automatically. The reason for this is, as a new derived type is added I don't want to have to search through many source files and make sure everything will be compatible.
The following code demonstrates a simple case of this, and my proposed (again I am looking for a common well thought out method of doing this, mine may not be so good) solution.
class Shape {
public:
virtual void draw() const = 0;
virtual void serialize();
protected:
int shapeType;
};
class Square : public Shape
{
public:
void draw const; // draw code here.
void serialize(); // serialization here.
private:
// square member variables.
};
class Circle : public Shape
{
public:
void draw const; // draw code here.
void serialize(); // serialization here.
private:
// circle member variables.
};
// The proposed solution: rather than store list<shape*>, store a generic shape type which
// takes care of copying, saving, loading and throws errors when erroneous casting is done.
class GenericShape
{
public:
GenericShape( const Square& shape );
GenericShape( const Circle& shape );
~GenericShape();
operator const Square& (); // Throw error here if a circle tries to get a square!
operator const Circle& (); // Throw error here if a square tries to get a circle!
private:
Shape* copyShape( const Shape* otherShape );
Shape* m_pShape; // The internally stored pointer to a base type.
};
The above code is certainly missing some items, firstly the base class would have a single constructor requiring the type, the derived classes would internally call this during their construction. Additionally in the GenericShape class, copy/assignment constructor/operator would be present.
Sorry for the long post, trying to explain my intents fully. On that note, and to re-iterate: above is my solution, but this likely has some serious flaws and I would be happy to hear about them, and the other solutions out there!
Thank you
What is the problem of a std::list< shape* > (or a std::list< boost::shared_ptr > thereof)?
That would be the idiomatic way of implementing a list of shapes with polymorphic behavior.
I want the use of this system to be safe; I don't want a user to have undefined errors when he/she erroneously casts a base class pointer to the wrong derived type.
Users should not downcast, but rather use the polymorphism and the base (shape) operations provided. Consider why they would be interested in downcasting, if you find a reason to do so, go back to drawing board and redesign so that your base provides all needed operations.
Then if the user wants to downcast, they should use dynamic_cast, and they will get the same behavior you are trying to provide in your wrapper (either a null pointer if downcasting pointers or a std::bad_cast exception for reference downcasting).
Your solution adds a level of indirection and (with the provided interface) require the user to try guessing the type of shape before use. You offer two conversion operators to each of the derived classes, but the user must call them before trying to use the methods (that are no longer polymorphic).
Additionally I want as much as possible the work for copying/serializing this list to be taken care of automatically. The reason for this is, as a new derived type is added I don't want to have to search through many source files and make sure everything will be compatible.
Without dealing with deserialization (I will come back later), your solution, as compared to storing (smart) pointers in the list, requires revisiting the adapter to add new code for each and every other class that is added to the hierarchy.
Now the deserialization problem.
The proposed solution is using a plain std::list< boost::shared_ptr >, once you have the list built, drawing and serialization can be performed right out of the box:
class shape
{
public:
virtual void draw() = 0;
virtual void serialize( std::ostream& s ) = 0;
};
typedef std::list< boost::shared_ptr<shape> > shape_list;
void drawall( shape_list const & l )
{
std::for_each( l.begin(), l.end(), boost::bind( &shape::draw, _1 ));
}
void serialize( std::ostream& s, shape_list const & l )
{
std::for_each( l.begin(), l.end(), boost::bind( &shape::serialize, _1, s ) );
}
Where I have used boost::bind to reduce code bloat instead of iterating manually. The problem is that you cannot virtualize construction as before the object has been constructed you cannot know what type it actually is. After the problem of deserializing one element of a known hierarchy is solved, deserializing the list is trivial.
Solutions to this problem are never as clean and simple as the code above.
I will assume that you have defined unique shape type values for all shapes, and that your serialization starts by printing out that id. That is, the first element of serialization is the type id.
const int CIRCLE = ...;
class circle : public shape
{
// ...
public:
static circle* deserialize( std::istream & );
};
shape* shape_deserialize( std::istream & input )
{
int type;
input >> type;
switch ( type ) {
case CIRCLE:
return circle::deserialize( input );
break;
//...
default:
// manage error: unrecognized type
};
}
You can further alleviate the need to work on the deserializer function if you convert it into an abstract factory where upon creation of a new class the class itself registers it's deserialization method.
typedef shape* (*deserialization_method)( std::istream& );
typedef std::map< int, deserialization_method > deserializer_map;
class shape_deserializator
{
public:
void register_deserializator( int shape_type, deserialization_method method );
shape* deserialize( std::istream& );
private:
deserializer_map deserializers_;
};
shape* shape_deserializator::deserialize( std::istream & input )
{
int shape_type;
input >> shape_type;
deserializer_map::const_iterator s = deserializers_.find( shape_type );
if ( s == deserializers_.end() ) {
// input error: don't know how to deserialize the class
}
return *(s->second)( input ); // call the deserializer method
}
In real life, I would have used boost::function<> instead of the function pointers, making the code cleaner and clearer, but adding yet another dependency to the example code. This solution requires that during initialization (or at least before trying to deserialize) all classes register their respective methods in the shape_deserializator object.
You could avoid lots of repetition in GenericShape by using templates (for the constructors and converters), but the key bit that's missing is having it inherit from Shape and implement its virtuals -- without it it's unusable, with it it's a pretty normal variant on envelope/implementation idioms.
You may want to use auto_ptr (or somewhat-smarter pointers) rather than a bare pointer to Shape, too;-).
I would propose boost::shared_pointer<Shape> in an STL container. Then use dynamic_cast to downcast guarantee type correctness. If you want to provide helper functions to toss exceptions instead of returning NULL, then follow Alex's suggestion and define a template helper function like:
template <typename T, typename U>
T* downcast_to(U *inPtr) {
T* outPtr = dynamic_cast<T*>(inPtr);
if (outPtr == NULL) {
throw std::bad_cast("inappropriate cast");
}
return outPtr;
}
and use it like:
void some_function(Shape *shp) {
Circle *circ = downcast_to<Circle>(shp);
// ...
}
Using a separate class like GenericShape is just too strongly coupled with every class that descends from Shape. I wonder if this would be considered a code smell or not...
I want the use of this system to be
safe; I don't want a user to have
undefined errors when he/she
erroneously casts a base class pointer
to the wrong derived type.
Why would you get undefined errors? The behavior of dynamic_cast is perfectly well-defined and catches the error if you cast a base class pointer to the wrong derived type. This really seems like reinventing the wheel.
Additionally I want as much as
possible the work for
copying/serializing this list to be
taken care of automatically. The
reason for this is, as a new derived
type is added I don't want to have to
search through many source files and
make sure everything will be
compatible.
I'm not sure what the problem is here. If all the derived classes are serializable and copyable, isn't that good enough? What more do you need?
I'm also not sure what to make of the first two requirements.
What do you mean, the ABC should "enforce a common functionality"? And what is the point in having derived classes, if their role is only to perform that same common functionality, be copyable and serializable?
Why not just make one non-abstract class serializable and copyable then?
I'm probably missing something vital here, but I don't really think you've explained what it is you're trying to achieve.