Archived

This topic is now archived and is closed to further replies.

jesterlecodeur

Multiples inheritences

Recommended Posts

I my engine design i have defined some interfaces (likes DirectX does) but I have a question on the implementation. The MSDN give the first way to do this with COM object wich have multiples interfaces. Apply to DX it wil make:
  
class CODirectGraphics: public IUnknown
{
    // Implementing IUnkonwn methods


    class CODirect3D8: IDirect3D8
    { // Implementing stuff from IDirect3D8

    }
    friend CODirect3D8;
    CODirect3D8 m_CODirect3D8;


    class COD3DDevice8: ID3DDevice8
    { // Implementing stuff from ID3DDevice8

    }
    friend COD3DDevice8;
    COD3DDevice8 m_COD3DDevice8;
}
  
it''s a little boring for sharring members for all the interfaces. there is i think an another way
  
class CODirectGraphics: public IUnknown, IDirect3D8, ID3DDevice8
{
    // Implementing all the stuff together

}

// and for QueryInterface from IDirect3D8 to ID3DDevice8 I make

// lpIDirect3D8 is a pointeur to an IDirect3D8 interface

CODirectGraphics* temp1 = dynamic_cast<CODirectGraphics> (lpIDirect3D8) ;
IDirect3D8 * temp2 = dynamic_cast<IDirect3D8 > (temp1 ) ;
// Return the needed interface

return temp2;
  
Is the second methods good ( I know it doesn''t work if 2 methods will have the same name)? Are the Run-time type information slowing the program too much? Why English rules?? C pas très malin tout ça!

Share this post


Link to post
Share on other sites
Simply inheriting the DX8 interfaces (IDirect3D8 etc) doesn't give your class any functionality from the DirectX8 objects. Remember that when you create an object (via for example Direct3DCreate8 or whatever the function is called) you get an object supporting an interface. An interface is just that, an 'interface'. It doesn't contain code, but is more of a 'contract' describing how an object will provide its functionality.

Inheriting a custom class from IDirect3D8 etc. will force you to implement all the methods defined in those interfaces (DrawPrimitive etc).

I don't quite understand what you are trying to do...
Do you simply want to use the DX8 interfaces within your CODirectGraphics object or do you want your CODirectGraphics object to "be" a Direct3D and a Direct3DDevice?

I think neither of your examples would work. The first one doesn't give CODirectGraphics any of the functionality of IDirect3D8/ID3DDevice8, it simply declares two classes supporting those interfaces. And your second example would probably fail miserably. You shouldn't use the C++ built-in RTTI system with COM classes. You must use the QueryInterface method to "cast" (really, retreiving a supported interface from an object).

Perhaps I don't make much sense, and perhaps I have completely misunderstood you, please feel free to ask/tell me if that is the case.

Edited by - Dactylos on June 13, 2001 1:58:04 PM

Share this post


Link to post
Share on other sites
If your goal is to make CODirectGraphics appear to "be" a Direct3D8 and D3DDevice8 object then you could use aggregation. I don''t know if DirectX currently supports COM aggregation though, as far as I can remember it wasn''t supported in DX6; but maybe it is now...

Share this post


Link to post
Share on other sites
I don''t have time to pendantically address all of the issues ... but I have some info that will help.

1. All COM objects inheiret from IUnknown - and therefore must implement the QueryInterface function. Your implementation of this function is how you provide access to ALL of the interfaces you support. The implementation is irrelevant AS LONG AS you return valid Interfaces through the QueryInterface function. You can use Inheiretance, member objects, nested classes, whatever you want for you actaul implementation ... but the client must access them ONLY by first getting the desired interface from QueryInterface.

EX.
  
class ISomeInterface : public IUnknown
{
public:
virtual IUnknown* QueryInterface(...) = 0;

virtual void Foo(void) = 0;
}

class CSomeImplementer : public ISomeInterface
{
public:
virtual IUnknown* QueryInterface(...);

void Foo(void);
}

class CCompositeOne : public CMyClass
{
public:
virtual IUnknown* QueryInterface(...);
}

class CCompositeTwo : public IUnknown
{
public:
virtual IUnknown* QueryInterface(...);

protected:
CSomeImplementer mImplementer;
}


anyway ... what I''m trying to show is this ... if you have an interface ... and someone who Implements it for you ... you can composite those in different ways ... all that matters is that you implement the QueryInterface function (and add/release ref of course) to correctly return the proper pointer. Also important ... the first base class for ALL interfaces must be IUnknown .. because the COM spec specifies that the first three entries in the vtable for each interface MUST be QueryInterface, AddRef and Release. So you derived types will have many more IUnknown entries than needed (and you CANNOT use virtual inheiritance either) ... but that''s not a very large price to pay.

good luck

Share this post


Link to post
Share on other sites
Hum sure I'm not clear.
I have use some DirectX interfaces for exemple only. I known that interface are virtual pure. The fact is that i have begin my engine in a true COM objective but with no MIDL ( I want void* ). So I have to design my architecture (taken from COM) manualy.
I think It will derivate all from IUnknow and use IClassFactory.

In the second exemple, I will use the dynamic_cast in the QueryInterface fonction (with an AddRef naturally).
My idea for the second exemple is that having inner class (as the COM illustred in the MSDN have) is very borring because lot of memnber reside in the parent object.

And let's compare the 2 idea
the MSDN (work always)
      
class COUtilityCar : public IUnknown
{
public:
// Main Object Constructor & Destructor.

COUtilityCar(IUnknown* pUnkOuter);
~COUtilityCar(void);

// A general public method for initializing this newly created

// COUtilityCar object. Creates any subordinate arrays, structures,

// or objects. Not exposed as a method in an interface.

HRESULT Init(void);

// IUnknown members. Main object, non-delegating.

STDMETHODIMP QueryInterface(REFIID, PPVOID);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);

private:
// We show nested interface class implementations here.


// We implement the basic ICar interface in this COUtilityCar

// COM object class.

class CImpICar : public ICar
{
public:
// Interface Implementation Constructor & Destructor.

CImpICar(COUtilityCar* pBackObj, IUnknown* pUnkOuter);
~CImpICar(void);

// IUnknown members.

STDMETHODIMP QueryInterface(REFIID, PPVOID);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);

// ICar members.

STDMETHODIMP Shift(short nGear);
STDMETHODIMP Clutch(short nEngaged);
STDMETHODIMP Speed(short nMph);
STDMETHODIMP Steer(short nAngle);

private:
// Data private to this interface implementation of ICar

ULONG m_cRefI; // Interface Ref Count (for debugging)

COUtilityCar* m_pBackObj; // Parent Object back pointer

IUnknown* m_pUnkOuter; // Outer unknown for Delegation

};

// We implement the IUtility interface (ofcourse) in this COUtilityCar

// COM object class. This is the interface that we are using as an

// augmentation to the existing COCar COM object class.

class CImpIUtility : public IUtility
{
public:
// Interface Implementation Constructor & Destructor.

CImpIUtility(COUtilityCar* pBackObj, IUnknown* pUnkOuter);
~CImpIUtility(void);

// IUnknown members.

STDMETHODIMP QueryInterface(REFIID, PPVOID);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);

// IUtility members.

STDMETHODIMP Offroad(short nGear);
STDMETHODIMP Winch(short nRpm);

private:
// Data private to this interface implementation of IUtility

ULONG m_cRefI; // Interface Ref Count (for debugging)

COUtilityCar* m_pBackObj; // Parent Object back pointer

IUnknown* m_pUnkOuter; // Outer unknown for Delegation

};

// Make the otherwise private and nested ICar interface implementation

// a friend to COM object instantiations of this selfsame COUtilityCar

// COM object class.

friend CImpICar;
friend CImpIUtility;

// Private data of COUtilityCar COM objects.


// Nested ICar implementation instantiation.

CImpICar m_ImpICar;

// Nested IUtility implementation instantiation.

CImpIUtility m_ImpIUtility;

// Main Object reference count.

ULONG m_cRefs;

// Outer unknown (aggregation & delegation).

IUnknown *m_pUnkOuter;
};


mine (wich require that if can't be 2 different methods with the same name and typing (Maybe overloading can help in some case)
It's simpler and slower
  
class COUtilityCar : public IUnknown, ICar, IUtility
{
public:
// Main Object Constructor & Destructor.

COUtilityCar(IUnknown* pUnkOuter);
~COUtilityCar(void);

// A general public method for initializing this newly created

// COUtilityCar object. Creates any subordinate arrays, structures,

// or objects. Not exposed as a method in an interface.

HRESULT Init(void);

// IUnknown members. Main object, non-delegating.

STDMETHODIMP QueryInterface(REFIID, PPVOID);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);

// ICar members.

STDMETHODIMP Shift(short nGear);
STDMETHODIMP Clutch(short nEngaged);
STDMETHODIMP Speed(short nMph);
STDMETHODIMP Steer(short nAngle);

// IUtility members.

STDMETHODIMP Offroad(short nGear);
STDMETHODIMP Winch(short nRpm);

// Main Object reference count.

ULONG m_cRefs;

// Outer unknown (aggregation & delegation).

IUnknown *m_pUnkOuter;
};


// and implement the QueryInterface like:

HRESULT COUtilityCar::QueryInterface(REFIID riid, PPVOID ppv)
{
if( riid == IID_ICar )
*ppv = dynamic_cast<ICar> (this);
else if( riid == IID_IUtility )
*ppv = dynamic_cast<IUtility> (this);
else if( riid == IID_IUnknown )
*ppv = dynamic_cast<IUnknown> (this);
else
return E_NOINTERFACE;

this->AddRef();

return NOERROR;
}


I tried to use the dynamic method but i have an error
conversion from 'class CStuff *' to 'class IStuff2 *' exists, but is inaccessible
but i'm a beginner in dynamic cast.

I found this Query implementation on a IClassFactory implementation so it give me the idea, but I will use multiple inheritence.
  
//

// Class factory IUnknown implementation

//

HRESULT __stdcall CFactory::QueryInterface(const IID& iid, void** ppv)
{
if ((iid == IID_IUnknown) || (iid == IID_IClassFactory))
{
*ppv = static_cast<IClassFactory*>(this) ;
}
else
{
*ppv = NULL ;
return E_NOINTERFACE ;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
return S_OK ;
}


Hum a bit long maybe, sorry.
I hope i'm more clear.

Why English rules?? C pas très malin tout ça!

Edited by - jesterlecodeur on June 13, 2001 2:40:55 PM

Edited by - jesterlecodeur on June 13, 2001 4:55:52 PM

Share this post


Link to post
Share on other sites
T'ai suis j'perfe #2
(As you can tell, your English is much better than my French)

The problem you can run into with the dynamic_cast and COM, "is inaccessible ....", is that more than one class can inherit from the same base class - most notably IUnknown.

So if you make a coclass:
class CoMyNewClass : public I3DGraphics, I2DGraphics

It get's 2 IUnknown's, I3DGraphics::IUnknown and I2DGraphics::IUnkown

So you need to specific which method pointer you want to use. They don't necessarily point to the same method, but in this case they will.

i.e.
  
*ppv = dynamic_cast<ICar::IUnknown>(this);



Magmai Kai Holmlor
- The disgruntled & disillusioned


Edited by - Magmai Kai Holmlor on June 13, 2001 9:55:42 PM

Share this post


Link to post
Share on other sites