www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Base class with member parameterized on type of extending class

reply "rcor" <murphyslaw480 gmail.com> writes:
I'm trying to make a game, and would like to set up the following 
hierarchy:
At any time, the game is in one Scene. Each scene has a state 
machine that manages States of type T, where T is the type of the 
scene (e.g. Overworld, Menu).

abstract class State!T {
   void update(T scene, float time, InputManager input);
   ...
}

class StateMachine!T { //manages states of type State!T }

abstract class Scene {
   alias T = // type of class extending Scene
   // methods pushState, popState, currentState access 
_stateMachine
   private StateMachine!T _stateMachine;
}

class MainMenu : Scene {
    // I want _stateMachine of type StateMachine!MainMenu
}

class Overworld : Scene {
   // I want _stateMachine of type StateMachine!Overworld
}

class MoveToLocation : State!Overworld {
   override void update(Overworld world, float time, InputManager 
input) {
     // access properties of Overworld here
   }
}

Within the Scene class, I've tried alias T = typeof(this), but 
that appears to be resolved within Scene. This means that any 
Scene, such as Overworld, have a state machine of type 
StateMachine!Scene rather than StateMachine!Overworld. Since 
States are particular to a certain scene and are designed to 
manipulate properties specific to that type of scene, this would 
involve a lot of casting if States are not parameterized. It 
feels like I need something like a class version of the (this T) 
syntax used in templates.

This all smells a bit off though, so I wouldn't be surprised if 
the answer is that I'm approaching this all wrong, but right now 
I'm not seeing it.
Oct 19 2014
parent reply Jacob Carlborg <doob me.com> writes:
On 2014-10-19 13:19, rcor wrote:
 I'm trying to make a game, and would like to set up the following
 hierarchy:
 At any time, the game is in one Scene. Each scene has a state machine
 that manages States of type T, where T is the type of the scene (e.g.
 Overworld, Menu).

 abstract class State!T {
    void update(T scene, float time, InputManager input);
    ...
 }

 class StateMachine!T { //manages states of type State!T }

 abstract class Scene {
    alias T = // type of class extending Scene
    // methods pushState, popState, currentState access _stateMachine
    private StateMachine!T _stateMachine;
 }

 class MainMenu : Scene {
     // I want _stateMachine of type StateMachine!MainMenu
 }

 class Overworld : Scene {
    // I want _stateMachine of type StateMachine!Overworld
 }

 class MoveToLocation : State!Overworld {
    override void update(Overworld world, float time, InputManager input) {
      // access properties of Overworld here
    }
 }

 Within the Scene class, I've tried alias T = typeof(this), but that
 appears to be resolved within Scene. This means that any Scene, such as
 Overworld, have a state machine of type StateMachine!Scene rather than
 StateMachine!Overworld. Since States are particular to a certain scene
 and are designed to manipulate properties specific to that type of
 scene, this would involve a lot of casting if States are not
 parameterized. It feels like I need something like a class version of
 the (this T) syntax used in templates.

 This all smells a bit off though, so I wouldn't be surprised if the
 answer is that I'm approaching this all wrong, but right now I'm not
 seeing it.
You can always make Scene a template class: abstract class Scene (T) { private StateMachine!T _stateMachine; } class MainMenu : Scene!(MainMenu) {} But I'm guessing you like to avoid that if possible. -- /Jacob Carlborg
Oct 19 2014
parent reply "rcor" <murphyslaw480 gmail.com> writes:
On Monday, 20 October 2014 at 06:17:42 UTC, Jacob Carlborg wrote:
 You can always make Scene a template class:

 abstract class Scene (T)
 {
     private StateMachine!T _stateMachine;
 }

 class MainMenu : Scene!(MainMenu) {}

 But I'm guessing you like to avoid that if possible.
I would, as I need to keep track of the current scene in a variable somewhere: Scene _currentScene; // problematic if Scene is a template I could just declare the StateMachine separately in every Scene, but that seems like a lot of duplicate code (I then repeat the same code for updating the state machine, ect.)
Oct 20 2014
parent reply Jacob Carlborg <doob me.com> writes:
On 2014-10-20 12:27, rcor wrote:

 I would, as I need to keep track of the current scene in a variable
 somewhere:

    Scene _currentScene; // problematic if Scene is a template
If the state machine doesn't need to be exposed you can create base class for Scene which is not templated: abstract class Scene {} // As it is now minus the state machine abstract class ConcreteScene (T) : Scene { private StateMachine!T _stateMachine; // other code that need access to _stateMachine } class MainMenu : ConcreteScene!(MainMenu) {} Scene _currentScene = new MainMenu; "ConcreteScene" might not be the best name of an abstract class.
 I could just declare the StateMachine separately in every Scene, but
 that seems like a lot of duplicate code (I then repeat the same code for
 updating the state machine, ect.)
Or you could use a template mixin: template StateMachineMixin (T) { private StateMachine!T _stateMachine; // other code that need access to _stateMachine } class MainMenu : Scene { mixin StateMachineMixin!(typeof(this)); } -- /Jacob Carlborg
Oct 20 2014
parent "rcor" <murphyslaw480 gmail.com> writes:
 If the state machine doesn't need to be exposed you can create 
 base class for Scene which is not templated:

 abstract class Scene {} // As it is now minus the state machine

 abstract class ConcreteScene (T) : Scene
 {
     private StateMachine!T _stateMachine;

     // other code that need access to _stateMachine
 }

 class MainMenu : ConcreteScene!(MainMenu)  {}

 Scene _currentScene = new MainMenu;

 "ConcreteScene" might not be the best name of an abstract class.
Just came up with something similar before I saw this post: interface IScene { // enter, exit, update, draw } class Scene!T : IScene { private StateMachine!T _stateMachine; void update(float time) { _stateMachine.update(cast(T) this, time); } } The cast is unfortunate but since it only happens once per update cycle I'm not that worried about it.
 I could just declare the StateMachine separately in every 
 Scene, but
 that seems like a lot of duplicate code (I then repeat the 
 same code for
 updating the state machine, ect.)
Or you could use a template mixin: template StateMachineMixin (T) { private StateMachine!T _stateMachine; // other code that need access to _stateMachine } class MainMenu : Scene { mixin StateMachineMixin!(typeof(this)); }
Interesting idea, I might give this a try but the first suggestion seems fine for now. Thanks!
Oct 20 2014