Advertisement

Interfaces, inheritance, etc

Started by June 10, 2000 11:32 AM
21 comments, last by Kylotan 24 years, 6 months ago
Ok, I''m sure this isn''t all that hard but I have a mental block. I can force it with casts and void pointers, but I don''t want to do that. Basically, my system is as follows: A base DrawManager class, with numerous pure virtual functions that take a ''Graphic*'' to do things such as load it, blit it, etc. Derived from DrawManager, are a number of implementation classes, for example GDIDrawManager, DDrawManager, D3DXDrawManager, etc. I also figured I would derive from Graphic, GDIGraphic, DDrawGraphic, D3DXGraphic, etc. Now, the problem is that using the abstract interface is not sufficient. DDrawManager can''t take a pointer to a simple Graphic class as it needs to know about LPDIRECTDRAWSURFACEs, which are only in the DDrawGraphic derived class. Similarly, GDIDrawManager can''t just take a Graphic as it needs to know about the HBITMAP in GDIGraphic, etc. I thought about making the derived DrawManager implementations entirely responsible for creating, using, and destroying their graphics, in which case they can use the specific derived Graphic class rather than a pointer to Graphic. But then, there is no point deriving anything if every single implementation now needs to have different functions. And everything has to be rewritten for each implementation from the ground up, which seems to defeat the object. Can anyone see what I am trying to achieve and give me some ideas on how to do it? I want to be able to pass a Graphic to a DrawManager, knowing they are the correctly compatible types, and have it know how to render it appropriately.
OK, if I know what you''re trying to do, here''s my suggestion. First of all, if DDrawGraphic is derived from Graphic, then you can refer to a DDrawGraphic* as a Graphic*. Therefore, your functions can take Graphic* as paremeters, and you can cast them. No void pointers needed. If you want a safe, type checking cast, you''ll have to turn on RTTI (run time type information) and use the dynamic_cast operator, but that''s slow, so what I''d do is just make sure in the code that you don''t get into a situation of passing a GDIGraphic to DDrawManager, etc..

If you don''t want to cast at all, then I don''t know what you can do. But if you don''t mind casting (you won''t need any void pointers here, just Graphic pointers) then you could use this. Just keep in mind that you can refer to a pointer of a derived class as a pointer to the base class always.

Good luck.
Advertisement
Yeah, I don''t know why I said void pointers, just habit I guess. I''d like some way of ensuring that the D3DX DrawManager only ever deals with D3DX Graphics: I figured the best way to do this would be to use a factory method such as

Graphic* DrawManager::NewGraphic();

Which is a pure virtual function that, in the derived classes, instantiates the correct type of Graphic and returns the pointer. GDIDrawManager would return a GDIGraphic, D3DXDrawManager would return a D3DXGraphic, etc. This method should mean I can always assume the right types are being passed, unless I''ve missed something?

So, given that setup, I -could- just cast it and assume that DDrawManager::Blit(Graphic*) can access an LPDIRECTDRAWSURFACE, GDIDrawManager::Blit(Graphic*) can access a HBITMAP, etc.

But this still seems ''wrong'' to me Since most ''professional'' software developers will claim there is almost always a way of doing it without casts, I''d like to find it.

What about templatizing (is that a word? ) the Graphic class? Eg, Graphic<class Surfacetype> ? But how would I refer to it in the abstract base class, DrawManager, when all the functions need a pointer to some sort of Graphic?

Any more ideas anyone? This is holding me up.
Templates may work. I''ve been playing with this idea (in my head, anyway) for a while now, but haven''t gotten around to testing it.
This is actually a classical problem with C++ classes. You run into it anytime you make a framework, like you''re making here. That''s where you have two or more classes that work together, and you try to make subclasses of both base classes.
I''ll be working on this in the next little while: seeing if templates are up to the job. I''ll let you know what I come up with.

Morbo
Just one question. Are you planning for this to go at decent speeds or do you only want it to work on super computers?

-----------------------------

A wise man once said "A person with half a clue is more dangerous than a person with or without one."
-----------------------------A wise man once said "A person with half a clue is more dangerous than a person with or without one."The Micro$haft BSOD T-Shirt
Templated classes are no slower at runtime. Templates are generated at compile time. It will make your code larger, as templates are generated once per compiled unit (cpp file), unless your linker supports template spanning (I think that''s what it''s called). CodeWarrior does it.
The only thing about classes that is slow are virtual functions, which require lookups to call. And, of course, complex copy-constructors.

"Humans do not yet know the meaning of suffering!"
Advertisement
ImmaGNUman, there''s no reason why this should be slow. Just versatile. Using derived classes, there might be one or two extra pointer dereferences for each blit, that''s all.
Well, I''ve done exactly how you have laid out (the wrong way ), and I''m totally happy with it. The different types should be setup so that they are mutually exclusive, and the manager should act as a factory, so there should NEVER be a situation where the DDraw manager gets a GDI Graphic. I just cast in the derived manager.

Templates seem like way overkill to me. Anytime there is code bloat (or compiled bloat in this case I guess), there is SOME speed issue with the cache and such. Its just my opinion, but I take the approach of a balanced middleground. We''re writing games, not a OOP master''s thesis. I had this argument with my gaming partner, and I won''t say either approach is wrong, because they aren''t, but adhering to rules like "never cast" and the like seem an acedemic excercise, not always well suited for the real world. Just my opinion. (And by the way, I''m strongly against casts in most places, but this is one of the very few where I decided to use it. I think it is the only place where I downcast classes at all).

Rock
I agree that, if you can''t find any other way, casts are fine. After all, the idea is to get working code, not elegant code that doesn''t do what you need it to. But apart from simple float<-->int conversions, and the occasional pointer conversion for DirectX functions, I can''t think of any time in the past that I have really needed to use a cast. So I am thinking I don''t -need- one here And would therefore like to know if there is another way around it.
ok, bear with me here a minute.

DrawManager can''t do a damned thing, its an abstract base class, and takes a Graphic* parm into its member functions. Graphic being an abstract class also.

from Drawmanager, you are deriving one for GDI, one for DDraw, and one for D3D. you are deriving similar version for the Graphic class.

i would assume that any Graphic from a lower class can be used on a drawmanager of the same level... i.e. the D3D mgr can use the d3d, dd, and gdi graphics, teh dd mgr can use dd and gdi graphics, and the gdi mgr can only use the gdi graphics.

here''s one solution:

class DrawManager
{
public:
class Graphic{/*declarations*/};
/*delcarations*/
//sample member function
RESULTTYPE Draw(DrawManager::Graphic*);
};

class GDIMgr:public DrawManager
{
public:
class GDIGraphic: public DrawManager::Graphic{};
RESULTTYPE Draw(GDIMgr::GDIGraphic*)
//GDIMgr inherits DrawManager::Draw(DrawManager::Graphic*)
};

class DDrawMgr:public GDIMgr
{
public:
class DDGraphic:public GDIMgr::GDIGraphic{};
RESULTTYPE Draw(DDrawMgr::DDGraphic*);
};

class D3DMgr:public DDrawMgr
{
public:
class D3DGraphic:public DDrawMgr::DDGraphic{};
RESULTTYPE Draw(D3DMgr::D3DGraphic*);
};

it may seem a little convoluted at first, but it just might do the trick.

Get off my lawn!

This topic is closed to new replies.

Advertisement