6

Lets say I have a class Handler with some subclasses like stringhandler, SomeTypeHandler, AnotherTypeHandler. The class Handler defines a method "handle" as a common interface for all the subclasses. The logic to "handle" is ofcourse completely different for the different handlers.

So what I need to do is pass a value of anything to the handle method. The specific classes can then cast the "anything" to the type they expect.

Basically what I need is something like the java class Object :D

The first thing I tried was a void*, but apparently you can not do B* someB = dynamic_cast<B*>(theVoidPointer), so no luck there.

My second idea was to use boost::any. however, the requirement to use boost::any is that the value must be copy cunstructable, which is not the case for my data.

Any ideas to get this to work?

Thanks

EDIT: Note that I know I could use a SomeData class with no members at all, and let my data be subclasses of that, but I am looking for a more generic approach which does not require me to make my own wrapper class.

W. Goeman
  • 1,674
  • 2
  • 15
  • 31
  • 2
    Are you sure that you need `dynamic_cast`, and can't use `reinterpret_cast`? – Marc Bollinger Apr 10 '12 at 14:41
  • It looks like most of your options are described here: http://stackoverflow.com/questions/913505/casting-void-pointers-depending-on-data-c – satnhak Apr 10 '12 at 14:45
  • 1
    You could also use boost::any to store the pointer, so the handled types need not to be copy constructible – fdlm Apr 10 '12 at 14:46
  • 5
    I think you should step back a bit and tell us what you're trying to accomplish instead of your idea of how to implement it. It sounds to me like you're basically heading off into the weeds with an idea that might work in Java, but is horrible abuse of C++ (truthfully, a horrible of OO in general, but one that Java embraces). – Jerry Coffin Apr 10 '12 at 14:51
  • @fdlm I am trying that now. Sounds like a solid plan. Maybe you should make an answer out of that? – W. Goeman Apr 10 '12 at 14:53
  • 1
    Step back, step back, step back. Upcast is generally a sign of bad design. Type switching (on open sets) is generally a sign of bad design. – Matthieu M. Apr 10 '12 at 15:09
  • @JerryCoffin and MatthieuM, To give a more general idea, I am looping over all these handlers and they all have there tasks, but I can enter that loop with different kinds of data. So the handlers mus be able to cope with that. If there is a better approach, please elaborate. – W. Goeman Apr 10 '12 at 15:22

5 Answers5

1

You can for example define a base class:

class BaseHandlerData {
   ...
};

Then derive your specific data classes, which are expected by your handlers:

class StringData: public BaseHandlerData {
   ...
};

class SomeData: public BaseHandlerData {
   ...
};

Then you should be able to pass a BaseHandlerData* argument to the handle method, and use something like:

void handle(BaseHandlerData *data) {
    StringData* stringData = dynamic_cast<StringData*>(...);
    // handle your string data here ...
}

to safely cast to your expected data type.

Gerald

nutrina
  • 1,002
  • 1
  • 12
  • 26
  • This is in detail what I briefly said in my edit. A good solution indeed, but I rather do not write the extra class for this. I rather have this as a basic utility (like boost::any). – W. Goeman Apr 10 '12 at 15:04
1

Okay, here is a simple approach using boost::any to hold pointers to your datatypes. However, beware that boost::any adds some overhead code decreasing performance slightly (in most cases neglectible).. consider using boost::spirit::hold_any instead, or void* if you don't need type safety.

class Handler {
public:
    void handle( boost::any value ) { 
        do_handle( value );
    }
private:
    virtual void do_handle( boost::any& value ) = 0;
};

template<class T>
class HandlerBase : public Handler {
private:
    void do_handle( boost::any& value ) {
        // here you should check if value holds type T*...
        handle_type( *(boost::any_cast<T*>( value )) );
    }

    void handle_type( const T& value ) = 0;
};

class StringHandler : HandlerBase<std::string> {
private:
    void handle_type( const std::string& value ) {
        // do stuff
    }
};

Now you can write lots of handler classes, deriving from HandlerBase, without assuming that the handled types have a common base class.

fdlm
  • 614
  • 1
  • 5
  • 14
  • This is the solution I used, except for the templated HandlerBase. I left that out. I just used let the specific handlers cast to the type they need. As soon as I have multiple handlers for the same data type, I will concider putting the templated base in between. Thanks! – W. Goeman Apr 10 '12 at 15:09
1

Another alternative, getting more toward the C world, would be a union type (http://en.wikipedia.org/wiki/Union_(computer_science)#C.2FC.2B.2B). This would only allow you to pass the types you specifiy, not any type, but has the type of behavior you describe.

JohnMcG
  • 8,709
  • 6
  • 42
  • 49
0

You need to have a base class called DataObject or something. All of your data types, (string, number, whatnot) are sub classes of DataObject. The you define Handle like this:

void Handle(DataObject *dataObject);

This is a much safer way to do what you want. To make it even better, the DataObject can even know what kind of data it contains. Then the handlers can check that they've been sent the right type of data.

Rocketmagnet
  • 5,656
  • 8
  • 36
  • 47
  • This is basically what I added in my edit. This seems like a good solution indeed, but I was looking for a more generic approach. I still keep it in my list of options in case the other solutions do not work out. – W. Goeman Apr 10 '12 at 14:47
-1

You can do

B* someB = static_cast<B*>(theVoidPointer);
BertR
  • 1,657
  • 11
  • 12
  • @R.MartinhoFernandes and BertR Could you please eloborate on the differences between reinterpret, static and dynamic? I thought reinterpret and static had some dangers. – W. Goeman Apr 10 '12 at 14:44
  • @R. Martinho Fernandes: Before C++11 the reinterpret_cast can indeed lead to undefined behavior, but for C++11 this is no longer the case. Well it seems that Stroustrup made the same mistake, so I could have made worse ones: http://compgroups.net/comp.lang.c++.moderated/stroustrup-void-and-reinterpret_cast/68873 – BertR Apr 10 '12 at 16:04