Cast vs Virtual Method vs TypeId?

QAston via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Thu Jun 30 10:07:55 PDT 2016


On Thursday, 30 June 2016 at 00:25:53 UTC, Jonathan Marler wrote:
> I'd like to hear peoples thoughts on the various solutions for 
> the following problem.  Say you have some hierarchy of classes 
> like:
>
> class GameObject {
>   // ...
> }
> class Entity : GameObject {
>   // ...
> }
> class Player : Entity {
>   // ...
> }
> class Enemy : Entity {
>   // ...
> }
> // ...
>
> Assume you have a function that accepts a GameObject but does 
> something special if that GameObject happens to be an instance 
> of the Player class. How would you go about determining this? 
> (Note: assume you need to make the distinction at runtime, so 
> you can't use a static if with an 'is' expression inside a 
> template.)
>
> I'd like to hear what people think in 2 cases
>   1) Only need to know if it's an instance of the Player class.
>   2) Need to know if it's an instance of the Player class AND 
> need an instance of the Player class.
>
> The potential solutions I thought of were:
>
> 1) typeid (Only handles case 1)
>
> if(typeid(obj) == typeid(Player) {
>   // treat as player object
> }
>
> If you don't need an instance of the Player class, maybe this 
> one is good? I don't know in terms of efficiency if this is 
> better, or casting is better.  Maybe cast uses the typeid under 
> the hood to determine if the cast can be performed?
>
> 2) Custom Type Enum (Only handles case 1)
>
> enum GameObjectType {
>   gameObject, player, ...
> }
> class GameObject {
>   GameObjectType type;
> }
>
> if(obj.type == GameObjectType.player) {
>   // treat it as a player object
>   // Note: if you need to use Player class specific fields
>   //       then you'll need to use the cast or virtual function
>   //       design which kinda defeats the purpose of this 
> design in the
>   //       case where it is actually a Player object.
> }
>
> This method may be similar to the typeid method, not sure since 
> I don't know how typeid works under the hood.  If it's similar 
> then this would just be a waste of memory and should not be 
> used in favor of the typeid method.
>
> 3) Cast (Handles case 1 and 2)
>
> auto player = cast(Player)obj;
> if(player) {
>   // treat it as a player object
> }
>
> I don't know how cast works under the hood so it's hard to 
> compare it to other methods.  Any information on how cast works 
> under the hood would be great.
>
> 4) Virtual Method (Handles case 1 and 2)
>
> class GameObject {
>   Player asPlayer() { return null; }
> }
> class Player {
>   override Player asPlayer() { return this; }
> }
>
> auto player = obj.asPlayer;
> if(player) {
>   // treat it as a player object
> } else {
>   // treat it as any other game object
> }
>
> This solution handles the same cases as regular casting, but I 
> can't compare them since I don't know how casting works under 
> the hood.  One thing to consider is that this method scales 
> linearly since you need to add a new virtual method for every 
> type you want to support, so the vtable gets larger as you add 
> more types.
>
>
> Any other solutions? Thoughts?  Thanks in advance for the 
> feedback.

This problem is not D specific, there are lots of different 
approaches.
If you're not going to add new types to the hierarchy very often 
the visitor pattern is a good choice, see[1]. You can also use 
composition instead of inheritance to share code by using mixins 
+ templates. Or you can try more traditional 
entity-component-systems.

Take a look at game engines written in D, they may have some good 
D specific ideas already.

http://www.deadalnix.me/2012/08/25/visitor-pattern-revisited-in-d/


More information about the Digitalmars-d-learn mailing list