Skip to main content
gamedev.net
🔒 Locked

Question about casting

Started by gommo 6 replies 300+ views
Original Post
gommo
gommo
I may be going around this design the wrong way but really my question is just on casting. I have a CAbstractRenderer class that encapulates the behaviour of a Rendering API such as OpenGL or DirectX. I have derived classes, that override some virtual functions, these are called COpenGLRenderer and CDirectXRenderer; Now, in my other classes through out the system I have various Interfaces such as ICamera, IPanel that have 2 derrived classes which are the opengl and directx equivalents of the interfaces. For example, because my directx camera class needs to set up its view matrices etc using the IDirect3DDevice9 interface I''d like to get access to this, this is in my CDirectXRenderer and there will be a specific function for it to return an IDirect3DDevice9*. But, my main game engine just has a pointer to a CAbstractRenderer. Can I just do this if .. CAbstractRenderer* myRenderer = new CDirectXRenderer(); from my camera class (or whatever) I get the myRenderer and CDirectXRenderer* localRenderer = dynamic_castmyRenderer; I want to try use proper c++ casts throughout my code too, not just standard c ones. Thanks
Poontardis
Poontardis
well you'd just do:
CDirectXRenderer* localRenderer = dynamic_cast <CDirectXRenderer*> (myRenderer);

And if localRenderer is NULL then it's an OpenGL renderer you have rather then DirectX.

But the thing is - you possibly are going about this the wrong way. The renderer is the only thing that should know whether you are using OpenGL or DirectX (mostly). I could see how a camera class *might* need to know the renderer type, but a panel class certainly shouldn't.

You possibly need to create some more abstraction away from a specific gfx API.
For example, what if the Glide API was still in use? : You'd have to have a third version of the Panel interface. What if Nvidia and ATI create their own optimised rendering APIS, what if you want to port your code to PS2 or Gamecube too? You'd need seven versions of IPanel.... not too great.

The *correct* (I can't think of a better word just now ) thing to do is have the higher level camera code (API independant) in your IPanel interface thingy, and then pass the API independant camera state to the renderer which converts it to the correct API format before using it.

Anyway, forgetting all that then yeah, you can just use dynamic_cast. (note: using virtual functions in a renderer is going to slow it down as dynamic_casts are slow, and calling virtual functions is slow).

[edited by - poontardis on February 14, 2003 11:24:46 AM]
Qw3r7yU10p!
Qw3r7yU10p!
I''d say you''re heading the wrong way. Casts are technically possible but not a good solution to many situations. They''re the wide path to walk.

I think this will be an interesting situation to look into. Could you answer a few questions, as fully as you can so that we can give you a better way to do it? It''s difficult to generalise about design and far easier with a concrete problem.

I don''t know a lot about directX 9 (my knowledge all became redundant after 7). Can you explain why your camera really needs to know about directx, what it needs to know, what creates it at the moment, what creates the renderer, what would be coordinating the information outside of the two at the moment etc? Maybe provide us with the class declarations.

Then we''ll pull it all apart (c:

Nicely of course.

It''ll be fun!
Xai
Xai
Ok, the quick C++ cast tutorial with no design critisism added:

//Assume the following:
class BaseClass;
class DerivedClass : public BaseClass;
class OtherClass;
BaseClass baseClassObject;
BaseClass* blassClassPointer;
DerivedClass derivedClassObject;
DerivedClass *derivedClassPointer;
OtherClass otherClassObject;

In C++, an object will be automatically downcast to it''s base class(es), no cast required. The same is true for object pointer to base class pointers.

//These are correct C++
baseClassObject = deriverClassObject;
baseClassPointer = derivedClassPointer;

The const_cast operator can ONLY change the constness of an object, never any aspect of the type ... except that auto downcasting (see above) still happens, so

const_cast(derivedClassPointer)

is a valid expression, but

const_cast(baseClassPointer)

is not.

The dynamic_cast operator is used only to cast baseclass pointers to more derived classes, called upcasting. If the given object actually derived from the specified type, the cast will succeed and return a valid pointer, otherwise it will fail and return zero. The dynamic_cast operator can also downcast, but such casting does not need to use an operator, and can never fail.

dynamic_cast<baseClass*>(derivedClassPointer) // silly and can never fail.
dynamic_cast(baseClassPointer) // correct use of dynamic_cast, and might return 0 if object is not correct type.

Please note that upcasting requires the compiler to put RTTI information into the object code, and so you pay a code size penalty when you enable this feature (depending on your compiler''s default behavior). Compiler''s are getting better at RTTI, and the current performance / size penalty is not very high ... but be carefull to hoist downcasts upward in your design (aka do them once before a loop when possible, or before calling 10 performance critical functions, instead of inside of each one - and your design will look better also, less people upcasting means less people assume too much about their arguments).

The static_cast is used for actually changing an object''s type, aka casting a float to a double, etc ... this is the cast that will trigger the overloaded cast functions you might write.

otherclass = static_cast(derivedClassObject);

The reinterpret_cast is usually a do-nothing, compiler information / warning removal cast. It is usually used to cast pointers of non-related types to each other - such as to supply the char * arguments that binary IO operations expect, or to cast to and from void *''s. This operator is also very usefull for doing platform specific tricks such as casting poitners to and from 32 bit integers (on 32 bit platforms of course). This is what you would use to pass a pointer as an LPARAM in windows ...

myfile.write(reinterpret_cast(derivedClassObject), sizeof(DerivedClass));

PostMessage(hWnd, msgCode, wParam, reinterpret_cast(&derivedClassObject));

Hope that helps all you who want to express your designs in proper C++ code. For 2-3 months I didn''t like using these casts, especially due to their long size. I still don''t like the code bloat (source code, not compiled code), but I definately like the fact that my casts tell me or whoever reads the code, exactly what I THINK I''m doing ... which makes finding logic flaws a little easier.
gommo
gommo
Poontardis & Anonymous Poster:
Thanks for your advice. In regards to having different versions of the IPanel interface, I wouldnt need that because everything that uses IPanel just operates through the interface. I would however need different concrete classes that implement the intercace, I wasn''t sure if thats what you meant.

The reason I have different implementations of say the IPanel interface and the potential ICamera interface is that they can call API specific things, like Transformations, renderstates etc..

Is this better done by just placing a generic call to the Renderer and have it do all the things depending on which API it is designed for??

Also I can see how I could potentially lessen the API dependancy by creating my own Matrix classes, vector classes but in the end dont these need to be transformed into their API equivalents.

Also with my Panel class at the moment It creates a small Vertex buffer and loads in two simple triangles etc.. to place a texture on. I realise that you should only really have larger vertex buffers, one each for the different types of FVF used . In openGL, as i havent really got onto that, I dont know how this is handled. So obviously I have to modifiy my panel class maybe to use my engine specific vector etc.. and send these to the renderer which places them into the API specific vertex buffer then renderers.

I''m guesing I could have say a static buffer in the engine that I can load in say panels for menu screen, buttons etc.. and they wont have to be changed each frame except for maybe the pointers to the textures that are applied to them. (So I can do menu item rollovers etc.)

Anyway, how does everyone do this to make there game engines easily able to plug different renderers into them. I have a long way to go yet, but am willing to learn. hehehehe
Xai
Xai
Do you own the book "Design Patterns" by the Gang of Four? If not, and you can possibly afford the $50 or so ... buy it now ... you''ll have it in a week, and you will keep it and use it for not less than the next 10 years of your programming career ... (well I''ve used mine for all 6 that i''ve owned it) ... It is simply the best book for gaining a vocabulary for talking about program designs ... for instance, you have paralled classes which look very much like the ones he uses in his example about the "Abstract Factory" pattern, which describes a design whereby you can specify which parallel tree you want to use by setting up the abstract factory. This does not directly have to do with your problem, but there are so many little ideas and discussions about the trade offs of various ways of connecting your related class heirarcies, and defering desicions to the appropriate handlers, that I can''t help but think you would really benifit from the book in general.

Specifically about the DirectX / OpenGL thing, I think you would just create an abstraction over the top of the engines ... such as your IRender, ICamera, IPanel classes ... but you SHOULD NOT need to upcast to their actual derived classes in very many places ... a Rendered should just use a Camera though the ICamera interface, and not need to know about the actual type of camera ... INTERNALLY, the OpenGL camera would make OpenGL calls, and the DirectX one would make DirectX calls ... so it would be a complete error to ever instantiate one in OpenGL and the other in DirectX ... but that doesn''t mean that each class needs to worry about that ... only the constructor classes need to worry about which type your using (hence the Abstract Factory pattern) ... everyone else just uses the public interface.

And if you really want to give special features to either the DirectX or OpenGL version based on what they can do efficiently, one great way is to do it like COM does ... create multiple interface classes, and ask the object if it supports the more advanced one(s). for example ...

a DirectXRendered object could use the provided camera''s ICamera interface ... but perhaps your OpenGLRenderer knows how to do more camera tricks ... if the camera supports them ... so it asks the camera object if it supports the ICamera2 or IOpenGLCamera interface ... and if so ... does the good stuff calling the more advanced functions ... otherwise it falls back and does the simple stuff.

The COM system is all based around the function QueryInterface ... which is really simple, and you can emulate it without really use all of COM. Just write a base class, aka IBasicCOM, which just defines 1 virtual function ... QueryInterface .. which accepts an ID for the interface (whatever type you like for an ID, COM uses UUIDs, I use ints) and returns an IBasicCOM pointer .. which can be 0 if not supported (this isn''t exactly like COM, but close enough for us).
gommo
gommo
Xai:
I dont own the book but my work does and I have read it fairly much all the way through (Hence the Abstract factory pattern).
You are right in what you said about having abstraction ssuch as ICamera, IPanel etc.. Thats waht I have, the reason I was wondering about upcasting is for example, If my CDXCamera class wants to use Set the perspective transformation etcc using SetTransform then it needs the IDirect3DDevice9 pointer. This is stored in the concreted DirectX Class of the CAbstractRenderer i.e. CDXRenderer. So I cant just have a method in CAbstractRenderer called GetDevice() Cause this cant return a IDirect3DDevice9* for obvious reasons (openGL, softwrae ,API)

So That is why in DirectX Specific concrete classes of ICamera, IPanel and whatever elase (ITerrain??) They can get access to the CDXRenderer Singleton Class i.e.

CDXRenderer* blah = CDXRenderer::Create();

Actually, thinking about this now, I probably dont need to upcast, because I am assured that CDXRenderer has already been instantiated via my Engine and so calling the static method Create() will return the correct CDXRenderer*.??? Maybe this is how I should do it.
In my previous post I was actually thinking allong the terms of getting the CAbstractRenderer* from my Engine class and using a dynamic_cast to cast it to a CDXRenderer*.

And since posting the original post, I have also been pondering ways of not even needing CDXPanels and Cameras but calling Abstract methods like SetTransform and CreateVertexBuffer to the CAbstractRenderer and it set all the right calls depending on API. This is probably the correct way but a lot more design is needed than I have currently. I am interested to hear anybody''s thoughts or implementations on these matters.

Thanks for the comments. They are all useful

Topic Locked

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

Sign in to reply to this topic.