Question

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.

Was it helpful?

Solution 4

I finally figured 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.

OTHER TIPS

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

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; }
};

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(); }

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top