Skip to main content
gamedev.net
🔒 Locked

Interfaces & Implementations

Started by EnlightDM 5 replies 1.1k views
Original Post
EnlightDM
EnlightDM
Hi, while working on my component system for my engine, I've found a little problem related to interfaces & implementations. In this sample I have 2 interfaces & 2 implementations: IMesh -> CMesh ILODMesh -> CLODMesh. CLODMesh IS also a CMesh, so in the "interface side", ILODMesh SHOULD be derived from IMesh. Take a look at the sample code (actually working code):

class IMesh
{
public:
	virtual int GetVertexCount() = 0;
};

class CMesh : public IMesh
{
public:
	int GetVertexCount() { return 50; }
};

class ILODMesh : public CMesh
{
public:
	virtual int GetCurrLODLevel() = 0;
};

class CLODMesh : public ILODMesh
{
public:
	int GetCurrLODLevel() { return 3; }
};
Now I can use these 2 classes like this:

IMesh * mesh = new CMesh;
ILODMesh * lodmesh = new CLODMesh;
What I want to do, is to HIDE the implementation headers (CMesh.h, CLODMesh.h), the user will only have the interface headers (IMesh.h , ILODMesh.h). The factory system is responsible for returning new CMesh or new CLODMesh. Now the problem is THIS line --->
class ILODMesh : public CMesh
what I really want to do is something like this:
class ILODMesh : public IMesh
so the ILODMesh.h doesn't have to know anything about the CMesh implementation. There is nothing wrong with that line, BUT the problem comes when I want to IMPLEMENT CLODMesh using both: the ILODMesh interface and the CMesh implementation. I've tried the following without luck:
class CLODMesh : public ILODMesh, public CMesh
the compiler complains:
'CLodMesh' : cannot instantiate abstract class
Do you think there is a way to keep implementations on one side and interfaces in the other and create this kind of hierarchical relationships? At the moment my solution was the sample code above, creating ILODMesh from public CMesh, but the client will require cmesh.h... What do you think guys...
Antheus
Antheus
Ugly (the dreaded diamond):
// Interfacestruct IMesh {	virtual int GetVertexCount() = 0;};struct ILODMesh : public virtual IMesh // virtual interface inheritance{	virtual int GetCurrLODLevel() = 0;};// implementationsstruct CMesh : public virtual IMesh // virtual inheritance again{	int GetVertexCount() { return 50; }};struct CLODMesh : public CMesh, public ILODMesh // extends CMesh, implements ILODMesh{	int GetCurrLODLevel() { return 3; }};


Which is the diamond inheritance
          IMesh  <- ILODMesh             ^          ^               |          |          CMesh  <- CLODMesh


I don't have a clean solution for this (compiles with a warning, may result with problems down the line).

But there are some rules (based on Java model).

- Interfaces inherit only from interfaces.
- Class can inherit from other classes, or implement an interface.
- An interface cannot inherit from a class (hence your ILODMesh -> CMesh is invalid).

Virtual inheritance allows you to get by this, but not in necessarily elegant way. I don't even know how soon this would break with more complex models, it does however completely hide implementation from interfaces.
EnlightDM
EnlightDM
Wow!, your code is working and no warnings on my compiler (MSVC8), btw: I replaced your "struct"(s) with "class"(es).

Quote:
- Interfaces inherit only from interfaces.
- Class can inherit from other classes, or implement an interface.
- An interface cannot inherit from a class (hence your ILODMesh -> CMesh is invalid).


Now, that is exactly what I want. My ILODMesh -> CMesh was a working version but not the solution.

Quote:
Virtual inheritance allows you to get by this, but not in necessarily elegant way. I don't even know how soon this would break with more complex models, it does however completely hide implementation from interfaces.


In my design, the hierarchy is pretty flat so, I don't see future problems from "complex" interfaces-implementation relations using the (now, new to me) virtual public inheritance. I don't know how this feature works, I think I'll have to read a little more on the subject.

Thanks!
Antheus
Antheus
Make sure to read this:
http://www.parashift.com/c++-faq-lite/multiple-inheritance.html

There's be dragons here.
EnlightDM
EnlightDM
I've found these 2 articles in the wikipedia, very useful:

http://en.wikipedia.org/wiki/Diamond_problem

http://en.wikipedia.org/wiki/Virtual_inheritance#Stateful_Diamond_Point_.28SDP.29_Pattern_in_C.2B.2B

It explains the problem very clearly, specially the 2nd.
EnlightDM
EnlightDM
Quote:
Make sure to read this:
http://www.parashift.com/c++-faq-lite/multiple-inheritance.html

There's be dragons here.


Very good one too, bookmarked...
MaulingMonkey
MaulingMonkey
Quote:
Original post by Antheus
Ugly (the dreaded diamond):

Ugly (templates) alternative:

struct IMesh {	virtual int GetVertexCount() = 0;};struct ILODMesh : public IMesh {	virtual int GetCurrLODLevel() = 0;};template < typename Interface >struct MeshBase : public Interface {	int GetVertexCount() { return 50; }};struct CLODMesh : public MeshBase< ILODMesh > {	int GetCurrLODLevel() { return 3; }};


Which flattens the inheritence
IMesh <-- ILODMesh <-- MeshBase<ILODMesh> <-- CLODMesh


Pros:
- No extra size overhead due to virtual inheritence

Cons:
- Increased compile times when modifying MeshBase implementation details (all inheriting classes must be recompiled, even if no functions or members were added, removed, or had their signatures changed)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.