I'd like to know if it's possible to inherit from boost::function.
Basically, for ease of use, what I'd like to
is have a type "Delegate" which is basically a boost::function.
It's just for ease of use in some code I'm writing.
I at one point typedef'd boost::function to Delegate, but typedef'ing in my experience plays hell with gdb stuff. Especially if it's templated, so I wanted to avoid that (ever try debugging stl containers that've been typdeffed? oofta).
I found some code online which gave some sort of an example:
template<class Signature>
class Delegate : public boost::function<Signature>
{
public:
using boost::function<Signature>::operator();
};
Now, as I attempt to use it I get some errors.
A usage example would be:
Tank * tankptr = new Tank();
Delegate<void ()> tankShoot(boost::bind(boost::mem_fn(&Tank::Shoot),tankptr));
This yields errors such as
error: no matching function for call to ‘Delegate<void ()()>::Delegate(boost::_bi::bind_t<boost::_bi::unspecified, boost::_mfi::mf0<void, Tank>, boost::_bi::list1<boost::_bi::value<Tank*> > >)’
Delegate.h:26: note: candidates are: Delegate<void ()()>::Delegate()
Delegate.h:26: note: Delegate<void ()()>::Delegate(const Delegate<void()()>&)
If I had to guess why I'm getting these errors, I'd have to say it's cause I'm missing
some kind of copy constructor that takes whatever base a boost::bind constructor returns.
Any thoughts on how I can get past this hurdle, or anyone able to point me to good examples
of inheriting from boost::function?
Deriving from a class does not automatically 'inherit' the base class constructors for the derived class. You will need to create all required constructors there.
hKaiser was correct in my needing to write the required constructors.
I had a hell of a time of it, until I found the interface file for the boost class "function"
on their website.
In the end I ended up with something like:
template<class Signature>
class Delegate : public boost::function<Signature>
{
public:
///use the functor operator from the original class
using boost::function<Signature>::operator();
///base constructor for our new type,
Delegate() : boost::function<Signature>() {/*empty*/}
///copy constructor for our new type
Delegate(const boost::function<Signature>& x) : boost::function<Signature>(x) {/*empty*/}
Delegate& operator=(const Delegate & _delegate)
{
boost::function<Signature> x = _delegate;
try
{
dynamic_cast<boost::function<Signature> & >(*this) = x;
}
catch(bad_cast &bc)
{
cout << "Bad Cast Exception. " << bc.what();
int * ptr = NULL;
*ptr = 1; //force seg fault instead of assert
}
return *this;
}
};
I'm not sure if I'm properly using the dynamic_cast (in the context of adhering to good coding practices) or if I even need it there in the assignment operator, but it does work, and works extremely well.
Related
I might simply be overlooking something or being stupid, in which case I am sorry, but I'm really not sure how to, if it is even possible, access a virtual member function. Actually, the virtual part is a second issue about a possible solution I will describe later. Here's some example code that summarizes my issue:
class BaseClass
{
public:
virtual std::string ClassName()
{
return "BaseClass";
}
};
class DerivedClass : public BaseClass
{
public:
std::string ClassName()
{
return "DerivedClass";
}
};
template<class cT>
void StatusPrint(const std::string& message)
{
return cT.ClassName(); // Here's where my issue arises.
}
So, I tried to replace cT. with ct::, however, while that causes compiler issues on its own, it also tries to access the virtual function in BaseClass, but I want to access the overridden function in DerivedClass.
Is what I am trying to do possible like this?
Sorry if I'd seem rude, but you cannot return anything from void function. So apparently, we don't have the full story here.
Do you really want a compile time solution?
Looking at your code, it seems that className() does not use at all the state of the object. So you could make it static (instead of virtual). THe problem would then be solved with:
template<class cT>
std::string StatusPrint(const std::string& message) // returns string, not void
{
return cT::ClassName(); // :: if class name is static.
}
Since the template cannot derive the type from its argument, you'd need to provide it, making the choice of the class completely compile-time:
cout<< StatusPrint<DerivedClass>("test"s)<<endl;
This kind of practice is used, when you have some utility classes and you want to configure at compile time which one to use.
Do you want a dynamic solution?
If you want a dynamic solution at runtime, you need to use some object, because virtual require an object that knows its dynamic type at runtime.
Then it depends on the context. One solution is to use a cT parameter, with the advantage of parameter deduction:
template<class cT>
std::string StatusPrint ( cT object, const std::string& message)
{
return object.ClassName(); // Here's where my issue arises.
}
You'd then call it:
DerivedClass test;
...
cout<< StatusPrint(test, "test"s)<<endl;
Online Demo
But of course, it could also use some global object instead (but the template makes then much less sense), or better, an object in a template class if you refactor StatusPrint() to be a member function of such a class.
I'm not sure what exactly you are trying to do, but see if this is more like it:
std::string StatusPrint(BaseClass *instance) {
return instance->ClassName();
}
Template parameters are for types, virtual inheritance needs pointers.
DerivedClass derived;
std::cout << StatusPrint(&derived) << std::endl; // note the &
cT is a type, not an object. You can only call functions on object instances (unless they're static functions, but that's not what you're trying to do here). You need to pass in an instance of the object you want to print out. e.g.
template<class T>
std::string StatusPrint(const T& obj, const std::string& message)
{
return obj.ClassName();
}
It's also customary to name template types with Uppercase to avoid this confusion.
I have to admit the compiler error for this is confusing, but it does give you a hint that there's something wrong with cT. It's saying that what comes before . is not what it was expecting.
With GCC 9:
error: expected primary-expression before '.' token
24 | return cT.ClassName();
| ^
Consider the following abstract class, which will be the interface for a class that writes the information carried by some object to standard output.
class FileBuilder
{
public:
virtual void build(const Object& object) = 0;
virtual ~FileBuilder() = default;
};
At this point I will note that Object is also an abstract class with derived class SpecialObject. Now I am going to implement SpecialFileBuilder : FileBuilder, as follows.
class SpecialFileBuilder : public FileBuilder
{
public:
void build(const SpecialObject& specialObject);
};
...
void SpecialFileBuilder::build(const SpecialObject& specialObject)
{
// Do some stuff
}
I don't fully understand why this should not be possible. SpecialFileBuilder respects the interface FileBuilder, and everywhere which expects a FileBuilder can instead be given a SpecialFileBuilder. I appreciate your help in advance.
Of course, this would work if I changed things to the following.
void SpecialFileBuilder::build(const Object& object)
However, in my implementation of SpecialFileBuilder::build() I need to use the fact that the argument is a SpecialObject, not just an Object.
How should I instead approach this design?
TL;DR no, this does not make any sense.
Full version below.
I don't fully understand why this should not be possible.
virtual void build(const Object& object) = 0;
This declaration is a promise. It promises that build can accept any Object as an argument. Such promises are legally binding for derived classes, i.e. they must implement the promise as stated by the base class. Note the declaration does not promise that build can accept some objects and not others.
FileBuilder* builder = GetBuilder(); // we don't know what kind of builder it is
SpecialObject some;
builder->build(some); // must work
OtherSpecialObject some;
builder->build(other); // must work too
UnrelatedObject whatever;
builder->build(whatever); // must work as well
Now looking at the other declaration
void build(const SpecialObject& specialObject);
It reneges on the promise. The original promise is strong. Give me any object, I can deal with it. The new promise is weak. Oh, I am a special little builder, I can only cope with special little objects!
Sorry bud, you cannot override a strong promise with a weaker one. If you were allowed to, how would we be able to trust any promise?
Now if your design doesn't fit in this outline, i.e. you always know what kind of builder you get, and you don't want to promise to cope with all kinds of objects, then you have selected a wrong tool for the job. Perhaps you want to give generic programming a try.
template <typename T>
class FileBuilder {
virtual void build (const T& t) = 0;
};
class SpecialBuilder: public FileBuilder<SpecialObject> {
void build (const SpecialObject& t) override;
};
Now the code above won't work, we need to fix it
FileBuilder<SpecialObject>* builder = GetBuilder<SpecialObject>(); // we know exactly what we want to build
SpecialObject some;
builder->build(some); // will work;
OtherSpecialObject other;
builder->build(other); // sorry that's not in the contract, won't compile
I don't fully understand why this should not be possible. SpecialFileBuilder respects the interface FileBuilder, and everywhere which expects a FileBuilder can instead be given a SpecialFileBuilder
You may have covariant return type.
But for argument, you would need contra-variant return type (which is not supported in C++).
As following code should be correct
SpecialFileBuilder specialFileBuilder;
FileBuilder& fileBuilder;
SpecialObject2 specialObject2; // Other derived class, unrelated to SpecialObject
Object& object = specialObject2;
fileBuilder.build(object); // correct type
// but
specialFileBuilder.build(specialObject2); // won't compile
contra-variant parameter would be
struct Base {
virtual void f(const Cat&) = 0;
};
struct Derived : Base
{
void f(const Animal&) override; // if contra-variance was supported
};
In my application I have to derive some classes from a base one, the problem is that I want to enforce the derived classed to have 3 particular constructor implementation. As c++ don't have virtual pure constructor, it seemed quite desperate (I had to check manually each class implementation to ensure that the particular ctors are implemented, not quite fun to do).
Yesterday I found a crazy way to simulate the comportment of a virtual ctor:
template <class T>
class AbstractEnforcer{
protected:
AbstractEnforcer(){}
private:
static void Enforcer(){
delete new T();
delete new T(*(new unsigned int));
delete new T(*(new unsigned int, *(new QString));
}
}
class AbstractClass : private AbstractEnforcer<AbstractClass>{
}
the only inconvenience with this approach is that I have to to declare all derived class with the syntax:
class X : private AbstractEnforcer<X>
And even if it's not a problem; because the Enforcer() methode never get called (and even so it does nothing [hope so!!!])
My question is:
"is there any mean (not with macros) to force the derived class to use this mechanism without parametrazing the AbstractClass (because this will work in only one level of derivation"
template <class T>
class AbstractClass : private AbstractEnforcer<T>{
}
Your solution does not solve the problem since templated code that isnt used is not instantiated, and thus unless you manually invoke this function - it will not verify the existence of the wanted constructors.
What you can do is have this method called from the constructor of your enforcer:
template <class T>
class AbstractEnforcer{
protected:
AbstractEnforcer(){ Enforcer(); }
private:
static void Enforcer(){
delete new T();
delete new T(*(new unsigned int));
delete new T(*(new unsigned int, *(new QString)));
}
// disable:
AbstractEnforcer (const AbstractEnforcer &enf);
};
class AbstractClass : private AbstractEnforcer<AbstractClass>{
};
int main () {
AbstractClass c;
}
Then, the compiler complains - mission accomplished.
Note that I've disabled the copy constructor so that there will be no way to bypass that check (by calling a different constructor).
Edit - Non leaking Enforcer(): [As there's absolutly no need to use dynamic allocations there..]
static void Enforcer(){
T t1();
T t2(int(3));
T t3(int(4), QString());
}
See this page in the C++ FAQ.
What I would do is something like this:
class AbstractClass {
public:
virtual AbstractClass* create() const = 0;
virtual AbstractClass* create(unsigned int) const = 0;
virtual AbstractClass* create(unsigned int, QString) const = 0;
};
Then each of the derived classes would be forced to override these functions, which should create new objects with different constructors.
From this comment to one of the answers I think that you do not really want to achieve what you are asking here, but rather a different thing. The comment I refer to is:
Part 1:
I knew that we can't have a virtual
constructor and I don't want to have
one, my purpose is to a compiler
static code checking that will alert
me if I forgot to implement a specific
constructor prototype.
Part 2:
My project is a
plugin like dynamic loading system and
I have in some way enforce the ctors
prototypes implementation of the third
party code.
What you are asking in the question is 1, and you can enforce it in different ways, just read some of the answers, or take a look at metaprogramming examples and the boost::type_traits library.
Now, if what you really want is part 2: provide a dynamic loading plugin mechanism then you do not need to enforce the constructors, but a common interface both for the plugin objects and the creation of the plugin objects. There is no way of instantiating an instance of an object of unknown (at compile time) object, and that means that you will not be able to call the constructors from your code. I would suggest
// Interface:
class plugin {
public:
virtual void operation() = 0;
};
class plugin_factory {
public:
virtual plugin* create() = 0;
virtual plugin* create( unsigned int ) = 0;
virtual plugin* create( unsigned int, QString const & ) = 0;
};
Users will need to provide the implementation of a plugin and the factory that creates them. They will probably need to implement a point of entry for their library so that you can get access to the factory (or so they can register their factory within your system, or else I would suggest using a library for these purposes (boost::extension seems like a place to look at)
I'd probably just have a template for generating a test:
template <typename T>
void enforceConstructors() {
T t1;
T t2(0);
QString q;
T t3(0, q);
}
Then somewhere in your tests, do:
enforceConstructors<X>();
enforceConstructors<Y>();
enforceConstructors<Z>();
Those might be all together, or in separate places for each of the classes X, Y, Z. Depends how you want to organise your tests.
If the values I used aren't suitable, either put in some values which are, or else compile that test but don't run it. If you don't have unit tests either get some, or else add the following to the class (instead of inheriting from the base class):
#ifndef NDEBUG
static void test() { enforceConstructors<X>(); }
#endif
You don't usually need to make constructors part of an interface defined by an abstract base class. The reason is that such interfaces are for dynamic polymorphism - you pass an object to some function, and it calls functions on it. You can't "pass" a class to a function and have it instantiate the class other than with templates. Templates mostly enforce their interfaces at compile time anyway - if you instantiate the template and it uses the constructor, then the constructor has to be there.
If you forget to implement the constructor but use it in your code, you will get a compilation error. For example:
Base * p = new Derived( 42 );
will be a compile-time error if the Derived(int) constructor is not provided - the Base constructor will not be used.
I finally adopted this solution, but not quit convienced:
#ifdef NDEBUG
#ifndef ENFORCE_CTORS
#define ENFORCE_CTORS(enforcingTemplate, enforcedClass) \
friend void enforcingCtors(){static enforcingTemplate<enforcedClass> _enforcer;}
#endif
template<class T>
class AbstractEnforcer : T{
public:
explicit AbstractEnforcer(){
T enforcedCtor0( );
T enforcedCtor1( *(new unsigned int) );
T enforcedCtor2( *(new unsigned int), *(new QString) );
T enforcedCtor3( *(new unsigned int), *(new float ) );
}
};
#endif
and in each class that I wan't to enforce I just add like this:
class X{
ENFORCE_CTORS(AbstractEnforcer, X);
/*
.....
*/
}
I didn't find any other way to inject this code dynamically in a class. And I may been unclear about the final purpose of the operation (sorry for my terrible English).
I'm trying to use templates to get std:list of items, where each item has a pointer to the list which contains it, but I keep hitting a compiler message.
Here's a very stripped down version of the code.
template <class E> class Item
{
public:
E* owner; // pointer to list that owns us.
};
template <class E> class BaseList: public std::list<E>
{
protected:
typedef std::list<E> inherited;
public:
void push_back(const E &e)
{
E tmp(e);
tmp.owner = this; // This line gives the error.
inherited::push_back(tmp);
}
};
class MyList;
class MyItem : public Item<MyList>
{
};
class MyList : public BaseList<MyItem>
{
};
void foo() // test code to instantiate template
{
MyList l;
MyItem m;
l.push_back(m);
}
However, my compiler barfs at the line:-
tmp.owner = this;
Error is:
[BCC32 Error] Unit7.cpp(30): E2034 Cannot convert 'BaseList<MyItem> * const' to 'MyList *'
It's like "this" has somehow become const, but I can't see why. Compiler is Codegear C++Builder 2009.
I admit I'm not 100% happy using templates, so I'm unsure if this is my problem or the compilers. The same code without template use compiles fine, but obviously that's not what I want, as I have several item/list classes that want to work this way.
Also, is there a better technique that would avoid having all the "owner" pointers in each item?
EDIT: I think I stripped the example down too far: "MyList" actually introduces new methods, which "MyItem" must then access through the "owner" pointer.
SUMMARY: Thanks for all comments and answers. As the accepted answer says, the problem is simply one of type incompatibility between pointer to a BaseList vs. MyList.
The issues raised about deriving from STL containers and alternative designs are also helpful, but the solution I've used is essentially identical to Luc Touraille's one below.
At line 30, "this" is a pointer to a BaseList<MyIteM>, not a MyList. You can substitute a class with a derived one, but not the other way around.
You can either typedef MyList to be a BaseList<MyItem>, like so:
typedef BaseList<MyItem> MyList
or let MyItem derive from Item<BaseList<MyItem> > instead.
When you derive from a type, you create a different type. When you typedef, you create an alias for that type. So when you typedef the compiler will accept this.
In addition to the answers you already have, I would also point out that the standard library collection classes are not intended to be derived from, as they do not have virtual destructors, and none of their member functions is virtual.
Shouldn't it be tmp.owner = static_cast<MyList*>(this). The type of E is MyList in the MyItem hence E* will be MyList* . The type of this pointer will be BaseList*, hence compiler gives the error that you can not convert the base class pointer to the derived class pointer.
It's hard to say if there's a better solution, when you don't say what it is you need.
Why do each element need a pointer to the list they're stored in?
Anyway, bad things can happen when you inherit from standard containers. They don't have virtual destructors, so you have to be very careful.
A better solution might be to just provide a free function performing the push_back:
template <typename T>
void push_back(std::list<T>& list, const T& t) {
T tmp(t);
tmp.owner = this;
list.push_back(tmp);
}
Apart from avoiding the nonvirtual destructor problem, it also solves your compiler error, because you now only have one type of list.
Of course, if we know why you need this owner pointer in the first place, better still solutions may exist.
Edit
In response to your edit and the comments, use composition, not inheritance:
struct House {
std::string zipcode;
std::list<Person> persons;
void AddPerson(const Person& person) {
Person tmp(person);
tmp.owner = this; // The owner field should be a house, not the list of persons.
persons.push_back(tmp);
}
};
Although I'm not sold on the almost circular references you get when a House stores a list of Persons, and a Person has a pointer to the House it's in.
I'd prefer to decouple these classes as much as possible. If I want to send a letter to a person, I'd call SendLetter(Person, House). Send a letter to this person in that house.
On the side note, you should not extend any classes from std, they are not built for it.
Specifically they don't have virtual destructor so when you call delete on pointer to base class your derived class's destructor will never get called.
You can read more on it Advice on a better way to extend C++ STL container with user-defined methods
I like jalf's free function idea. I'd make it:
template <class X, class Y> // X must have a push_back(Y) member, Y must have an X* owner member
void push_back(X& container, Y value)
{
value.owner = container;
container.push_back(value);
}
This is agnostic over whether the X passed is
a container itself,
is derived from a container as in the original code
or contains a container and has a forwarding push_back function
As it has already been pointed out, the affectation
tmp.owner = this;
fails because this doesn't have the same type as tmp.owner. One solution is to perform a cast, but to do so, you need to provide the container type to BaseList. This can be done using a typedef in Item. Here is the code:
template <class Item> class BaseList
{
public:
void push_back(Item i)
{
i.owner = static_cast<Item::containerType *>(this); // note the cast
items.push_back(i);
}
Item & back() { return items.back(); }
protected:
std::list<Item> items;
};
template <class Container> class Item
{
public:
typedef Container containerType; // Typedef used by BaseList
containerType* owner; // pointer to list that owns us.
};
I also removed the public derivation of std::list: as many said, this is (most of the time) best avoided ; you should consider using composition, or maybe private inheritance.
P.S.: I tried making owner private and BaseList<Item>::push_back friend of Item, but I didn't manage to do it. Is it possible at all? (If too long to answer in comment, feel free to ask a question and answer it)
Regarding const: The type BaseList<MyItem> * const that the compiler mentions is a red herring -- it's not a pointer-to-a-const-object, but a pointer that is const, i.e. an address that won't change. (When you think about it, this never changes to point to something else, does it?)
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.