tags:

views:

106

answers:

6

hi,

I have handles of different types inside a hierarchy.

class Handle { common data }
class HandleA : Handle { data specific to a }
class HandleB : Handle { data specific to b }

Most parts of the code only deal with handles. But some parts ( the "managers" for HandleA/HandleB ) need access to the data in the child classes. eg:

void ManagerA::DoSomething(Handle handle)
{
    // needs access to data in handleA
}

Are there any solutions to this that don't involve casting?

My ideas so far:
- Save the additional data inside a map in ManagerA/B and use the handle to lookup that data (additional hashtable lookup)
- Have polymorphic methods in the handles ( handle.DoSomething()) that call the appropiate manager methods (needs an additional pointer in every handle)
- Screw it and use casts

Any ideas? Am I missing something?

Thanks

+4  A: 

If it's data specific to only one -- and only one type, use dynamic_cast<T>, that's what it's there for. Otherwise declare a virtual function in the base class.

EDIT: It's unlikely that any solution is going to result in measurable performance differences at runtime.

Billy ONeal
A: 

What about changing the signature for DoSomething to:

void ManagerA::DoSomething(HandleA handle)
fbrereto
+1  A: 

I wouldn't use polymorphism for handles - being handles rather than pointers, they are supposed to absolutely hide the implementation of the referenced object. If you use virtual functions, the user of the handle could call those functions, which is surely a bad idea.

The two common solutions are casting and using a map. If the later, your handle doesn't even have to be a class - it could just as well be an int or so. On Windows, handles are void* pointers. I have no idea what's really behind the pointer, but I really don't care. And that's the point of handles, as far as I'm concerned.

eran
A: 

Your first and third ideas would work. Another idea is to use double-dispatch (I don't know if that Wikipedia article is understandable: the original article/explanation in Meyer's More Effective C++ is 20-odd pages long), which means implementing a virtual method like Handle::DoSomething(Manager&).

ChrisW
+7  A: 

Receiving an argument by value, as you're doing in:

void ManagerA::DoSomething(Handle handle)

WILL "slice away" anything in the passed-in argument beyond what a Handle instance holds, so your handle argument will have NO "extra data". You absolutely need to pass by pointer or reference (possibly const if the data does not need to be modified, of course).

That being said, the normal polymorphic approach involves defining virtual methods within the base class and overriding them appropriately in the subclasses. Why not follow such a perfectly normal architecture rather than fighting against the OO approach? There may be valid reasons (which justify e.g. adopting some variant on a visitor pattern, etc), but you just don't explain enough of the forces in play for us to be able to help along those lines; on the information as presented I'd have to suggest "rearchitect to use virtual methods".

Alex Martelli
A: 

Another possibility is storing the concrete type in each handle, possibly as an integer or an enum. You either hard-code all the possible concrete handle types, or use some sort of type registration mechanism. Obviously this approach has its own drawbacks, but it is another possibility, one you didn't mention. It's the approach X-Windows used for event types. The event data structure was a union of all possible event data, with a type variable indicating the true data type of a particular event. Not saying it's good, just saying it's an option, one that doesn't require dynamic casting.

enum HandleType
{
  HANDLE_TYPE_A,
  HANDLE_TYPE_B
};

class Handle
{
 private:
  HandleType _type;
 protected:
  Handle(HandleType type) :
    _type(type)
  {}
 public:
  HandleType get_type() const
  { return _type; }
};

class HandleA
{
  HandleA() :
    Handle(HANDLE_TYPE_A)
  {}
};

void ManagerA::DoSomething(Handle& handle)
{
  if (handle.get_type() == HANDLE_TYPE_A)
    do_something();
}
Darryl