Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dynamic cast with interfaces

I have a class with implements 2 interfaces and inherits 1 class. So, generally it looks like this:

class T : public A, public IB, public IC {
};

There is one point in the code where I have an IB *, but could really use an A *. I was hoping that a dynamic cast would like this:

IB *b_ptr = new T; // it's really more complicated, but serves the example
A *a_ptr = dynamic_cast<A *>(b_ptr);

unfortunately, this doesn't work. Is there a proper way to do this? Or should I implement a work around? I've thought about having both IB and IC inherit virtually from A, but IIRC last time I tried that there were some complications that made it undesirable.

Any thoughts?

EDIT: oh yea, this is part of a plugin API, so unfortunately I don't have direct access to the T type where I need the A *. My example has then next to each other, but as mentioned, it's more complicated. Basically I have 2 shared libraries. T and T1 (where I have an IB *) are both classes which implement a plugin API and are internal to the shared libraries.

To clarify: Here's a more specific example of my typical plugins (they are in separate libraries):

plugin A:

class PluginA : public QObject, public PluginInterface, public OtherInterface {
};

plugin B:

class PluginB : public QObject, public PluginInterface {
    // in here, I have a PluginInterface *, but really could use a QObject *
    // unfortunately, PluginB has absolutely no knowledge of the "PluginA" type
    // it just so happens that my PluginInterface * pointer points to an object of type
    // PluginA.
};

EDIT: I have a guess that the issue is that pluginA and pluginB are in different shared libraries. Perhaps the rtti doesn't cross module boundaries. I think this might be the case because people's examples seem to work fine in my tests. Specifically, pluginB has no "typeinfo for PluginA" if I do an "nm" on it. This may be the core of the issue. If this is the case, I'll simply have to work around it by either virtual inheritance or a virtual cast_to_qobject() function in one of my interfaces.

like image 880
Evan Teran Avatar asked Jan 14 '09 05:01

Evan Teran


1 Answers

Does each class have at least one virtual method? If not, there's your problem. Adding a virtual destructor to each class should overcome the problem.

The following happily worked for me:

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

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

class A
{
public:
    virtual ~A() {}
    void foo() { /* stick a breakpoint here to confirm that this is called */ }
};

class T : public A, public IB, public IC 
{
public:
    virtual ~T() {}
};


int main(void)
{
    IB *b_ptr = new T;
    A *a_ptr = dynamic_cast<A *>(b_ptr);
    a_ptr->foo();
    return 0;
}

EDIT:

After all the new info, and the unusual behavior (your code should just work!), does the following help? I've introduced an interface called IObject and use virtual inheritance to ensure that there is only one copy of this base class. Can you now cast to IObject and then to A?

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

class IC : virtual public IObject
{
public:
    virtual ~IC() {}
};

class IB : virtual public IObject
{
public:
    virtual ~IB() {}
};

class A : virtual public IObject
{
public:
    virtual ~A() {}
    void foo() { /* stick a breakpoint here to confirm that this is called */ }
};

class T : virtual public A, virtual public IB, virtual public IC
{
public:
    virtual ~T() {}
};


int main()
{
    IB *b_ptr = new T;
    A *a_ptr = dynamic_cast<A *>( dynamic_cast<IObject *>(b_ptr) );
    a_ptr->foo();
    return 0;
}

I'm not suggesting that it's the right solution, but it might offer some info as to what's going on...

like image 55
Daniel Paull Avatar answered Sep 22 '22 00:09

Daniel Paull