Topic: proposal: extending type_info


Author: "Chris Minnoy" <cminnoy@starlab.net>
Date: 1998/12/29
Raw View
For the moment, Run Time Type Information (RTTI) is only weakly supported in
C++. With the new standard we have new abilities like asking a name of a
class or
comparing type_info's of classes, but the usefulness of that is quit
limited. If we look at some other languages we see that they have more
powerful
technics at hand. For example in CLOS we see a very powerful system called
multiple
dispatching.

Multiple dispatching is a technic that enables you not only to make your
code polymorphic for one hierarchy but for a multitude of hierarchies.

I did some experiments on trying to make a system that could do the
trick in C++ and came to the conclusion that if the C++ standard would
just extend the class type_info a little bit it's very easy to make C++
even more powerful than CLOS.

For the moment type_info looks something like this:

class type_info {
public:
    virtual ~type_info();

    bool operator==( const type_info& ) const;
    bool operator!=( const type_info& ) const;
    bool before( const type_info& ) const;
    const char* name() const;

private:
    type_info( const type_info& );
    type_info& operator=( const type_info& );
};

If type_info would be extended with a function like

const bool has_public_parent( const type_info&, int* const = 0 ) const;

it would become easy to make a dispatcher class.
The function returns true or false, depending on the fact that that
class has such a public parent. The optional pointer returns
the shortest (!!!) path to that public parent. It is important that
it is the shortest path, otherwise our dispatch algorithm can not
resolve the problem in an optimal way.

To take an example of double dispatching (two hierarchies) the
dispatcher whould receive two parameters and could decide,
based on their typeid's which memberfunction to call. If it can't
find an exact typeid it asks at the objects typeid's if they have
a public parent like he has in his list. If so, the dispatcher knows
that the object(s) are derived from a class where the dispatcher has
a solution for and calls the correct member function.
To solve the problem of which function to call, it asks the depths of
inheritence, computes the average and the variance, and compares
them all. Then based on average (smallest), priority (largest) and
variance (largest) it chooses the correct function.

The dispatcher could look something like this (the class is not complete):


template <class _A1, class _A2, class _R>
class dispatcher : public std::binary_function<_A1,_A2,_R>
{
    typedef dispatcher Self;

public:

    class EUnresolvedDispatch { };  // if dispatch can not be resolved

    virtual ~dispatcher() throw() = 0 { }
    _R operator()( _A1&, _A2& ) const;  // start dispatch

protected:

    struct Group // or some other name
    {
        const type_info* id1; // typeid of first object
        const type_info* id2; // typeid of second object
        _R (Self::*f)( _A1&, _A2& ); // pointer to member function
        char priority;
        bool symetric; // is this information also valid with reversed
objects
    };

    template <class T>
    void Register  // add dispatch information
    (
    const type_info& id1,
    const type_info& id2,
    _R (T::*f)( _A1&, _A2& ),
    bool symetric = true,
    char priority = 0
    )
    {
        Group temp;
        temp.id1 = &id1;
        temp.id2 = &id2;
        temp.f = static_cast<_R (Self::*)( _A1&, _A2& )>( f );
        temp.priority = priority;
        temp.symetric = symetric;
        v.push_back( temp );
    }

private:
    std::vector<Group> v;
};

}; // class 'dispatcher'

The template class has a vector which contains information about which
member function to call in case a certain combination of objects is received
with ofcourse there correct typeid's. It's as easy as it can get.
The dispatch information must be set by a derived class of dispatcher.

eg. A game

class IGameObject { ... };
class IShip : public IGameObject { ... };
class IAsteroid : public IGameObject { ... };
class IStation : public IGameObject { ... };
class IMilitaryShip : public IShip { ... }; // a militaryship must act like
a ship


class CollisionEngine : public dispatcher<IGameObject,IGameObject,bool>
{
    typedef CollisionEngine Self;

public:
    CollisionEngine();

protected:
    virtual bool ShipShip( IGameObject&, IGameObject& );
    virtual bool ShipStation( IGameObject&, IGameObject& );
    virtual bool ShipAsteroid( IGameObject&, IGameObject& );
    virtual bool MilitaryShipAsteroid( IGameObject&, IGameObject& );
};

CollisionEngine::CollisionEngine()
    : dispatcher<IGameObject,IGameObject,bool>()
{
    Register( typeid(IMilitaryShip), typeid(IAsteroid),
&Self::MilitaryShipAsteroid );
    Register( typeid(IShip), typeid(IShip), &Self::ShipShip );
    Register( typeid(IShip), typeid(IStation), &Self::ShipStation );
    Register( typeid(IShip), typeid(IAsteroid), &Self::ShipAsteroid );
}

You see that this system is very flexible, you can even derive a class of
the
CollisionEngine and redefine the member functions, so a new behavior
emerges. The huge difference with other double dispatch systems tried
in C++ is the simplicity by which you can extend new behavior.
You can have five points of polymorphism with this system:
- in the two hierarchies
- in the dispatcher (using Register)
- in the dispatcher (virtual member functions)
- using interface classes

As you can see, with this system it's easy to implement double dispatching
in
C++. So, as a conclusion, I would like to have the standard extended.
First the member has_public_parent should be added to type_info.
Secondly it would be nice if the standard also added a double dispatcher.

If you have any questions, please feel free to ask.

Chris Minnoy
cminnoy@starlab.net
cminnoy@vub.ac.be
http://www.starlab.net

Note:
all information provided here is as is. No warranty can be provided nor does
any responsibility lays on the shoulders of the author.
All information provided may not be used in any product without
the written premission of the author.
However, the ANSI and ISO commision responsible for defining the C++
standard
may use, spread, change, or add  this information to the standard as they
feel like,
without any permission needed by the author.



[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]