Double Dispatch alternitive

Started by
3 comments, last by _walrus 20 years, 8 months ago
Hi i made a post a while ago reguarding an alternitve to using Dynamic type checking, that post provided me with a lot of good insight on to the problem and I was recommended to use the Double Dispatching pattern. This pattern is a good solution where the set of classes is of a limited number.


class Number {
virtual Number add(Number * n)=0;
};


class Integer
{
Number add(Number * n) {
  return n ->addFromInteger(this);
}
Number addFromFloat(Number* aFloat) {
  //do Integer + Float
}
Number addFromInteger(Number* anInteger) {
  //do Integer + Integer
}
};

class Float
{
Number add(Number * n) {
  return n ->addFromFloat(this);
}
Number addFromFloat(Number* aFloat) {
  //do Float + Float
}
Number addFromInteger(Number* anInteger) {
  //do Float + Integer
}
};


  
But when we are dealing with many classes, the number of methods for each class gets quite bloated (one method for each type of class). For example:

class Integer
{
Number add(Number * n) {
  return n ->addFromInteger(this);
}
Number addFromFloat(Number* aFloat) {
  //do Integer + Float
}
Number addFromInteger(Number* anInteger) {
  //do Integer + Integer
}
Number addFromDouble(Number* aDouble){
  //do Integer + Double
}
Number addFromFraction(Fraction* aFraction){
  //do Integer + Fraction
}
Number addFromSomething(Something* aSomething){
  //do Integer + Something}
};
.....
...
..
};
  
Now here is my problem: what i have is a scene graph layer and GUI layer that is a representation of a scene graph . I can select a Scene Graph node and drag it onto another scene graph node. The code that does this checks the runtime type of the node begin dragged and the runtime class of the node that is being dropped onto. This is done in the GUI layer as follows:

class SceneGraphGUIRep 
{
   SceneGraphNode * sceneGraphNode;
   void SceneGraphNode * getSceneGraphNode();
}

class DragAndDropController
{
void mouseRelease
{
 if (dragStart->getSceneGraphNode->isKindOfClass(Transformation))
  {
    if (dragEnd->getSceneGraphNode->isKindOfClass(someType))
    {
       //do some action
    }
    else if(...)
    {
    }
  }
  else if(...)
  {
  }
  else if (...)
  {
  }
}

}
  
I was thinking of replacing this with the Double Dispatch Pattern. But using the Double Dispatch Pattern would result in very bloated classes with the need for a specific method to handle each type of class. Additionally, this would move the drag and drop code to the Scene Graph layer (from the GUI layer, which would be bad since the drag and drop function should be a semantic of the GUI). Decentralization of code( we would loose cohersion as bits and pieces of the drag and drop code would be laced throughout the various scene graph classes). Idealy i want to be able to: - To Maintain that drag and drop code is restricted to the GUI layer. - Eliminate the use of dynamic type checking - Maintain Cohersion of the drag and drop code would my existing solution be the best or is there something else that would fit? how about the Vistor Pattern? In his book Effective C++ Scott Myeres says that the "if-then-else" style of programming should be reserved for cases that have no alternitive. Any help or suggestions would be of great help. Thanks. [edited by - _walrus on August 5, 2003 2:39:16 PM]
Advertisement
Well, if you really want to pursue double-dispatch, you can either use the Vistor pattern to emulate it or use something like Loki''s MultiMethod implementation. This is probably one of the most intricate areas of Loki, which is to say one the most complicated pieces of C++ code you will lay eyes on.

It''s also not widely used, thus not widley tested nor proven portable to boot.

An alternative is to say that all nodes know who acceptable parents are, and give the scene-graph node a virtual function, bool IsCompatibleParent(INode parent).

Or, logicaly disect the classifications of nodes and what they support, make them implement that interface, and then you can try a dynamic_cast via RTTI (or a QueryInterface-like call). Child nodes then need to know what classification of services the parent node must support.

- Magmai Kai Holmlor

"No, his mind is not for rent to any god nor government" - Rush, Tom Sawyer

[Look for information | GDNet Start Here | GDNet Search Tool | GDNet FAQ | MSDN RTF[L] | SGI STL Docs | STFW | Asking Smart Questions ]

[Free C++ Libraries | Boost | ACE | Loki | MTL | Blitz++ | wxWindows| Spirit(xBNF)]
[Free C Libraries | zlib ]

- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
great thanks for the replay, I want to try to stay away from unnecessary complexities (Loki''s MultiMethod implementation) so i''ll probably try and use the notion of nodes knowing acceptable parents, but i''ll look into the Visitor pattern. Thanks for your help.
An alternative to consider when the number of combinations is large is dispatch using a map. The key is the pair of types and the value is a function pointer. Something like this:
template< class T >class Dispatcher{public:    typedef void (*DispatchMapValue)( T * a, T * b );    typedef std:air< type_info const &, type_info const & > DispatchMapKey;    typedef std::map< DispatchMapKey, DispatchMapValue > DispatchMap;    void Register( type_info const & a, type_info const & b, DispatchMapValue f )    {        m_DispatchMap.insert( DispatchMap::value_type( DispatchMapKey( a, b ), f ) );    }    void Dispatch( T * a, T * b ) const    {        type_info const & type_a = typeid( *a );        type_info const & type_b = typeid( *b );        DispatchMap::const_iterator f;        f = m_DispatchMap.find( DispatchMapKey( type_a, type_b ) ];        if ( f != m_DispatchMap.end() )        {            (f->second)( a, b );        }        else        {            f = m_DispatchMap.find( DispatchMapKey( type_b, type_a ) ];            if ( f != m_DispatchMap.end() )            {                (f->second)( b, a );            }            else            {                // No dispatch for this combination! Now what?            }        }    }private:    std::map< DispatchMapKey, DispatchMapValue > m_DispatchMap;}  
Warning: I didn''t try compiling the above code.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
quote:Original post by JohnBolton
An alternative to consider when the number of combinations is large is dispatch using a map. The key is the pair of types and the value is a function pointer. Something like this:
template< class T >class Dispatcher{public:    typedef void (*DispatchMapValue)( T * a, T * b );    typedef std:air< type_info const &, type_info const & > DispatchMapKey;    typedef std::map< DispatchMapKey, DispatchMapValue > DispatchMap;    void Register( type_info const & a, type_info const & b, DispatchMapValue f )    {        m_DispatchMap.insert( DispatchMap::value_type( DispatchMapKey( a, b ), f ) );    }    void Dispatch( T * a, T * b ) const    {        type_info const & type_a = typeid( *a );        type_info const & type_b = typeid( *b );        DispatchMap::const_iterator f;        f = m_DispatchMap.find( DispatchMapKey( type_a, type_b ) ];        if ( f != m_DispatchMap.end() )        {            (f->second)( a, b );        }        else        {            f = m_DispatchMap.find( DispatchMapKey( type_b, type_a ) ];            if ( f != m_DispatchMap.end() )            {                (f->second)( b, a );            }            else            {                // No dispatch for this combination! Now what?            }        }    }private:    std::map< DispatchMapKey, DispatchMapValue > m_DispatchMap;}   
Warning: I didn''t try compiling the above code .



umm, maybe it''s late or i''m missing something from your post, but i''m not too sure as to how this will solve the issue of trying to reduce "dynamic type checking" and the use of downcasting; or how it applys to reduce an inherent problem of the double dispatch pattern (bloating classes will multiple methods to handle various types of classes derived from a common base). Could you elaborate on ur post please . Thanks for your help

This topic is closed to new replies.

Advertisement