• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
Numsgil

dynamic_cast in MSVC

32 posts in this topic

Visual Studio (C++) apparently does a string compare when you call dynamic_cast, to handle edge cases of inheriting across assembly boundaries. This makes an operation that wasn't all that fast to begin with excruciatingly slow. Is there a way to tell the compiler not to do that? Let it assume that all libraries will either be static linked or not have classes inherit across assembly boundaries?
0

Share this post


Link to post
Share on other sites
It should only ever need to do pointer compares, which are quite fast. Have you profiled it? Have you looked at the assembly? Unless you can measure that it's slower, then it's not really worth worrying about...
0

Share this post


Link to post
Share on other sites
Quote:
Original post by bgarney
It should only ever need to do pointer compares, which are quite fast. Have you profiled it? Have you looked at the assembly? Unless you can measure that it's slower, then it's not really worth worrying about...

No, he's right. MSVC's dynamic_cast will perform a full string comparison between class names. If you've got a deep hierarchy, it is indeed very slow. If you see strcmp bubbling to the top in the profiler, dynamic_cast is a possible suspect.

Unfortunately, I don't believe any such compiler switch exists. The best solution, if possible, would be to eliminate the need for dynamic_cast in the first place.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Sc4Freak
Quote:
Original post by bgarney
It should only ever need to do pointer compares, which are quite fast. Have you profiled it? Have you looked at the assembly? Unless you can measure that it's slower, then it's not really worth worrying about...

No, he's right. MSVC's dynamic_cast will perform a full string comparison between class names. If you've got a deep hierarchy, it is indeed very slow. If you see strcmp bubbling to the top in the profiler, dynamic_cast is a possible suspect.

Unfortunately, I don't believe any such compiler switch exists. The best solution, if possible, would be to eliminate the need for dynamic_cast in the first place.


Yeah, right now it's all tucked in level initialization code, so it's not slowing the game down, but it's just a pain that it's doing that. And it limits me to only really using it during initialization. And it makes it a hard sell to other programmers who are just C casting instead. I really only use it for things like this:


class A { virtual ~A(); };
class B : public A { };

A *APointer = new B();

...

B *BPointerQuery = dynamic_cast<B *>(APointer);
if(BPointerQuery)
//...


That is, I'm upcasting to a universal base class, then downcasting back to the original type. I could implement my own RTTI system, using virtual functions, but it's a pain to maintain.

Is there a way to build my own hacky version of a RTTI by somehow fiddling with the virtual table pointer? It would only need to be MSVC specific, since I think MSVC is the only one that does it like this. And it can be much stupider than the usual dynamic_cast since I'm only interested in upcasting then downcasting back to the same type. Something like this(?):


#define HACKY_POINTER_DYNAMIC_CAST(DesiredType, Pointer)
#ifdef _MSC_VER
// hacky
#else
dynamic_cast<DesiredType *>(Pointer)
#endif


(I know there are some macro tricks I should use here that I'm not, but it's purely a quick and dirty example). Or maybe use templates so I can still use the label<Type *>(pointer) format.

[Edited by - Numsgil on September 19, 2008 1:38:22 PM]
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Numsgil

Is there a way to build my own hacky version of a RTTI by somehow fiddling with the virtual table pointer?



Yes but it's VERY hacky and VERY scary. It requires using implementation details of how MSVC stores vtable pointers and using some assembly to modify portions of your code. SiCrane talks about it in detail towards the end of this thread.



0

Share this post


Link to post
Share on other sites
Quote:
Original post by MJP
Quote:
Original post by Numsgil

Is there a way to build my own hacky version of a RTTI by somehow fiddling with the virtual table pointer?



Yes but it's VERY hacky and VERY scary. It requires using implementation details of how MSVC stores vtable pointers and using some assembly to modify portions of your code. SiCrane talks about it in detail towards the end of this thread.


Heh, that's a bit too hacky and scary :)
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Numsgil
Quote:
Original post by Sc4Freak
Quote:
Original post by bgarney
It should only ever need to do pointer compares, which are quite fast. Have you profiled it? Have you looked at the assembly? Unless you can measure that it's slower, then it's not really worth worrying about...

No, he's right. MSVC's dynamic_cast will perform a full string comparison between class names. If you've got a deep hierarchy, it is indeed very slow. If you see strcmp bubbling to the top in the profiler, dynamic_cast is a possible suspect.

Unfortunately, I don't believe any such compiler switch exists. The best solution, if possible, would be to eliminate the need for dynamic_cast in the first place.


Yeah, right now it's all tucked in level initialization code, so it's not slowing the game down, but it's just a pain that it's doing that. And it limits me to only really using it during initialization.


Limiting the use of dynamic_cast is the way it should be, you shouldn't be seeking to change that. An abundance of dynamic_casts (or any type of cast, really) points to design problems.

Quote:
I really only use it for things like this:


class A { virtual ~A(); };
class B : public A { };

A *APointer = new B();

...

B *BPointerQuery = dynamic_cast<B *>(APointer);
if(BPointerQuery)
//...


That is, I'm upcasting to a universal base class, then downcasting back to the original type. I could implement my own RTTI system, using virtual functions, but it's a pain to maintain.


If you're design isn't malleable enough right now to restructure things such that dynamic_cast isn't needed, then you might consider a variant of the visitor pattern.

Or you could do something even simpler by adding a virtual function to your hierarchy that does something in B's overridden implementation, but nothing by default. It's not particularly neat, but it's no worse than your casts.

Quote:
Is there a way to build my own hacky version of a RTTI by somehow fiddling with the virtual table pointer? It would only need to be MSVC specific, since I think MSVC is the only one that does it like this. And it can be much stupider than the usual dynamic_cast since I'm only interested in upcasting then downcasting back to the same type. Something like this(?):
[code]


Don't even go there :)

IMHO, it would be better to get out of this "it's ok to use dynamic_cast a lot" mindset.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by the_edd
Limiting the use of dynamic_cast is the way it should be, you shouldn't be seeking to change that. An abundance of dynamic_casts (or any type of cast, really) points to design problems.


That depends. Should we avoid dynamic casts because they're slow, or because they lead to bad designs? A program in another language using dynamic typing is certainly valid if that's how the language is meant to be used. Ruby for example. I haven't done much dynamic typing myself, but I recognize lots of strengths from it.

I would say you should avoid dynamic casts because they're slow, not because there's something inherently less flexible, or more risky, about a design incorporating them. Well, that and C++ is really built to be statically typed, and you're kind of twisting it to be dynamically typed. So you might code yourself in to a dead end with language limitations. But at its core I don't think there's anything evil about dynamic casting. Or I haven't seen any evidence of it anyway.
0

Share this post


Link to post
Share on other sites
Checking a type with if/else is always a bad design.
If you want to check different possibilities of types, you should perform an actual visitation, which will actually scale and won't allow you to forget cases.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by loufoque
Checking a type with if/else is always a bad design.
If you want to check different possibilities of types, you should perform an actual visitation, which will actually scale and won't allow you to forget cases.


Sure if you're essentially switch casing on every possible case you're probably better off restructuring a bit. I mean more things like this:


void SerializeToNetwork(Object *Ob)
{
INetworkable *network = dynamic_cast<INetworkable *>(Ob);
if(network)
network->SendNetworkData();
}


The idea being you don't want to pollute the def of Object with networking logic, but you want to be able to catch when an object implements the Networkable interface. There's probably other ways of doing this, but what's inherently bad about a design like this? (I don't mean this rhetorically, I'm honestly asking).

Note, this isn't actually the case I have in my code right now, this is just a hypothetical example.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Numsgil
Quote:
Original post by the_edd
Limiting the use of dynamic_cast is the way it should be, you shouldn't be seeking to change that. An abundance of dynamic_casts (or any type of cast, really) points to design problems.


That depends. Should we avoid dynamic casts because they're slow,

Yes.

Quote:
or because they lead to bad designs?

And double yes :)

Quote:
A program in another language using dynamic typing is certainly valid if that's how the language is meant to be used.


These languages don't use dynamic casts. They simply use polymorphism. Perhaps the implementation of the language interpreter has type-switching or something functionally equivalent to a dynamic_cast internally, but this is hidden inside there because it's a bad idea to put it into user code.

For example in Python when I call a lambda or a function or an instance of a class that has a __call__ method, the interpreter doesn't dynamic_cast the object I'm calling to a function. It simply asks the object if it's callable.

Quote:
Ruby for example. I haven't done much dynamic typing myself, but I recognize lots of strengths from it.


Dynamically typing is certainly nice for a lot of things, but it has nothing inherently to do with dynamic casts. It's just a higher level of polymorphism.

Quote:
I would say you should avoid dynamic casts because they're slow, not because there's something inherently less flexible, or more risky, about a design incorporating them. Well, that and C++ is really built to be statically typed, and you're kind of twisting it to be dynamically typed. So you might code yourself in to a dead end with language limitations. But at its core I don't think there's anything evil about dynamic casting. Or I haven't seen any evidence of it anyway.


Well I would say, as your example shows, that you could redesign your hierarchy to avoid it i.e. you're not using the tools for polymorphism that the statically typed language provides, C++ in this case.

But as you point out, this isn't always easy and sometimes your program yourself in to a corner because runtime polymorphism in languages such as C++ is only really doable via inheritance. Since inheritance is a very strong form of coupling, this polymorphic behaviour is hard to change and you might end up having to do a dynamic_cast here or there. But this is the exception -- it's there because you've got yourself stuck. It's not a means to avoid getting stuck in the first place. Otherwise the syntax would be much nicer; the C++ cast syntax is designed to look ugly for a reason.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by the_edd
For example in Python when I call a lambda or a function or an instance of a class that has a __call__ method, the interpreter doesn't dynamic_cast the object I'm calling to a function. It simply asks the object if it's callable.


How is that different to the end user to something like this (aside from implementation details of dynamic_cast):


ISomeInterface *pointer = dynamic_cast<ISomeInterface *>(MyObject);
if(pointer)
pointer->SomeFunction();
else
//some error, or ignore


Other than that some languages allow duck typing so you don't even need to know the name of your interface. You can just use the name of the function. You can even collapse that code above in to a macro if you're adverse to things that you consider ugly :) Then it'd just be ATTEMPT_ACTION(MyPointer, MyInterface, MyFunction);

Your post seems to say not to use dynamic cast because it's dynamic cast, and you're not supposed to use that. That is, the argument sounded cyclical. "These languages don't use dynamic_cast, they just use something that is exactly the same but not called that."

I also don't find template notation as ugly as most people seem to find it. Angle brackets don't make me immediately wet myself. Writing templates, on the other hand... :)
0

Share this post


Link to post
Share on other sites
Quote:
Original post by NumsgilYour post seems to say not to use dynamic cast because it's dynamic cast, and you're not supposed to use that. That is, the argument sounded cyclical. "These languages don't use dynamic_cast, they just use something that is exactly the same but not called that."

Another way to look at is to say that *explicit* dynamic casts are the problem. Of course something similar happens under the hood in Python or Ruby, but the programmer doesn't perform the cast explicitly.
What the language does internally doesn't really count. You could say that internally, C/C++ uses goto's all over the place (because it compiles to assembly, where goto is standard practice), but that doesn't mean you, the programmer, should be writing goto's all over the place.

By the way, something that puzzles me a bit:
Quote:
And it makes it a hard sell to other programmers who are just C casting instead

How does that work? If people can get away with using a plain C cast instead, it seems like your dynamic casts aren't necessary. And if they aren't necessary, why are you using them?

Unless I misunderstand you, it seems like you're using dynamic casts in cases where you could get away with a static_cast.


Also have you looked into boost.variant or boost.any? If you just need a way to store a bunch of objects in a single container, using one of those instead might solve your problem.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Spoonbender
By the way, something that puzzles me a bit:
Quote:
And it makes it a hard sell to other programmers who are just C casting instead

How does that work? If people can get away with using a plain C cast instead, it seems like your dynamic casts aren't necessary. And if they aren't necessary, why are you using them?

Unless I misunderstand you, it seems like you're using dynamic casts in cases where you could get away with a static_cast.


The main difference being a C cast doesn't check, it's basically you saying 'yeah, trust me on this, it'll work'. The C++ casts on the other hand are more explicate in what they are doing AND in the case of dynamic_cast do a runtime check.

It's the age old story of programming; safety at the cost of runtime speed.
C casts are "fast" because they don't check, they are also more likely to asplode in your face if you ever feed it something wrong. However, while it works it's a hard sell to say to people 'look, use the safe version' because they'll just turn around and say 'but it's slow and we'll never make that mistake..' (insert sitcom laugh track here).

0

Share this post


Link to post
Share on other sites
Quote:
Original post by Numsgil:
void SerializeToNetwork(Object *Ob)
{
INetworkable *network = dynamic_cast<INetworkable *>(Ob);
if(network)
network->SendNetworkData();
}
In my engine, I would write it like this:
void SerializeToNetwork(Object *Ob)
{
INetworkable *network = Ob->GetInterface<INetworkable>();
if(network)
network->SendNetworkData();
}

GetInterface looks like this:
template<class T>
T* Object::GetInterface()
{
InterfaceMap::iterator itInterface = m_Interfaces.find( GetTypeInfo<T>() );
if( itInterface != m_Interfaces.end() )
{
return static_cast<T*>( itInterface->second );
}
return NULL;
}

N.B. m_Interfaces is a std::map<TypeInfo,void*>
In the objects constructor, it populates m_Interfaces by doing static_casts to each interface type that it supports.
e.g. m_Interfaces[GetTypeInfo<INetworkable>()] = static_cast<INetworkable*>( this );

TypeInfo and GetTypeInfo are my wrapper for C++'s native RTTI. On compilers that have fast RTTI I just let the compiler do it, but on MSVC I try and optimize type comparisons:
	#if the compiler has fast RTTI
class TypeInfo
{
public:
TypeInfo() : t(0) {}
TypeInfo( const std::type_info& t ) : t(&t) {}
TypeInfo( const TypeInfo& t ) : t(t.t) {}
bool operator==(const TypeInfo& rhs) const { return t == rhs.t || (t && rhs.t && *t == *rhs.t); }
bool operator!=(const TypeInfo& rhs) const { return !(*this == rhs); }
private:
const std::type_info* t;
};
template<class T>
inline TypeInfo GetTypeInfo() { return typeid( T ); }
#else
typedef uint32 TypeInfo;
template<class T>
inline TypeInfo GetTypeInfo( ) { static uint32 hashT = hash::Fnv32a(typeid(T).name()); return hashT; }
#endif

This does run the risk of hash-collisions though, so the next thing I'm going to do with this code is check for collisions in debug builds...

Also this system bloats the size of your objects because the interface map is stored per-object, not per-class (but you can use that to have objects choose whether they expose an interface or not at runtime, if needed - e.g. I use this interface system for inter-entity communication in my component system which require runtime-composition of classes).
0

Share this post


Link to post
Share on other sites
@phantom: yes, that's my thinking. We could get away with static casting but that puts a lot of faith in a system that was hastily built to meet a milestone goal. It'd be far better if it were something we could leverage from the language so we knew, period, that this is not a potential source for bugs.

Quote:
Original post by Hodgman
Quote:
Original post by Numsgil:
void SerializeToNetwork(Object *Ob)
{
INetworkable *network = dynamic_cast<INetworkable *>(Ob);
if(network)
network->SendNetworkData();
}
In my engine, I would write it like this:*** Source Snippet Removed ***
GetInterface looks like this:*** Source Snippet Removed ***
N.B. m_Interfaces is a std::map<TypeInfo,void*>
In the objects constructor, it populates m_Interfaces by doing static_casts to each interface type that it supports.
e.g. m_Interfaces[GetTypeInfo<INetworkable>()] = static_cast<INetworkable*>( this );

TypeInfo and GetTypeInfo are my wrapper for C++'s native RTTI. On compilers that have fast RTTI I just let the compiler do it, but on MSVC I try and optimize type comparisons:*** Source Snippet Removed ***
This does run the risk of hash-collisions though, so the next thing I'm going to do with this code is check for collisions in debug builds...

Also this system bloats the size of your objects because the interface map is stored per-object, not per-class (but you can use that to have objects choose whether they expose an interface or not at runtime, if needed).


Yeah, I had thought along those lines. Essentially you invent your own RTTI system that's more efficient than the built in one. Which is cool and good when the built in one is slow (ala MSVC). It's just a pain because it's so brittle to overworked programmers forgetting to add an interface. Every new class has to do something in it's ctor, and it's easy to forget. Every new interface you add needs to add something to the ctor and that's easy to forget, too. And heaven forbid if you remove and interface and forget to modify the ctor.

It just seems like it'd make more sense to let the language handle it, if it weren't so dang blasted terrible at doing it efficiently.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Numsgil
It's just a pain because it's so brittle to overworked programmers forgetting to add an interface. Every new class has to do something in it's ctor, and it's easy to forget. Every new interface you add needs to add something to the ctor and that's easy to forget, too. And heaven forbid if you remove and interface and forget to modify the ctor.
Yeah, remembering to register interfaces is a problem with my system, but if you remove an interface and are still trying to register it then your code wont compile (invalid static_cast).

The engine I use at work also implements it's own RTTI using magic macros that just have to be put into the class declaration. If you forget to add the macros you'll also get compiler errors to remind you, so it's easy to maintain. The downside of this particular system is that it doesn't allow for multiple inheritance, which is useful in the case of exposing interfaces, but might not be required in your case.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Hodgman
In my engine, I would write it like this
void SerializeToNetwork(Object *Ob)
{
INetworkable *network = Ob->GetInterface<INetworkable>();
if(network)
network->SendNetworkData();
}
Much like the COM approach:
void SerializeToNetwork(IUnknown *Ob)
{
CComQIPtr<INetworkable> network = Ob;
if (network)
network->SendNetworkData();
}
0

Share this post


Link to post
Share on other sites
This approach to components always left a bad taste in my mouth, mostly because of high access cost to individual components.

dynamic_cast may be convenient, but it's slow (not because of string comparison, but because of its existence in first place.


Establishing use cases first may help eliminate the penalty altogether.

For network serialization, the ad-hoc transmission typically isn't needed. When an object is serialized to a certain client, it will need to be updated as well. This means that once sent, client subscribes to changes.

The approach then involves several components:
- When client is first connected, it is made aware of all the objects in range.
void onSubscribe(Client * c) {
Point3D p = c->getPosition();
for (every e in world->serializationManager->entities) {
if (e->inRange(p) e->subscribe(c);
}
};

- When a component subscribes it sends the state using internal routines:

void Serializable::onCreate(World * world) {
world->addOnTickListener(&Serializable::OnTick, this);
world->serializationManager.add(this);
}
void Serializable::onSubscribe(Client *c) {
subscribers->add(c);
Message msg = packInitialState();
c->send(msg);
};
void Serializable::OnTick() {
Message msg = packDeltaState();
for (every c in subscribers) {
c->send(msg);
}
};

....
// GenericComponentManager<Serializable> World::serializationManager;



This has two advantages. First, whenever a serialization component (or any component for that matter) is added, it embeds itself world. Serialization component knows what and why it exists, and world can keep track of it without knowing which entity it belongs to.

If you look above, you'll see that no dynamic casts are needed anywhere, since we have inverted to flow control. Component becomes self-aware, and registers on construction.

The above can also be implemented once, or extended, in later case, there is no extra work to be done with regard to registration, perhaps just implementing virtual packXXX() functions.


I find that whenever you need to query some objects for some OO concept, the design should be changed to avoid it. "If (x instanceof Y)" indicates a problem which is typically solvable without query and without cast.

The above approach retains the ad-hoc run-time object composability, but has O(1) access time to components, and a guaranteed hit rate when trying to use a certain part of certain entity with very low (negligble) access cost. Compared to O(logn) each time you try to use a component plus a high cost (dynamic_cast) on every access to component.

YMMV.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Numsgil
Quote:
Original post by MJP
Quote:
Original post by Numsgil
Is there a way to build my own hacky version of a RTTI by somehow fiddling with the virtual table pointer?

Yes but it's VERY hacky and VERY scary. It requires using implementation details of how MSVC stores vtable pointers and using some assembly to modify portions of your code. SiCrane talks about it in detail towards the end of this thread.

Heh, that's a bit too hacky and scary :)

Actually, that was just for implementing your own typeid. Implementing dynamic_cast is even more hacky and scary.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Numsgil
That is, I'm upcasting to a universal base class,


This is the root of all of your pain. Redesign your system so you don't do this. Either don't do this, or make everything you need to do possible with just the base class pointer.

In your design, since you need the actual derived class type (which is why you seeked out dynamic_cast), you shouldn't be storing things as a Base*, losing the type information.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
dynamic_cast may be convenient, but it's slow (not because of string comparison, but because of its existence in first place.


?

Quote:

Establishing use cases first may help eliminate the penalty altogether.

For network serialization, the ad-hoc transmission typically isn't needed. When an object is serialized to a certain client, it will need to be updated as well. This means that once sent, client subscribes to changes.

...

I find that whenever you need to query some objects for some OO concept, the design should be changed to avoid it. "If (x instanceof Y)" indicates a problem which is typically solvable without query and without cast.

The above approach retains the ad-hoc run-time object composability, but has O(1) access time to components, and a guaranteed hit rate when trying to use a certain part of certain entity with very low (negligble) access cost. Compared to O(logn) each time you try to use a component plus a high cost (dynamic_cast) on every access to component.

YMMV.


Sure, you can always reorder a problem to avoid almost any C++ feature you can think of. None of it's necessary beyond what C had. Your solution has concrete instances register themselves with a manager, and listen for events. Which is certainly a fine solution. I won't argue that. My point more is that that is not the only way to do things. It might not be appropriate for every project.

Using dynamic casts to query for interface implementations is another way. The advantage being that it's easy to maintain and the engine doesn't have to know about classes on the game side that might have been created after it was. The engine just asks stupid questions like "do you know how to draw yourself?" and "do you understand the idea of position?" If you add a new interface to the engine, you don't have to add a new manager to handle that. And if you add an interface to a class, you don't have to worry about a lot of extra code to make sure it registers itself with various managers. If it inherits from an interface, the engine knows how to use it. No special tricks.

You push most of that tedious overhead down to the language level. Your game code and your engine code keep a clean interface with each other: they mostly just communicate through interfaces.

All that said, I'm not advocating a system like that in C++ because I really do understand that dynamic_cast is awfully slow. On par with several string compares, 'cause that's what it's actually doing. I'm just saying, that speed issue aside, there's nothing inherently flawed about a design that needs to use RTTI. Again, seriously, speed issue aside. If one more person brings up the point that dynamic cast is slow and that's why you shouldn't use it... I know! I never said it wasn't. It doesn't have to be, though. It's entirely possible to have a language that implements dynamic_cast on the cheap. It's like pointing to bubble sort as the reason why you should avoid sorting integers (ie: there's such a thing as a radix sort when you're dealing with integers).

Quote:
Original post by RDragon1
Quote:
Original post by Numsgil
That is, I'm upcasting to a universal base class,


This is the root of all of your pain. Redesign your system so you don't do this. Either don't do this, or make everything you need to do possible with just the base class pointer.

In your design, since you need the actual derived class type (which is why you seeked out dynamic_cast), you shouldn't be storing things as a Base*, losing the type information.


Maybe I don't know the type information. Maybe the type is defined in a module that is a client to mine. All I know is that I can ask it questions about what interfaces it implements. Things like "do you know how to draw yourself" and "do you understand the idea of position."
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Numsgil
Quote:
Original post by the_edd
For example in Python when I call a lambda or a function or an instance of a class that has a __call__ method, the interpreter doesn't dynamic_cast the object I'm calling to a function. It simply asks the object if it's callable.


How is that different to the end user to something like this (aside from implementation details of dynamic_cast):


ISomeInterface *pointer = dynamic_cast<ISomeInterface *>(MyObject);
if(pointer)
pointer->SomeFunction();
else
//some error, or ignore



It's a hash-table look up rather than a cast, which isn't important in itself I admit. But a dynamic_cast says "I haven't designed my hierarchy quite right here". Nothing in the Python code says that. 99.9% of the time in python, that function is going to exist. If it doesn't it's probably a programmer error.

Quote:

Other than that some languages allow duck typing so you don't even need to know the name of your interface. You can just use the name of the function. You can even collapse that code above in to a macro if you're adverse to things that you consider ugly :) Then it'd just be ATTEMPT_ACTION(MyPointer, MyInterface, MyFunction);


Well, the point I was trying to make is that dynamic_cast is ugly for a *reason* masking that reason is a very bad idea, IMHO.

Quote:

Your post seems to say not to use dynamic cast because it's dynamic cast, and you're not supposed to use that. That is, the argument sounded cyclical. "These languages don't use dynamic_cast, they just use something that is exactly the same but not called that."


Well, I was arguing against the specific points you raised in my own reference frame. I don't have anything against dynamic_cast itself - it's existence is necessary, but its use generally points to design problems, as I see it, and so having lots of dynamic_casts indicates that design should be thought about some more. Lots of programs get by without dynamic cast just fine.

But if you don't agree about the pointing-to-design-problems part, then we won't reach any resolution in this exchange and we'll have to leave it at that :)

Quote:

I also don't find template notation as ugly as most people seem to find it. Angle brackets don't make me immediately wet myself. Writing templates, on the other hand... :)


Well, me neither. I'm a bit of a template nut myself, but dynamic_cast<XYZ>(lala) sticks out more than (XYZ)lala. It tells me the reason you're casting. Dynamic cast says "I couldn't get my hierarchy quite right (or someone else couldn't get theirs quite right)" so I have to do a manual type-check here. That's what I mean really by "looking ugly".
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Hodgman
In my engine, I would write it like this
void SerializeToNetwork(Object *Ob)
{
INetworkable *network = Ob->GetInterface<INetworkable>();
if(network)
network->SendNetworkData();
}


The problem here is obvious: you're using a top type.
An object manipulated through a top type is useless, you lost all of the useful information. What you should pass to SerializeToNetwork should be a const INetworkable&. Basically, what makes the most sense.

By the way, you shouldn't use pointers but references, and you should make your code const-correct (I doubt serializing an object should change its state; but to be honest I don't even understand your network/serialization relationship, those seem to be different layers to me)
0

Share this post


Link to post
Share on other sites
Quote:
Original post by loufoque
Quote:
Original post by Hodgman
In my engine, I would write it like this
void SerializeToNetwork(Object *Ob)
{
INetworkable *network = Ob->GetInterface<INetworkable>();
if(network)
network->SendNetworkData();
}


The problem here is obvious: you're using a top type.
An object manipulated through a top type is useless, you lost all of the useful information. What you should pass to SerializeToNetwork should be a const INetworkable&. Basically, what makes the most sense.


And where does that INetworkable reference come from? Either you're downcasting at some point (and need to use dynamic cast or your own RTTI if you want to be safe about it) or every interface has a list of objects that implement it. Which would mean a list for every interface, which is a pain to maintain. And you have to remember to register your classes with all the lists for the interfaces it implements. Or use some macro magic. Neither are great options. But we're forced to use them because of the performance limitations of dynamic cast.

Quote:

By the way, you shouldn't use pointers but references, and you should make your code const-correct (I doubt serializing an object should change its state; but to be honest I don't even understand your network/serialization relationship, those seem to be different layers to me)


Seriously, don't be so literally minded; my example was strictly off the cuff. My primary point was to illustrate a kind of design where you query objects to see if they know how to behave like certain abstract objects. If you got anything else from my example I make no claims to its correctness :)
0

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  
Followers 0