views:

786

answers:

5

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.

+3  A: 

Cast to a T* first then to A:

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

If IB in general should be castable to A, then maybe IB should inherit from A.

Edit: I just tried this and it works - note that E is unknown at the time of compiling the main method.

struct A
{
    virtual ~A() {}
};

struct C
{
    virtual ~C() {}
};

A* GetA();

int main()
{
    C *y = dynamic_cast<C *>(GetA());
    if (y == NULL)
     cout << "Fail!";
    else
     cout << "Ok!";
}

struct E : public A, public C
{
}; 

A* GetA() { return new E(); }
Eclipse
Good answer, but unfortunately, my arch is more complicated than the example. T is the implementation of a plugin API which is not accessible where this is wanted.
Evan Teran
So you're saying you have some type T that you know inherits from A and IB, but can't access T?
Eclipse
yes, my plugin architecture (QT based) is such that a plugin is inherits from A (QObject), and implements my interface. Then later, the QT system gives me an instance (originally a QObject *, which I've dynamic casted to my plugin Interface type.
Evan Teran
Unfortunately, I have a plugin which implements 2 interfaces, and the second doesn't have any real relation to the primary plugin interface.
Evan Teran
+5  A: 

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...

Daniel Paull
Yes, they do have virtual methods. Both interfaces are comprised entirely of pure virtual functions (and a virtual destructor which does nothing).
Evan Teran
Interesting that your example works though.. I need to look into my code more though, since I believe that it matches my scenario.
Evan Teran
And class A? If all classes, including the final class have virtual methods, then you got me beat. It should just work. Was something compiled without RTTI?
Daniel Paull
yeah, I even added a method to A and called it to make sure that the cast succeeded. Maybe write a small command line app and make sure the above example works with your environment.
Daniel Paull
I beleive that rtti is enabled (otherwise I'd get an error on the dynamic_cast right?)
Evan Teran
What kind of error are you getting? A null pointer or an exception?
Eclipse
@Daniel Paull, how do you know it works? You never test a_ptr. dynamic_cast will return null on failure.
zdan
A call to dyamic_cast<> should be a compiler warning if RTTI is off. However, I'm not sure what would happen if the framwork was compiled with RTTI and the plugin without. Do you get an exception thown? Or a null return? Or a crash? I don't know what the expected behavior is!
Daniel Paull
@zdan: the debugger showed that returned a non-null value, and as commented above, I even added a method to class A and called it to make sure the pointer was sane. I will amend the code in the post to reflect this.
Daniel Paull
I just get a NULL pointer result from it. :(
Evan Teran
What compiler are you using?
Eclipse
@Evan: I think you should go back to basics and try compiling the above code and verify that it works for you - if it does, go hunting for the problem in your app. If it fails, go hunting for the problem in your tools!
Daniel Paull
I'm using g++ (4.3.2) and the examples you guys have given work just fine. I am guessing it's an issue with the object being from different shared libraries.
Evan Teran
It should work fine across DLL boundaries so long as both DLL contain RTTI information. Man, you're in a tough situation because your dynamic cast should work just fine... I'm not sure how you should proceed other than start reading up on RTTI for your compiler.
Daniel Paull
Yea, I know that QT lets you disable RTTI, but I think it's enabled by default. I'll have to double check all this. (I do see "typeinfo for ________" entries in each plugins nm output). I'll just have to keep plugging at it.
Evan Teran
Good luck with this - when you crack it, please post your findings as I'm itching to know what caused this behavior.
Daniel Paull
+4  A: 

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.

I take it then that the definitions of IB and IC are under your control.

There's the way in which COM interfaces work on Windows; these do what you are wanting want to do, i.e.:

  • Cast from one interface to another
  • Implementation is opaque to the caller
  • Only the implementation knows which interfaces it implements

Do do this, you can do something like (untested code ahead) ...

interface IQueryInterface
{
  IQueryInterface* queryInterface(const Guid* interfaceId);
};

interface IB : public abstract IQueryInterface
{
  ...
};

interface IC : public abstract IQueryInterface
{
  ...
};

//within your implementation class
IQueryInterface* T::queryInterface(const Guid* interfaceId)
{
  if (matches(interfaceId,GUID_IB))
    return (IB*)this;
  if (matches(interfaceId,GUID_IC))
    return (IC*)this;
  if (matches(interfaceId,GUID_A))
    return (A*)this;
  return 0;
}

A much simpler, more hard-coded version of this would be:

class A; //forward reference
interface IB
{
  virtual A* castToA() { return 0; }
};
class T : public A, IB, IC
{
  virtual A* castToA() { return this; }
};
ChrisW
yes, the "castToA" method looks like a viable option if dynamic_cast can't work.
Evan Teran
+2  A: 

I finally figure it out Daniel Paull was correct in that a "sideways dybnamic_cast" should be allowed. My problem was because my code is involving shared libraries. The typeinfo from PluginA was not available in PluginB. My solution was to effectively add RTLD_NOW and RTLD_GLOBAL to my load process

technically it was

loader.setLoadHints(QLibrary::ResolveAllSymbolsHint | QLibrary::ExportExternalSymbolsHint);

because I'm using Qt's plugin system but same difference. These flags force all symbols from loaded libraries to be resolved immediately and be visible to other libraries. Therefore making the typeinfo available to everyone that needed it. The dynamic_cast worked as expected once these flags were in place.

Evan Teran
Glad ti hear you got it figured out!
Eclipse
+1  A: 

I've also recently been bothered with the same kind of problem. For more information, see GCC's FAQ entry:

http://gcc.gnu.org/faq.html#dso

Besides instructing dlopen with RTLD_* flags, some incarnations of this problem can be solved by the linker as well, see its -E and -Bsymbolic options.

Gert