3

Consider the following code:

class A {
public:
    virtual ~A() {}
};

class AA : public A {
};

////////////////////////////////////////

class B {
public:
    virtual void f(const A &a) {
        // code for A
    }
};

class BB : public B {
public:
    virtual void f(const AA &a) {
        // code for AA
    }
};

////////////////////////////////////////

int main() {
    A *a = new AA;
    B *b = new BB;

    b->f(*a);
}

Obviously, the vtables are constructed such that when the above is run, // code for A is executed. I am looking for a way to be able to execute instead // code for AA.

The motivation is that this is for a library of code where the end-user will often have to write classes of the form BB, and I would like the process to be as easy as possible (i.e. the user should not have to use RTTI to figure out what derived class of A they are dealing with). Any ideas (and voodoo from any version of the C++ standard) are appreciated.

utnapistim
  • 26,809
  • 3
  • 46
  • 82
  • maybe something like `b->*(BB::&f)(*a)` or even explicity casting `static_cast<(void*)(const AA&)>(b->f)(*a)`? – Valerij May 05 '14 at 18:53
  • 3
    Ask yourself if you *really* need to pass a reference to a `AA` to `BB::f()` instead of just passing a reference to an `A`. Or vice versa. – John Dibling May 05 '14 at 18:54
  • Could the function in `B` take an `AA`? How are the `BB` and `AA` classes related? – Alan Stokes May 05 '14 at 18:57
  • C++ does not support this type of covariance. – Captain Obvlious May 05 '14 at 18:57
  • 1
    Visitor pattern? (Depends how many classes derive from `A`, and how often the set changes.) – Alan Stokes May 05 '14 at 18:58
  • @Valerij: Unfortunately that is not an option. –  May 05 '14 at 18:59
  • 1
    @AlanStokes Eventually visitor pattern + a clever template metaprogramming mechanism? – πάντα ῥεῖ May 05 '14 at 18:59
  • @JohnDibling: Unfortunately yes –  May 05 '14 at 19:00
  • i vote for this will never work as `a` beeing an `A*` 'forgot' it is really an `AA`, same with `b`, so to get `BB::f(AA)` called you would need to cast them back to `AA*` and `BB*` which makes the initial type erasure useless, just keep them an `AA*` etc – Valerij May 05 '14 at 19:02
  • @par: I doubt it. The more likely explanation is that your design is broken. If you think your design isn't broken and you really do have a unique situation here, then elaborating more on your actual use case may help to garner real responses. – John Dibling May 05 '14 at 19:04
  • @JohnDibling: This is for a library. A is a generic description of a mathematical problem, while AA is a specific one and BB is a solver for that problem. However, BB might also be a solver for other problems. Users of the library will want to implement their own solvers, hence why I wanted any dynamic_casting to be hidden if possible. Judging from the conversation, it seems that this might not be possible, however. –  May 05 '14 at 19:10
  • One possible solution is to have a static const id associated with classes deriving A and have the user "register" associations between ids and function pointers. That way they can implement things like void my_function(const A &a) { /* perform dynamic cast and write code */ } which isn't too bad –  May 05 '14 at 19:13
  • 1
    @par: Right, so pass a reference to an `A` to `BB`, and then from `BB` call methods on `A` which will be dynamically bound to methods on an instance of `AA`. I'm still not seeing why this isn't possible in your use case. – John Dibling May 05 '14 at 19:17
  • @JohnDibling: +1 That is one solution. It's not ideal for this case because there is not one good way to capture what derived classes of ```B``` will need from derived classes of ```A``` a priori. This would just become ```A``` being a hashtable of fields (prototype-like). This might be a good way to go; I'm essentially debating between prototypes and the dynamic_cast solution I wrote about above. –  May 05 '14 at 19:29
  • @par: Discussing this in the abstract is difficult. However, `A` should provide public `virtual` methods for all of these "a priori" things that `B` needs to know about `A`. – John Dibling May 05 '14 at 19:33

4 Answers4

5

You can use RTTI and explicitly doing your own dispatching for that.

Co-Variant types only work for return types unfortunately.

Example:

class B {
    void f_base(const A &a) {
        // code for A
    }
public:
    virtual void f(const A &a) {
        f_base(a); // Moved the base case outside
                   // to isolate the dispatching mechanism.
    }
};

class BB : public B {
public:
    virtual void f(const A& a) {
        const AA* pAA = dynamic_cast<const AA*>(&a);
        if(pAA) {
            f(*pAA);
            return;
        }
        f_base(a);
    }
    void f(const AA &a) {
        // code for AA
    }
};

More discussion of this type of dispatch as well of nicer packaging with templates is demonstrated here: "type-switch" construct in C++11

Community
  • 1
  • 1
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
3

You need something that resembles the mechanism that is known as double dispatch, which in C++ can only be implemented indirectly:

class A {
public:
    virtual callF(B & b) {
        b.f(this);
    }

    virtual callF(BB & b) {
        b.f(this);
    }
};

class AA : public A {
public:
    virtual callF(B & b) {
        b.f(this);
    }

    virtual callF(BB & b) {
        b.f(this);
    }
};
Aasmund Eldhuset
  • 37,289
  • 4
  • 68
  • 81
3

This is horrible style but it seems you are under some tight constraints

#include <typeinfo>

 // everything else unmodified 

class BB : public B {
public:
    virtual void f(const A& arg) override {
        try { //try to convert to an AA&
            const AA& a{dynamic_cast<const AA&>(arg)};
            // code for AA
        } catch (std::bad_cast) { // if it fails, pass it up to B::f as an A&
            this->B::f(arg);
        }
    }
};

////////////////////////////////////////

int main() {
    A *aa = new AA;
    A *a = new A;
    B *b = new BB;

    b->f(*a);  // code for A executed
    b->f(*aa); // code for AA executed
}

as per Alan Stoke's comment, dynamic casting pointers is much faster when it fails, so you could alternatively use this if you expect failure regularly:

class BB : public B {
public:
    virtual void f(const A& arg) override {
        const AA* ap = dynamic_cast<const AA*>(&arg);
        if (ap == nullptr) {
            return this->B::f(arg);
        }
        const AA& a{*ap}; // get a reference, if you want to
        // code for AA
    }
};
Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
  • +1 Thanks! I have considered this as an option (equivalent to using RTTI and then dynamic casting), but I wanted to avoid it if possible. –  May 05 '14 at 19:08
  • 2
    `dynamic_cast` on pointers returns null on failure rather than throwing, which is almost certainly faster - which is important when you expect it to happen. – Alan Stokes May 05 '14 at 19:16
  • Why `ap == nullptr` and not `!ap`? – Deduplicator May 05 '14 at 19:28
  • @Deduplicator one is arguably more explicit/expressive than the other but it doesn't really matter, does it. – Ryan Haining May 05 '14 at 19:28
  • @RyanHaining: I just hate pointlessly writing twice as much code, especially if I have to read it again. More expressive? Cannot concurr. Extra fun when you leave out one half the `==`, though any decent compiler with warnings enabled will catch it. – Deduplicator May 05 '14 at 19:30
  • 1
    @Deduplicator to each his own I suppose. I never minded typing more characters if I felt it meant something. I just have no strong feelings on this particular point. – Ryan Haining May 05 '14 at 19:37
1

The concept you are looking for is called double dispatch. Read more about it on http://en.wikipedia.org/wiki/Double_dispatch

It is not built into C++ but there are various ways to emulate it. One of them being the visitor pattern also found in the above link.

However you'll probably find all approaches lacking of elegance because you need to introduce AA to B, BB to A, or use RTTI and casts.

Jonas Bötel
  • 4,452
  • 1
  • 19
  • 28