Double Dispatch Obsolete?

Discussion in 'C++' started by DeMarcus, Sep 4, 2007.

  1. DeMarcus

    DeMarcus Guest

    Since I started with OO I've been told switching on typeid is a big
    no-no. E.g.

    void Washer::wash( Vehicle myVehicle )
    {
    if( typeid(myVehicle) == typeid(Car) )
    Washer::washCar( myVehicle );
    else if( typeid(myVehicle) == typeid(Bike)
    Washer::washBike( myVehicle );
    else if( typeid(myVehicle) == typeid(Boat)
    Washer::washBoat( myVehicle );
    }

    The alternative is the more correct Double Dispatch. E.g.

    void Washer::wash( Vehicle myVehicle )
    {
    myVehicle.washer( this )
    }

    void Car::washer( Washer w )
    {
    w.washCar( this );
    }

    Now, consider we change Washer to XMLConverter and wash() to write().
    This will still work, but when we want to go backwards and read XML and
    write a Vehicle we need to switch on some kind of type id label anyway. E.g.

    Vehicle XMLConverter::readVehicle( XMLdoc doc )
    {
    Vehicle v;
    string s = doc.readAttr();
    if( s == "Car" )
    v = new Car();
    else if( s == "Bike" )
    v = new Bike();
    else if( s == "Boat" )
    v = new Boat();

    return v;
    }

    So why not just give every MyObject a typeName() method and switch or
    std::map<char*, fncPtr> on that throughout all dispatchers?


    //Daniel
     
    DeMarcus, Sep 4, 2007
    #1
    1. Advertising

  2. DeMarcus wrote:
    > Since I started with OO I've been told switching on typeid is a big
    > no-no. E.g.
    >
    > void Washer::wash( Vehicle myVehicle )
    > {
    > if( typeid(myVehicle) == typeid(Car) )
    > Washer::washCar( myVehicle );
    > else if( typeid(myVehicle) == typeid(Bike)
    > Washer::washBike( myVehicle );
    > else if( typeid(myVehicle) == typeid(Boat)
    > Washer::washBoat( myVehicle );
    > }


    Yes - no-no - not extensible. Code like this tends to proliferate and
    is prone to error.

    >
    > The alternative is the more correct Double Dispatch. E.g.
    >
    > void Washer::wash( Vehicle myVehicle )
    > {
    > myVehicle.washer( this )
    > }
    >
    > void Car::washer( Washer w )
    > {
    > w.washCar( this );
    > }


    Kind of.

    It would be more like:

    myVehicle.DoSomthing( Wash ).

    void Car::DoSomthing( Dispatcher & i_dispatch )
    {
    i_dispatch.WashCar( * this );
    }

    >
    > Now, consider we change Washer to XMLConverter and wash() to write().
    > This will still work, but when we want to go backwards and read XML and
    > write a Vehicle we need to switch on some kind of type id label anyway.
    > E.g.
    >
    > Vehicle XMLConverter::readVehicle( XMLdoc doc )
    > {
    > Vehicle v;
    > string s = doc.readAttr();
    > if( s == "Car" )
    > v = new Car();
    > else if( s == "Bike" )
    > v = new Bike();
    > else if( s == "Boat" )
    > v = new Boat();
    >
    > return v;
    > }


    This is usually solved by a generic factory system like Austria C++'s
    factory thing.

    v = at::FactoryRegister< Interface, std::string >::Get().Create( s )();

    >
    > So why not just give every MyObject a typeName() method and switch or
    > std::map<char*, fncPtr> on that throughout all dispatchers?


    That can be one way. If done correctly, the double dispatch technique
    is able to pick up when you miss a case by using pure virtual methods.
    In this example if a new type of vehicle is make and the method is not
    implemented, it can cause a compile time error which can flag missing
    actions.
     
    Gianni Mariani, Sep 4, 2007
    #2
    1. Advertising

  3. On 2007-09-04, DeMarcus <> wrote:
    > The alternative is the more correct Double Dispatch. E.g.
    >
    > void Washer::wash( Vehicle myVehicle )
    > {
    > myVehicle.washer( this )
    > }


    This isn't double dispatch, the parameter is being passed by value so
    if you are attempting to pass in something derived from Vehicle, this
    will be "sliced" into a base Vehicle in any case.

    >
    > void Car::washer( Washer w )
    > {
    > w.washCar( this );
    > }


    OK, but this won't get called by the function above.

    > Now, consider we change Washer to XMLConverter and wash() to write().
    > This will still work, but when we want to go backwards and read XML and
    > write a Vehicle we need to switch on some kind of type id label anyway. E.g.
    >
    > Vehicle XMLConverter::readVehicle( XMLdoc doc )
    > {
    > Vehicle v;
    > string s = doc.readAttr();
    > if( s == "Car" )
    > v = new Car();
    > else if( s == "Bike" )
    > v = new Bike();
    > else if( s == "Boat" )
    > v = new Boat();
    >
    > return v;
    > }


    When you are creating new objects from a serialized form you do have
    to workout exactly what type of object you want to create. This has
    to involve some sort of interpretation of a flat data format an making
    a decision based on that. Again, this code appears to be wrong. The
    return type is returning Vehicle by value, so whatever derived type is
    created in the method, only a base Vehicle can be returned. The local
    variable v is an object type, not a pointer, so unless the base
    Vehicle class has a very unusual form of assignment operator taking a
    pointer to Vehicle or derived type then this won't compile.

    > So why not just give every MyObject a typeName() method and switch or
    > std::map<char*, fncPtr> on that throughout all dispatchers?


    A std::map of char* might not do what you think it does. You'd have
    to find the char* in a fixed list of char* that matched the string
    which you were looking for, where the fixed list was known to be the
    actually char* which were used in creating the map. std::map<char*...
    will be sorted by pointer value, not string value. Also, you can only
    use a switch with integral or enumeration types, so you would still
    need a method to convert a string to such a type.
     
    Charles Bailey, Sep 4, 2007
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Lorenzo Bettini
    Replies:
    0
    Views:
    375
    Lorenzo Bettini
    Jul 26, 2003
  2. Sydex
    Replies:
    12
    Views:
    6,528
    Victor Bazarov
    Feb 17, 2005
  3. Thomas Matthews
    Replies:
    1
    Views:
    900
    Victor Bazarov
    Feb 21, 2005
  4. AndyL
    Replies:
    1
    Views:
    306
    bruno at modulix
    Feb 16, 2006
  5. saneman
    Replies:
    5
    Views:
    316
    James Kanze
    May 9, 2008
Loading...

Share This Page