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