Sign in to follow this  
Valor Knight

Casting up to a derived class [solved]

Recommended Posts

This is my situation: Class A Class B : public A function( A &input ) { //I need to cast input (currently A) to a B class } I need to cast the input from a base class to the required class. Why I need to do this is because I have a component or something that has functions I need to access outside of the base class. The current situation is I have a window class and a MSWindow class. As I dont know if linux, ect uses HWND, I have a class derived from window, MSWindow, which has a GetHWND() function. I will also need to use this later on in a component manager. I know static_cast<> can cast down B to A. dynamic_cast<> can cast up (I think) But I was told never to use dynamic casting in a game. How can I cast my input to the correct class? Also is HWND used for linux/mac as well? trying for api independence and dont want a base class with a MS only handle. [Edited by - Valor Knight on October 22, 2006 6:59:09 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by Valor Knight
I know static_cast<> can cast down B to A. dynamic_cast<> can cast up (I think) But I was told never to use dynamic casting in a game. How can I cast my input to the correct class?


dynamic_cast is perfectly fine in games too.

Quote:
Original post by Valor Knight
Also is HWND used for linux/mac as well? trying for api independence and dont want a base class with a MS only handle.


No HWND is not used on those platforms. If you want a platform independent windowing API for all the three platforms you mentioned, I can recommend wxWidgets: http://www.wxwidgets.org.

Share this post


Link to post
Share on other sites
If you are 100% absolutely undoubtedly sure that you do have an object of type B, then you can use static_cast<B&>(input). If you can't guarantee that, use dynamic_cast<B&>(input), which will throw an std::bad_cast exception if you really didn't have a B (or derived). When casting pointers (i.e. dynamic_cast<B*>(&input)), you instead get a null pointer if the cast fails.

Quote:
But I was told never to use dynamic casting in a game.


That's bullshit. There is nothing else you can use here. The tools are there, use them when you need them, otherwise you'll waste time coming up with a solution that's likely to both be slower and less maintainable.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
HWND is windows only. But it's just a typedef'd type, like a DWORD is an unsigned long. You can make your variable of the base type and be ok as long as the other OS's use the same or smaller sized types for their IDs.

Or, wrap the code in #ifdef's/#endif's or other solutions mentioned on this site by others (I do the #ifdef way, personally like it better then other solutions I've seen so far).

Share this post


Link to post
Share on other sites
Quote:
Original post by Fruny
Quote:
But I was told never to use dynamic casting in a game.


That's bullshit. There is nothing else you can use here. The tools are there, use them when you need them, otherwise you'll waste time coming up with a solution that's likely to both be slower and less maintainable.


I'll mantain that a virtual functions are usable - and often more mantainable - "here" (where here is the problem you're trying to solve using upcasting). That said, there are situations where dynamic_cast is the superior solution, and it is definately usable in game code.

Use it the wrong way, and yes, your performance will tank. For example, you don't want to try and dynamic_cast individual pixels in a screen buffer array. That said, 99.999% of C++, if used the wrong way, will simply invoke undefined behavior and crash in a messy, hard to track down or predict way, so this is really more of a benifit to dynamic_cast<>s than anything else.



In this case, it smells a bit like design rot. On linux, the system should never get an MSWindow. On windows, the system should never not get one. As such, I'd expect this kind of thing to be checked and verified at compile time, not run time. Just a thought.

[Edited by - MaulingMonkey on October 21, 2006 8:39:39 PM]

Share this post


Link to post
Share on other sites
This is exactly one of the very few situations I've needed to use dynamic_cast, and it's perfectly fine as long as you know why you are using it. It can hint at a bad design, but I use it to get the HWND when I'm setting up DirectX.

Share this post


Link to post
Share on other sites
When creating platform-dependant classes I tend to take a different approach and not use inheritence at all. In your case if you have a class called PlatformWindow and derived classes MSPlatformWindow and LinuxPlatformWindow, what I do instead is this...


//------------------
// PlatformWindow.h

struct PlatformWindowHandle;

class PlatformWindow
{
public:
const PlatformWindowHandle& GetHandle() const { return *m_handle; }
private:
std::auto_ptr<PlatformWindowHandle> m_handle;
}

//------------------
// Win32Impl.h

struct PlatformWindowHandle
{
HWND hWnd;
};

//------------------
// LinuxImpl.h

struct PlatformWindowHandle
{
int handle;
};




You do something similar for each of your platform-dependant classes, and then have a single header that defines the various platform-dependant structs such as PlatformWindowHandle that contain your HWND. The important part here is that the PlatformWindowHandle type is not defined in the PlatformWindow header, and you only deal with pointers & references to it. This allows you to hide the platform-specific details while still providing access to platform-specific data, without having to rely on pointless inheritence (since you need to recompile for each platform, the desired platform-specific class is known at compile-time).

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
@joanusdmentia:
That's a really good idea. Then just #ifdef around the headers you need.

Share this post


Link to post
Share on other sites
Thanks for the info on dynamic_cast. I will ignore what that person said about it. Thanks for the input


As for the HWND: I should have thought of this earlier but I just came up with. I dont know what I was thinking about the dervied classes (when I wrote it, it looked stupid to me):

#ifdef WIN32
typedef HWND WinHandle;
#endif

classWindow
{
WinHandle GetWindowHandle();
};

Share this post


Link to post
Share on other sites
There's also the question of why external code should ever need access to the HWND. In most cases, only member functions should need to use it.

[Edited by - Fruny on October 21, 2006 10:49:20 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Fruny
There's also the question of why external code should ever need access to the HWND. In most cases, only member functions should need to use it.


DirectX requires it to be passed when initializing a direct3d device. That's the only case I think it's ok to dynamic_cast. Even if you do whatever preprocessor tricks, you're still going to have to call

WinHandle.HWND

where you need it externally (which should only be one other place like directx), which is basically assuming that it's a windows class. That doesn't fix this problem at all, though I think it's good to use preprocesser directives for cross-platform code.

Share this post


Link to post
Share on other sites
Yea, Im passing it to a direct x device.

Im not using dynamic_cast nor need it now with the preprocessor commands
Just in the base class I have a getwindowhandle.

Actually now that I mention it, I am going to derive a class from a base window class for a Windows version of initing, ect... should I just have the class in preprocessor so there is only 1 window class and the preprocessor picks the correct one?

Share this post


Link to post
Share on other sites
If you have access to the base class definition, then dynamic_cast is not required:


class Base {
public:
virtual class Derived &asDerived() {
throw std::bad_cast(...); // or another appropriate exception.
}
};

class Derived : public Base {
public:
Derived &asDerived() {
return *this;
}
}



Of course, if this conversion is very infrequent, does not make any sense as part of the design (it does, after all, break the open/closed principle in most cases) or is required in a similar fashion for many other classes, then dynamic_cast is a better solution.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
@joanusdmentia:
That's a really good idea. Then just #ifdef around the headers you need.


No #ifdef's are required at all. The majority of your code will only include the 'PlatformWindow.h' file (which contains no platform-specific types). The parts which are platform specific (eg. the window's implementation, Direct3D, etc) will then include the appropriate platform specific header (eg. Win32Impl.h). By keeping each platform's implementations in different .cpp files you can compile and link only the source files for the current platform.

Share this post


Link to post
Share on other sites
I seem to recall that dynamic_cast has to do some extra work if you're using multiple inheritance (or the other crazy inheritance types). I'm not sure if it does this extra work if the compiler suspects that you only use single inheritance...

If you're using single inheritance, most cases I've seen will work properly if you compare the first pointer-sized section of data in the object against the same bytes in an object that you KNOW is your target class (you can make a dummy instance - I wish C++ had a construct to get direct access to the vtable pointer without a nasty hack like this)


ToohrVyk's system is a lot cleaner than this hack though. You have to maintain the base class more than usual, though.

Share this post


Link to post
Share on other sites
Quote:
Original post by Nypyren
(you can make a dummy instance - I wish C++ had a construct to get direct access to the vtable pointer without a nasty hack like this)


There is no such thing as a vtable pointer in C++, citizen.

The output of certain compilers may include a vtable pointer as an implementation of dynamic binding of methods. Other compilers, or certain virtual machines, may also implement dynamic binding of methods through a hash system or by decorating objects with their methods directly. While you can expect the usual C++ compilers to use a virtual table, you cannot assume that this is the case, lest you explicitly violate the C++ Standard and end up with undefined behaviour.

Also note that your hack can only be expected to work if the object is of the expected type. In particular, if C is-a B is-a A, then a reference to A storing a C will not be converted to a B this way. dynamic_cast and a virtual method would work here, however.

As for the work done by dynamic_cast: it's mostly a type lattice traversal (although it's not a lattice, but let's assume it is) which checks that the required type is a supertype of the dynamic type. This traversal is linear (straight up) when not using multiple inheritance, but it may need to look for several paths when multiple inheritance is used.

Share this post


Link to post
Share on other sites
dynamic_cast<>() requires c++ RTTI-system to be enabled. This afaik intoduces some overhead not only to your specific class but to all classes in your code. If you realy are absolutely sure you have a class of type B ( assuming B extends A ) you could also use reinterpret_cast<>(). I must admit, that this technique is a bit durty but if you only need it for 1 or 2 classes, i think its a good tradeof.

To ensure that your class is a B, you could simply write your own little RTTI for that class.

I personally though can't think of many situations where you really would require dynamic_cast<>(), since you have got a much more powerfull and elegant toy (virtual functions) to solve all of these problems. Just try to sit down a few minutes and rethink your design. As for platformspecific handles you could simply wrap them up, which is basically what joanusdmentia did.


t0}{!c

Share this post


Link to post
Share on other sites
Quote:
Original post by t0Xic
dynamic_cast<>() requires c++ RTTI-system to be enabled. This afaik intoduces some overhead not only to your specific class but to all classes in your code.


It's true: lattice traversal information is added to each class. This results in a slightly larger executable. However, speed and runtime memory usage are unaffected.

Quote:

To ensure that your class is a B, you could simply write your own little RTTI for that class.


This would most probably result in time and per-instance memory overhead for that class, not to mention that it would be unsafe. Using virtual functions to perform typecasts (as I outlined above) has no such overheads, is typesafe and requires less work.

Share this post


Link to post
Share on other sites
Quote:
Original post by joanusdmentia
Quote:
Original post by Anonymous Poster
@joanusdmentia:
That's a really good idea. Then just #ifdef around the headers you need.


No #ifdef's are required at all. The majority of your code will only include the 'PlatformWindow.h' file (which contains no platform-specific types). The parts which are platform specific (eg. the window's implementation, Direct3D, etc) will then include the appropriate platform specific header (eg. Win32Impl.h). By keeping each platform's implementations in different .cpp files you can compile and link only the source files for the current platform.


I like that method. I think I will use it in conjucnction with a ifdef for a handle. I dont want to have to parse that out in d3d or other stuff. Who knows may even do your handle method. Thanks for the idea.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this