Greetings,

I recently posted a problem I was having with exporting classes from DLL built with BCB to client VC++ applications. I don't believe I did a good of explaining my problem. I am trying to extend the technique of exporting abstract base classes with virtual functions, to provide an inheritance hierarchy at the client side. This has been causing me a serious headache with respect to compatibility between BCB and VCC++.

However, I believe I have now solved the problem. Because I feel what I'm doing could have general application, I have written a short article describing my solution (below).

Please comment on this. My solution is a little clumsy, but I can't find a better way of doing things. Suggestions on how to simplify code maintenance, or a better approach altogether would be extremely welcome.

Thanks

Andy


EXPORTING OBJECT HIERARCHIES FROM A DLL
Andy Thomas 2001


1] Introduction
This article describes how to extend to a common technique for exporting C++ objects from a DLL to facilitate the export of object hierarchies. In particular, it discusses the incompatibility issue which arises between compiler used to build the DLL and client applications.

This article assumes the reader is familiar with the technique of exporting abstract base classes with virtual functions--an approach shared with COM. See www.bcbdev.com/articles/vcdll2.htm for an introductory discussion of this.

N.B. This text document should be viewed using a fixed pitch font to preserve alignment of ASCII diagrams.



2] An Example Client Interface
Shown below is an example of an object interface that we may like to export to a client application. Note that we are deriving Icar from Ivehicle.


class Ivehicle
{
public :
virtual int __stdcall speed() = 0;
virtual ~Ivehicle();
};

class Icar : public Ivehicle
{
public:
virtual int __stdcall wheels() = 0;
};


In addition, the DLL needs to export some flat C functions to allow the creation and destruction of objects, i.e.


Ivehicle* __stdcall NewVehicle();
Icar* __stdcall NewCar();
void __stdcall DestroyVehicle(Ivehicle* v);


Internal to the DLL, concrete implementation classes are defined which inherit from the abstract classes above, and provide the implementation of the virtual functions. When the NewVehicle function is called, it will actually create an instance of a concrete vehicle class, but return only the abstract base class interface for use by the client. In addition, provided the concrete classes have been defined with virtual destructors, the DestroyVehicle function can be used to free instances of all objects derived from a common base class.

The approach of exporting abstract base classes is commonly used in Windows to facilitate DLL compatibility with different compilers, because compilers tend to share the same virtual dispatching system, a feature relied upon by COM. However, what we have done here is to extend the basic approach to define an interface hierarchy. Compatibility issues arise unless care is taken at the server side.



3] Server Implementation Difficulties
Below is an [erroneous] example of how we may wish to implement the abstract classes at the server side.


class Cvehicle : public Ivehicle
{
public:

virtual int __stdcall speed();
virtual ~Ivehicle();
};

class Ccar : public Cvehicle, public Icar
{
public:
virtual int __stdcall wheels() = 0;
};


Note that we are now in the world of multiple inheritance because Ccar must inherit from both Cvehicle (to get the implementation of Ivehicle) and from Icar (to get its interface). This code will not compile because Ccar actually inherits two copies of Ivehicle and only one implementation is provided. The heirachy looks like this:


Figure 1:

Ivehicle Ivehicle
| |
| |
Cvehicle Icar
| |
| |
Ccar-------------


How do we resolve this? Normally in C++, when faced with multiple base class inheritance, we can inherit virtually. To do this we must redefine the abstract interface and implementation heirachy to look like this:


// Client Interface
class Ivehicle
...

class Icar : public virtual Ivehicle
...


// Server Implementation
class Cvehicle : public virtual Ivehicle
...

class Ccar : public Cvehicle, public virtual Ivehicle
...


The use of virtual inheritance resolves the multiple inheritance issue, and our heirachy now looks like this:


Figure 2:

---Ivehicle---
| |
| |
Cvehicle Icar
| |
| |
Ccar-------------


If we build a client application using the same compiler as that used to build the DLL, everything seems to work. Problem solved! Not quite. Incompatibility arises when we build a client using a different compiler, for example a client built with Visual C++, where the DLL was built using Borland C++ Builder (or vice versa). It appears that the virtual function and vtable format shared between compilers does not extend to virtual inheritance. Therefore when a non-native client does the following, the results are disastrous:


Ivehicle* v = NewVehicle();
int s = v->speed(); // This works!
DestroyVehicle(v); // And so does this

// However...
Icar* c = NewCar();
int w = c->wheels(); // BOOM! DOOH!
DestroyVehicle(c);


The following section describes a possible method of resolving this incompatibility.



4] Resolving the Incompatibility
We must abandon the virtual inheritance approach, and accept the heirachy shown in figure 2. However, this leaves us with derived classes which contain multiple copies of the same abstract base class. The only solution appears to be that we must provide implementation for all copies of base classes.

Here's how it can be done at the server side:


// Base class as normal
class Cvehicle : public Ivehicle
{
public:
virtual int __stdcall speed();
virtual ~Ivehicle();
};

class Ccar : public Cvehicle, public Icar
{
public:

// Re-implementation of Cvehicle methods
virtual int __stdcall speed() { return Cvehicle::speed() };

// Icar method
virtual int __stdcall wheels();
};


If we have further derived classes, we must do this for each inherited copy. For example, if we want a class IsportsCar, derived from Icar, we must do the following:


// Client Interface
class IsportsCar : public Icar
{
public:
virtual int __stdcall luxury() = 0;
}

// Server Implementation
class CsportsCar : public Ccar, public IsportsCar
{
public:
// Re-implementation of Cvehicle methods
virtual int __stdcall speed() { return Cvehicle::speed() };

// Re-implementation of Ccar methods
virtual int __stdcall wheels() { return Ccar::wheels() };

// IsportsCar method
virtual int __stdcall luxury();
};


In practice, code maintenance may be simplified by defining macros to re-implement base classes, as follows:


#define IMPLEMENT_VEHICLE \
virtual int __stdcall speed() { return Cvehicle::speed() );

#define IMPLEMENT_CAR \
IMPLEMENT_VEHICLE \
virtual int __stdcall wheels() { return Ccar::wheels() };


// Class can now be defined as...
class CsportsCar : public Ccar, public IsportsCar
{
public:
IMPLEMENT_CAR
virtual int __stdcall luxury();
};


Although this technique is clumsy, raising efficiency and code maintenance issues, it does offer mechanism for supporting what is a powerful programming methodology if used judiciously.

Andy Thomas
[email protected]