Reputation: 423
I'm trying to create a system that uses different implementations of a class in each dll. So I'd have a VertexBufferObject class that has one implementation and private methods (and maybe methods used strictly by that DLL). But only a certain set of methods will be used by the main executable. For example:
In openglGraphics.dll:
class VertexBufferObject {
private:
// Unexported data
uint vbo;
// Exported data (won't actually use this though)
std::vector<Vec3> arr;
public:
// Unexported methods
IDirect3DVertexBuffer9 *getVBO();
// Exported methods
virtual void Build(Vec2 array);
virtual void Build(Vec3 array);
virtual void Unbind();
~VertexBufferObject();
};
In directXGraphics.dll:
class VertexBufferObject {
private:
// Unexported data
IDirect3DVertexBuffer9 vbo;
// Exported data (won't actually use this though)
std::vector<Vec3> arr;
public:
// Unexported methods
IDirect3DVertexBuffer9 *getVBO();
// Exported methods
virtual void Build(Vec2 array);
virtual void Build(Vec3 array);
virtual void Unbind();
~VertexBufferObject();
};
And finally, the executable can use a factory function and create the class's exported methods, but not the dll-specific methods. Is this possible? Is there another way to handle it? (Also, if you're into graphics APIs and can find any flaws in this that'd be cool, but not the point of the question.)
Upvotes: 1
Views: 236
Reputation: 73376
It is certainly possible to do this on the DLL side: each of your implementation would be in a separate compilation unit and a separate DLL, and even a separate project. But...
But this will not work on the DLL client side, because the client must know the definition of the object, and the ODR rule require to have only one definition.
So you'd better opt for a revised and more sustainable design.
Option 1:use inheritance of a public interface
class IVertexBuffer {
public:
// Exported methods
virtual void Build(Vec2 array)=0;
virtual void Build(Vec3 array)=0;
virtual void Unbind()=0;
virtual ~IVertexBuffer(); // virtual function ==> virtual dtor !!!
};
In openglGraphics.dll:
class VertexBufferGLObject : public IVertexBuffer {
private:
uint vbo;
std::vector<Vec3> arr;
public:
// Unexported methods
IDirect3DVertexBuffer9 *getVBO();
// Exported methods of the interface
void Build(Vec2 array) override;
void Build(Vec3 array) override;
void Unbind() override;
~VertexBufferObject();
};
A factory could load the right dll depending on software configuration, and create the VertexBufferGLObject , while the client code would only use the polymorphic base class.
The implication is however that the client code only uses pointers and references. If copying is required, you'd need to use a clone()
function if you want to avoid the risk of slicing.
Option 2: more flexibility in hiding internal structure
You could also use the PIMPL idiom also called compilation firewall.
The idea is the following:
class IVertexBufferImpl; // you need to define this only in the implementation
class VertexBufferObject {
private:
IVertexBufferImpl *myobject;
public:
// Exported methods
virtual void Build(Vec2 array);
virtual void Build(Vec3 array);
virtual void Unbind();
virtual ~VertexBufferObject();
// + rule of 3
};
You have a level of indirection more. But the implementation of this class will forward calls to the IVertexBuffer object, and you can use a factory to create the private object as in option 1. The advantage is that the client can use VertexBufferObject s as any other object (by value or by reference): it's a degree further in the encapsulation/isolation.
Option 3: use the bridge design pattern
The bride design pattern aims at decoupling an abstraction from its implementation.
It's similar to option 2 in the way it works, but different in the intent: the goal is not to hide the implementation, but to decouple, in order to avoid derivation and refinement on both side: on the abstraction and on the implementation side.
Upvotes: 1