Skip to content

Instantly share code, notes, and snippets.

@jstaursky
Last active July 22, 2022 15:34
Show Gist options
  • Save jstaursky/9e5e6482bc02912fca3e1e1d6fcca8da to your computer and use it in GitHub Desktop.
Save jstaursky/9e5e6482bc02912fca3e1e1d6fcca8da to your computer and use it in GitHub Desktop.
_DESIGN_PATTERN_state
#include <iostream>
class Context; // fwd declare. User should only deal with the Context class.
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class State
{
public:
State (Context *c) : ctx (c) {}
virtual ~State() {};
virtual void handle() = 0;
protected:
template <typename T> T* change_state(Context* context);
protected:
Context *ctx = nullptr;
};
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class ConcreteStateA : public State
{
public:
using State::State;
ConcreteStateA (Context *c) : State (c), detail_a("state is A")
{
std::cout << "A Ctor... " << std::endl;
}
void handle() override;
private:
std::string detail_a;
};
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class ConcreteStateB : public State
{
public:
using State::State;
ConcreteStateB (Context *c) : State (c), detail_b ("state is B")
{
std::cout << "B Ctor... " << std::endl;
}
void handle() override;
private:
std::string detail_b;
};
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class Context
{
friend class State;
public:
Context() : _state (new ConcreteStateA (this))
{}
~Context() { delete _state; }
void request() { _state->handle(); }
private:
State *_state;
};
/* IMPLEMENTATIONS */
template <typename T> T* State::change_state(Context* context)
{
std::cout << "CHANGE STATE CALLED" << std::endl;
delete context->_state;
context->_state = new T (context);
return static_cast<T*>(context->_state);
}
auto ConcreteStateA::handle() -> void
{
std::cout << "A State handle called." << std::endl;
auto updated_state = change_state<ConcreteStateB>(ctx);
// Need to return at this point. State is now ConcreteStateB. Accessing member variables
// that belong to ConcreteStateA at this point will cause crash (b/c 'change_state'
// called delete on 'CurrentState'). We are only still running inside ConcreteStateA at
// this point because code and data reside in different areas in memory.
updated_state->handle();
return;
}
auto ConcreteStateB::handle() -> void
{
std::cout << "B State handled called." << std::endl;
change_state<ConcreteStateA>(ctx);
// Need to return at this point. State is now ConcreteStateA. Accessing member variables
// that belong to ConcreteStateB at this point will cause crash (b/c 'change_state'
// called delete on 'CurrentState'). We are only still running inside ConcreteStateB at
// this point because code and data reside in different areas in memory.
return;
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int main (int argc, char *argv[])
{
Context *context = new Context();
context->request();
delete context;
return 0;
}
#include <iostream>
#include <memory>
class Context; // fwd declare.
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class State
{
public:
State (Context *context) : _context (context) {}
virtual ~State() {};
virtual void compute() = 0;
virtual void print_state_type () = 0;
protected:
Context *_context = nullptr;
};
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class A : public State
{
public:
using State::State;
A (Context *context) : State (context), datum (5 + (rand() % 6)) {}
void compute() override;
void print_state_type () override { std::cout << "state A" << std::endl; }
void a_specific() { std::cout << "IM UNIQUE TO STATE A!\n"; }
private:
int datum;
};
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class B : public State
{
public:
using State::State;
B (Context *context) : State (context), datum (2 + (rand() % 3)) {}
void compute() override;
void print_state_type () override { std::cout << "state B" << std::endl; }
void b_specific() { std::cout << "IM UNIQUE TO STATE B!\n"; }
private:
int datum;
};
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class Context
{
public:
Context() : _state (new A (this))
{}
void request() { _state->compute(); }
template <typename T>
T* change_state()
{
std::cout << "CHANGE STATE CALLED" << std::endl;
_state = std::make_unique<T> (this);
return static_cast<T*>(_state.get());
}
private:
std::unique_ptr<State> _state;
};
/* IMPLEMENTATIONS */
auto A::compute() -> void
{
std::cout << "State is A... datum is " << (datum - 1) << std::endl;
if (--datum <= 0) {
auto* s = _context->change_state<B>();
s->print_state_type ();
s->b_specific();
}
return;
}
auto B::compute() -> void
{
std::cout << "State is B... datum is " << (datum - 1) << std::endl;
if (--datum <= 0) {
auto* s = _context->change_state<A>();
s->print_state_type();
s->a_specific();
}
return;
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int main (int argc, char *argv[])
{
Context contoller;
for (int i = 1; i <= 50; ++i) {
contoller.request();
}
return 0;
}
#include <iostream>
class Player; // fwd declare.
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class State
{
public:
State (Player *owner) : Owner (owner) {}
virtual ~State() {};
virtual void Step() = 0;
protected:
Player *Owner = nullptr;
};
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class StandState : public State
{
public:
using State::State;
StandState (Player *owner) : State (owner), CountDown (5 + (rand() % 6))
{
std::cout << "STAND Ctor... countdown is " << CountDown << std::endl;
}
void Step() override;
private:
int CountDown;
};
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class JumpState : public State
{
public:
using State::State;
JumpState (Player *owner) : State (owner), CountDown (2 + (rand() % 3))
{
std::cout << "JUMP Ctor... countdown is " << CountDown << std::endl;
}
void Step() override;
private:
int CountDown;
};
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class Player
{
public:
Player() : CurrentState (new StandState (this))
{}
void Step()
{
CurrentState->Step();
}
template <typename T> void ChangeCurrentState()
{
std::cout << "CHANGE STATE CALLED" << std::endl;
delete CurrentState;
CurrentState = new T (this);
}
private:
State *CurrentState;
};
/* IMPLEMENTATIONS */
auto StandState::Step() -> void
{
std::cout << "STAND State... countdown is " << (CountDown - 1) << std::endl;
if (--CountDown <= 0)
Owner->ChangeCurrentState<JumpState>();
// Need to return at this point. State is now JumpState. Accessing member variables
// that belong to StandState at this point will cause crash (b/c 'ChangeCurrentState'
// called delete on 'CurrentState'). We are only still running inside StandState at
// this point because code and data reside in different areas in memory.
return;
}
auto JumpState::Step() -> void
{
std::cout << "JUMP State... countdown is " << (CountDown - 1) << std::endl;
if (--CountDown <= 0)
Owner->ChangeCurrentState<StandState>();
// Need to return at this point. State is now StandState. Accessing member variables
// that belong to JumpState at this point will cause crash (b/c 'ChangeCurrentState'
// called delete on 'CurrentState'). We are only still running inside JumpState at
// this point because code and data reside in different areas in memory.
return;
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int main (int argc, char *argv[])
{
Player *player = new Player();
for (int i = 1; i <= 50; ++i) {
player->Step();
}
delete player;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment