Thread overview
Base class with member parameterized on type of extending class
Oct 19, 2014
rcor
Oct 20, 2014
Jacob Carlborg
Oct 20, 2014
rcor
Oct 20, 2014
Jacob Carlborg
Oct 20, 2014
rcor
October 19, 2014
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.
October 20, 2014
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
October 20, 2014
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.)
October 20, 2014
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
October 20, 2014
> 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!