views:

537

answers:

4

I have hierarchy of public interfaces like this:

struct ISwitchable {
    /* Obtain pointer to another implemented interface of the same instance. */
    virtual int switch(unsigned int interfaceId, void** pInstance) = 0;
};
struct IFoo : public ISwitchable { /* Methods */ };
struct IBar : public ISwitchable { /* Methods */ };
struct IFooBar : public IFoo, public IBar { /* Methods */ };

Class implementing IFooBar is placed into dll along with factory function. Client code loads dll, uses factory function to create class instance and use it according interfaces (they are supplied as a header file).

Scheme works fine with dll made by MSVC and client code made by Borland C++ Builder 6.

I introduce virtual inheritance into hierarchy:

struct IFoo : public virtual ISwitchable { /* Methods */ };
struct IBar : public virtual ISwitchable { /* Methods */ };

And when in the same situation (dll by MSVC, client by Builder) client code requests instance of class he gets it with messy vtable.

Is there any solution except of rollback to ordinary inheritance?

A: 

Probably will not answer your question ... But is is strongly recommended to NOT use multiple inheritance as you do (called "dreaded diamond").

dilig0
In fact the DDD is only a problem when the multiple base class objects contain member data -- if they don't, it doesn't matter which object is chosen, as the methods (both virtual and plain) are all the same. The only issue you will face is that the compiler won't let you upcast to one of these bases without an intermediate cast, i.e. you need to write "(ISwitchable*) (IFoo*) pFooBar" -- annoying, but not life-threatening.
j_random_hacker
Why not? Look at what is being multiply inherited: the C++ equivalent of a Java interface. It should be safe.
David Thornley
Indeed my answer was wrong.As said in the first comment, problems with multiple inheritance raise when accessing base class data members. Thus this issue does not exist with interfaces. This weakness of C++ has been eliminated by the java language by accepting single inheritance but multiple interface implementations.
dilig0
+8  A: 

I didn't think that you could count on any built classes being compatible across compilers. Does Borland claim that they can load and interoperate with classes built by MSVC. If so, looks like they have a bug. As far as I know, nothing about the exact structure of the VTable is in the spec of C++, so it isn't expected to work across compilers.

Lou Franco
+5  A: 

Unlike for C, there is no cross-compiler ABI for C++ -- compilers are free to implement virtual inheritance (and even ordinary inheritance) any way they want.

The upshot is: calling C++ functions across compilers is not guaranteed to work. I know it's ugly, but if you want your DLL to interact happily with multiple compilers, you had probably better provide a set of plain extern "C" functions and manually-built tables of function pointers instead.

Note: Compilers that support building COM objects (or have an option to do so) are more constrained in their object layouts. (I know that recent versions of MSVC++ produce COM-compliant objects, at least in most cases -- not sure if virtual inheritance is covered though.)

j_random_hacker
The way COM handles it is by making the object do its own casting inside QueryInterface. You get clean, simple VTable's from QueryInterface to the interface you requested -- you aren't supposed to cast to others, you keep calling QI. The client doesn't know concrete class, it just has a pointer to the interface -- it doesn't care that the concrete class uses virtual inheritance.
Lou Franco
@Lou: Absolutely, but it's still the case that supporting COM means that the compiler is obliged to lay out its vtable as a contiguous array of function pointers, with specific alignment requirements (= no padding) and with entries in a specific order -- requirements that are not stipulated by the C++ standard.
j_random_hacker
Correct, but you only get one interface at a time -- meaning that the real concrete class might have a complex VTable, but it gets converted to the COM style through the cast which can't be to a multiply inherited class. So, compilers could layout multiply inherited vtables any way they want, as long as when I cast to a specific base class, I get the simple one.
Lou Franco
Yes -- AFAIK, only the layout of single-inheritance vtables is constrained by COM.
j_random_hacker
There is definitely a cross-compiler ABI for C++, the Itanium one. And I think variants of it are used by multiple compilers on other CPUs.
MSalters
This is a really good source of information, when it comes to virtual member function pointers, across different compilers.http://www.codeproject.com/KB/cpp/FastDelegate.aspx
TommyA
A: 

I'm leery of the void** argument. Using void pointers loses type information.

If you're working with multiple inheritance then type information can be important. Consider:

class Foo { ... }; class Bar { ... };

class Both: public Foo, public Bar { ... };

Let's assume that internally the layout of a Both instance is a Foo instance followed by a Bar instance. I can pass a Both* to a method expecting a Foo* without problems. I can also pass a Both* to a method expecting a Bar*, provided that I adjust the pointer to point to the embedded Bar. I can do this reliably because I know I'm working with a Both.

Now:

Foo *foo = new Both(...); Bar *bar = new Both(...);

void *p = foo; void *q = bar; Both both = (which) ? (Both)p : (Both*)q;

So: how do I know how to adjust either p or q when I assign to "both"? I can't because the type information is lost going through the void pointer.

A variant of this problem might be related to the problems you're having.

d3jones