Click here to Skip to main content
15,881,089 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi,

I have a problem with virtual inheritance between 2 compilers. The 2 compilers are Visual Studio 2010 and Borland C++ 2009 (Embarcadero).

I declare the 3 interfaces below :
C++
class IA
{
public:
	virtual void __stdcall methodInA() = 0;
};

class IB : public virtual IA
{
public:
	virtual void __stdcall methodInB() = 0;
};

class IC : public virtual IA
{
public:
	virtual void __stdcall methodInC() = 0;
};


I implements an object D in a dll compiled with Visual Studio :
C++
class D : public virtual IB, public virtual IC
{
public:
	D()  
	{ 
		OutputDebugStringA("D created : ");
		char tmp[32];
		sprintf(tmp,"this=0x%08X\r\n",this);
		OutputDebugStringA(tmp);
	}
	~D() 
	{ 
		OutputDebugStringA("D deleted : ");
		char tmp[32];
		sprintf(tmp,"this=0x%08X\r\n",this);
		OutputDebugStringA(tmp);
	}

	virtual void __stdcall methodInA() 
	{ 
		char tmp[64];
		sprintf(tmp,"D::methodInA() : this=0x%08X\r\n",this);
		OutputDebugStringA(tmp);
	}
	virtual void __stdcall methodInB() 
	{ 
		char tmp[64];
		sprintf(tmp,"D::methodInB() : this=0x%08X\r\n",this);
		OutputDebugStringA(tmp);
	}
	virtual void __stdcall methodInC() 
	{
		char tmp[64];
		sprintf(tmp,"D::methodInC() : this=0x%08X\r\n",this);
		OutputDebugStringA(tmp);
	}
};


in the dll, one method is exported to get a pointer on one of the interface IA, IB, or IC from an instance of D.

Everything works perfectly if the dll is used in a program compiled with Visual Studio.

But when I load the dll in a Borland C++ program:
if I get a pointer on the IA interface, I can call methodInA: OK:)
if I get a pointer on the IB interface, call to methodInB fails!:( (in fact, call to methodInA fails too)

Using disassembly on both side (a borland client program and a VS client program), I found differences to access the virtual tables of the interface IB.

My question is: Does any one know if there is an option in one of the 2 compilers to make them compatible? And Removing virtual inheritance is not an answer :).

Remark: __stdcall is a way I found to allow method call between Borland Prog and Visual dll (same way to put args on the stack)
Visual Studio uses "thiscall" by default for the class methods, it is equivalent to __stdcall except for the this pointer put in register ecx and not in the stack.


After removing virtual inheritance:
C++
#define iid(intf,iid) const int iid_##intf = iid
#define iidof(intf) (iid_##intf)

iid(IA,0);
class IA
{
public:
	virtual void* __stdcall Query(int iid) = 0;
	virtual int __stdcall AddRef() = 0;
	virtual int __stdcall Release() = 0;
	virtual void __stdcall methodInA() = 0;
};

iid(IB,1);
class IB : public IA
{
public:
	virtual void __stdcall methodInB() = 0;
};

iid(IC,2);
class IC : public IA
{
public:
	virtual void __stdcall methodInC() = 0;
};


If I create a class D like this one, it will work.
C++
class D : public IA, public IB, public IC
{
protected:
    int refCount;
    virtual ~D() 
    { 
    }
public:
    D() : refCount(0)
    { 
    }

    virtual void* __stdcall Query(int iid)
    {
          char tmp[128];
          void *ptr=NULL;
          switch(iid)
          {
          case iidof(IA):
               ptr=(IA*)this;
               break;
          case iidof(IB):
               ptr = (IB*)this;
               break;
          case iidof(IC):
               ptr = (IC*)this;
               break;
          }
          if(ptr!=NULL) AddRef();
          return ptr;
     }
     virtual int __stdcall AddRef()
     {
          return ++refCount;
     }
     virtual int __stdcall Release()
     {
          int lRefCount=--refCount;
          if(lRefCount==0)
               delete this;
          return lRefCount;
     }
     virtual void __stdcall methodInA() 
     { 
     }
     virtual void __stdcall methodInB() 
     { 
     }
     virtual void __stdcall methodInC() 
     {
     }
};

But if I want to create an A class like this one, to get all common task of the IA interface in one class:
C++
class A : public IA
{
protected:
	int m_refCount;
	virtual ~A()
	{
	}
public:
	A() : m_refCount(0)
	{ 
	}

	virtual int __stdcall AddRef() 
	{ 
		++m_refCount;
		return m_refCount;
	}
	virtual int __stdcall Release()
	{
		int refCount = --m_refCount;
		if(refCount==0)
			delete this;
		return refCount;
	}
};


If I inherit the D class from A (instead of IA), I need to reimplement the AddRef and Release because the compiler will not associate the IB::AddRef needed in D with the AddRef from A
Error message from Visual Studio Compiler:
error C2259: 'D' : can not instantiate an abstract class
          because of the following members :
          'int IA::AddRef(void)' : is abstract
          virtualinheritance.h(12) : see declaration of 'IA::AddRef'
          'int IA::Release(void)' : is abstract
          virtualinheritance.h(13) : see declaration of 'IA::Release'
          'int IA::AddRef(void)' : is abstract
          virtualinheritance.h(12) : see declaration of 'IA::AddRef'
          'int IA::Release(void)' : is abstract
          virtualinheritance.h(13) : see declaration of 'IA::Release'

the actual error message may be different, it is translated from french

The following D class is OK:
C++
class D : public A, public IB, public IC
{
protected:
	virtual ~D() 
	{ 
	}
public:
	D() 
	{ 
	}

	virtual void* __stdcall Query(int iid)
	{
		void *ptr=NULL;
		switch(iid)
		{
		case iidof(IA):
			ptr=(IA*)(A*)this; // cannot convert directly from D to IA because IA is also available from IB and IC
			break;
		case iidof(IB):
			ptr = (IB*)this;
			break;
		case iidof(IC):
			ptr = (IC*)this;
			break;
		}
		if(ptr!=NULL) A::AddRef();
		return ptr;
	}
	virtual int __stdcall AddRef()
	{
		int result = A::AddRef();
		return result;
	}
	virtual int __stdcall Release()
	{
		int result = A::Release();
		return result;
	}
	virtual void __stdcall methodInA() 
	{ 
	}
	virtual void __stdcall methodInB() 
	{ 
	}
	virtual void __stdcall methodInC() 
	{
	}
};

But there is less interest in creating the A class if you need to add all the methods from the IA in the final class.
Posted
Updated 23-Nov-12 1:25am
v3

There is no standard for C++ linkages in DLLs. You should expect problems when trying to mix calls between Microsoft Classes and Borland Classes in DLLs.

Use C helper functions to standardize the calls between the two.
 
Share this answer
 
Comments
Pascal-78 21-Nov-12 15:47pm    
The only problem is with virtual inheritance. Remove all the "virtual" keyword in class inheritance (not in the method definition) and it will work with a Microsoft DLL and a Borland Program. It seems that virtual table constructions are compatible at the method level but not at the class level. I think that why the call to IA::methodInA still works with virtual inheritance because IA does not have any inheritance.
You can use COM/Active X and follow related rules for binary compatibility.

In your case, if you want to derive virtually, one way to navigate between interface would be to have another interface (that does not derive virtually) and methods in that interface would allows you to get a pointer to other interfaces.

This would be similar to the purpose of QueryInterface in ActiveX...

Or alternatively add another interface that has all methods in each interfaces and only use that interface from the other language (calling code).


By the way if IA, IB and IC are interfaces, there is no purpose to derives virtually. IA should not have any data. You might have a class A that implement IA and have your class D derives from A...

But from the other side, you should only uses interface that does not contains data (as it is the case in ActiveX interfaces).
 
Share this answer
 
Comments
Pascal-78 22-Nov-12 3:34am    
Exactly what I meant.

IA is in fact similar to the IUnknown of the COM/ActiveX with the AddRef, Release and QueryInterface methods.

Virtual inheritance is used because "interface" is not really available in C++ like it is in C# or Java. Without virtual inheritance, I will have 2 "IA" in the D class (one from IB, and the other one from IC).

If I do not inherit IB and IC from IA and add all the IA methods in IB and IC, I create an A class with partial implementation of IA (the AddRef and Release method), I want to create the D class with A as a base class (to avoid rewriting common method each time I create a new class with IA interface) and IB and IC as new interfaces.
class D : public A, public IB, public IC;
in this case D is still abstract because AddRef and Release from IB and IC are not implemented in D but in A
Philippe Mori 22-Nov-12 8:34am    
Effectively as in your last sample, you will have to have small inline function to remove conflicts...

On the other hand, you can always remove conflict by not having IB or IC derives from IA and have each interface implement a single functionality.

By the way, I would recommand you to uses ATL and do standard ActiveX/COM interfaces instead of reiventing the Wheel. Every thing that is needed for binary compatibility is already provided and the compiler/librairie will help a lot do reduce writting redudant code.
Pascal-78 22-Nov-12 11:56am    
I must agree. At the beginning of the project ActiveX/COM interfaces seem to be too much, but now with the compiler interactions, it looks like the easiest solution (without removing one of the compilers :) ).
Philippe Mori 22-Nov-12 15:38pm    
10 years ago, I was using ActiveX/COM for interaction between Borland and Microsoft and also to have an architecture with component that are less coupled...

Presently, I only uses Visual Studio (C# and C++/CLI) for .NET development... and thus it is a bit easier.
Pascal-78 22-Nov-12 15:54pm    
I already tried to use ActiveX/COM with Borland, but I get some difficulties (same kind as __stdcall). Do you know the option in Borland C++ 2009 to avoid this trick?

I would like very much to use C# but it is difficult to rewrite an existing software (written with Borland and its old VCL). That's why we want to extract some part of code in DLLs compiled with Visual Studio to get less and less code with the VCL (still without 64bit OS support)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900