Archived

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

class interfaces

This topic is 5144 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have a class CGraphics that I want to be completely API-independent, because it will manipulate other classes such as CD3DGraphics to perform the actual work. The problem is, I'm not sure how I should have the classes depend on each other. I've come up with a couple solutions, but none of them satisfy me very well:
class CGraphics {
};

class CD3DGraphics : public CGraphics {
};

// use base class pointer to remove API-dependence

CGraphics* pGraphics = new CD3DGraphics;
The problem with the above is that the client code still needs to know about the specialized class, while I'd like the CGraphics class to stand alone and handle the CD3DGraphics class by itself. WIth this in mind:
// class has pointer to base class so it can store derived classes

class CGraphics {
public:
    HRESULT Initialize();
private:
    CGraphics* m_pRenderer;
};

// derived class

class CD3DGraphics : public CGraphics;

// instantiate appropriate derived class, and initialize it

HRESULT CGraphics::Initialize() {
    m_pRenderer = new CD3DGraphics;
    return m_pRenderer->Initialize();
}
This works as well, but for some reason it unsettles me to have to explicitly call CD3DGraphics::Initialize() in the base class. Also, what if I have another class that CGraphics doesn't know about, like CUnknownGraphics? How would I create that then? I need to have a solution that's not hardcoded, so I can load the code from a DLL. Last solution, class hell:
// basic class that CD3DGraphics derives from

class CBaseGraphics;

// graphics class interface, the only part visible to the client

class IGraphics {
public:
    HRESULT Initialize( CBaseGraphics* );
private:
    CBaseGraphics* m_pRenderer;
};

class CD3DGraphics : public CBaseGraphics {
};

HRESULT IGraphics::Initialize( CBaseGraphics* pImpl ) {
    m_pRenderer = pImpl;
}
I like my code to be concise, so having this many classes disturbs me even more than my second example. That's basically the only complaint I can find with this model. Which of these do you think would work best for me? Is there another that I haven't thought of? It's nothing serious, but ugly code seriously disturbs me, and I want to make sure I have the right classes. edit: topic title didn't make much sense. and damn forum html. [edited by - psykr on November 13, 2003 2:23:28 PM]

Share this post


Link to post
Share on other sites
First, IGraphics is not an interface. An interface should only declare functions and can''t have any members. If I understand you right, you will only have one subtype of CGraphics available to the client code, but you want to hide it and be able to change it at runtime(?).

One idea would be to make a simple factory, where you register a pointer to a function that can create the chosen concrete graphics class. If the user wishes to use OpenGl, you simply register a function that creates a COpenGlGraphics object. The client code uses the factory, not knowing the concrete type of CGraphics. CGraphics should have a protected constructor, forcing the client code to use the factory:


struct CGraphics
{
public:
virtual void f() = 0;
protected:
CGraphics() {}
};

struct CD3DGraphics : public CGraphics
{
virtual void f() {}
};

struct COpenGlGraphics : public CGraphics
{
virtual void f() {}
};

// creators, well hidden from client
CGraphics* CreateD3DGraphics()
{
return new CD3DGraphics();
}

CGraphics* CreateOpenGlGraphics()
{
return new COpenGlGraphics();
}

// factory
class GraphicsFactory
{
typedef CGraphics* (*GraphicsCreator)();
public:
static GraphicsFactory& Instance()
{
static GraphicsFactory instance;
return instance;
}
void setGraphicsCreator(GraphicsCreator gc)
{
gc_ = gc;
}
CGraphics* create()
{
if ( 0 == gc_ )
{
throw std::runtime_error("no graphics available");
}
return (gc_)();
}
private:
GraphicsCreator gc_;
GraphicsFactory() : gc_(0)
{
}
~GraphicsFactory()
{
}
GraphicsFactory& operator=(const GraphicsFactory&);
};

int main()
{
CGraphics* cg = 0;

// manager registers D3D graphics
GraphicsFactory::Instance().setGraphicsCreator(CreateD3DGraphics);

// client
cg = GraphicsFactory::Instance().create();
cg->f();
delete cg;

// manager registers OpenGl graphics
GraphicsFactory::Instance().setGraphicsCreator(CreateOpenGlGraphics);

// client
cg = GraphicsFactory::Instance().create();
cg->f();
delete cg;
}

Well, that''s one alternative. I hope the code didn''t disturb you

Share this post


Link to post
Share on other sites
When my engine boots up one of the first things it does it looks into a config file to find the plugin directories. It then scans these directories for dll''s to load.

All my plugin dll''s have a startPlugin and stopPlugin C Function. So when the engine loads it it calls startPlugin().

Now a DirectX 3D renderer dll would, in its startPlugin would first instantiate the D3DDriver and then register this pointer with the engine via a registerRenderDriver(pointer) type function. (The engine is a singleton)

Now the registerRenderDriver function in the engine takes a IDriver* as a parameter and the D3DDriver is of course a IDriver too.

So this way the engine has no idea what type of render drivers it has just a list of IDriver*.

Share this post


Link to post
Share on other sites