For a task, I am planning to implement a state machine in C++ .
I am trying to keep a context object which provides following:
a state_ object to keep track of current state
a TransitionTo() method to facilitate transition to new state.
When I wrote the sample implementation and tested it out, I am facing double free error.
I needed help from the community in guiding me on what might be going wrong.
Thanks a lot.
#include <iostream>
#include <string>
class State;
/* Context class and method implementation */
class Context
{
State* state_;
public:
explicit Context(State* state);
void TransitionTo(State* newState);
};
Context::Context (State* state): state_ (nullptr)
{
this->TransitionTo(state);
}
void Context::TransitionTo(State* newState)
{
std::cout <<"Requesting state transition to " << newState->stateName<<"\n";
std::string previous_state_name = "None";
if (this->state_ != nullptr)
{
previous_state_name = this->state_->stateName;
delete this->state_;
}
this->state_ = newState;
std::cout << "State changed from "<< previous_state_name << " to "<< this->state_->stateName << "\n";
this->state_->set_context(this);
}
/* State class and method implementation */
class State
{
protected:
Context* context_;
public:
std::string stateName;
void set_context(Context* newContext);
virtual ~State();
};
State::~State()
{
std::cout << stateName <<" state deleted \n";
delete context_ ;
}
void State::set_context(Context *newContext)
{
this->context_ = newContext;
}
/* Declaring different states which are derived from State */
class HappyState : public State
{
public:
HappyState();
};
HappyState::HappyState()
{
stateName = "Happy";
}
class NeutralState : public State
{
public:
NeutralState();
};
NeutralState::NeutralState()
{
stateName = "Neutral";
}
class SadState : public State
{
public:
SadState();
};
SadState::SadState()
{
stateName = "Sad";
}
/* Test the implementation */
int main()
{
Context* ctx = new Context(( new NeutralState()));
ctx->TransitionTo(new HappyState());
ctx->TransitionTo(new SadState());
return 0;
}
When I run this code, I am getting following output:
Output snapshot
I found the mistake which was indeed a very silly one.
I was deleting the context_ in ~State() which deleted my original context.
Related
I want to make simple AI using FSM. When I simply make object it works perfectly fine but when I put them in vector it crashes after second update. Here is smallest code that shows this problem
class State
{
public:
virtual ~State() = default;
void setContext(Context* context);
virtual void processState() = 0;
protected:
Context* m_Context;
};
void State::setContext(Context* context)
{
m_Context = context;
}
class State1 : public State
{
public:
virtual void processState() override;
};
void State1::processState()
{
std::cout << "Processing State1" << std::endl;
m_Context->setState(new State2);
}
class State2 : public State
{
public:
virtual void processState() override;
};
void State2::processState()
{
std::cout << "Processing State2" << std::endl;
m_Context->setState(new State1);
}
class Context
{
public:
Context(State* state);
void update();
void setState(State* state);
private:
State* m_State;
};
Context::Context(State* state)
{
setState(state);
}
void Context::update()
{
m_State->processState();
}
void Context::setState(State* state)
{
delete m_State;
m_State = state;
m_State->setContext(this);
}
If I make it like this
Context context(new State1);
context.update();
context.update();
context.update();
context.update();
context.update();
it works and prints
Processing State1
Processing State2
Processing State1
Processing State2
Processing State1
but if I make it like this
std::vector<Context> contexts;
size_t amount = 1;
contexts.reserve(amount);
for (size_t i = 0; i < amount; i++)
{
Context context(new State1);
contexts.push_back(context);
}
for (size_t i = 0; i < amount; i++)
{
for (size_t j = 0; j < 5; j++)
{
contexts[i].update();
}
}
it only prints first state, crashes on second and gives this error code
Processing State1
-1073741819.
I tried to use break points but i still don't understand what's going on
You should consider using smart pointers or at least initiate the pointers to nullptr and check for this before calling delete.
The value of m_State is garbage, and in some cases you're calling delete on this unknown value - this results in an undefined behaviour and this is the reason that for some cases it works but for other it crashes.
I have the following code:
class ISubscriber;
class News {
public:
float getVersion() { return this->version; }
void setVersion(float state) { this->version= state; this->notifyAllSubscribers(); }
void attach(ISubscriber *observer) { this->subscribers.push_back(observer); }
void notifyAllSubscribers() {
for (vector<ISubscriber*>::iterator it = subscribers.begin(); it != subscribers.end(); it++){
(*(*it)).update();
}
}
private:
vector<ISubscriber*> subscribers;
float version;
};
class ISubscriber {
public:
News *news;
virtual void update() = 0;
};
class Subscriber1 : public ISubscriber {
public:
Subscriber1(News *news) { this->news = news; this->news->attach(this); }
void update() override { cout << "Subscriber1: A new version of the newspaper has been launched (v" << this->news->getVersion() << ")" << endl; }
};
class Subscriber2 : public ISubscriber {
public:
Subscriber2(News *news) { this->news = news; this->news->attach(this); }
void update() override { cout << "Subscriber2: A new version of the newspaper has been launched (v" << this->news->getVersion() << ")" << endl; }
};
int main(int argc, char *argv[]) {
News newspaper;
newspaper.setVersion(2.1f);
Subscriber1 sb1(&newspaper);
Subscriber2 sb2(&newspaper);
return 0;
}
But strange errors happened:
The first error points to this code (*(*it)).update(); in news class.
Why that errors happened, what's the reason?
(*(*it)).update(); requires the type ISubscriber to be complete, just the forward declaration is not enough.
You could move the defnition of ISubscriber before the one of News, and give a forward declaration of News before that.
class News;
class ISubscriber {
public:
News *news;
virtual void update() = 0;
};
class News {
public:
float getVersion() { return this->version; }
void setVersion(float state) { this->version= state; this->notifyAllSubscribers(); }
void attach(ISubscriber *observer) { this->subscribers.push_back(observer); }
void notifyAllSubscribers() {
for (vector<ISubscriber*>::iterator it = subscribers.begin(); it != subscribers.end(); it++){
(*(*it)).update();
}
}
private:
vector<ISubscriber*> subscribers;
float version;
};
Question in short:
class B has a ptr to class C, which has a class D having a ptr to class B
assign class B to an array in class A by copying, expecting to see to ptr points to new instance in the array not original instance, but failed.
I am already able to do some workaround, but I want to know why my original approach fails.
More detailed explainations are as follows, and the code to reproduce the problem is posted as well
Anyone who is able to explain what is going on is appreciated.
There are 6 classes:
class CastInfo //contains a Character*
class Skill //abstract class, contains CastInfo
class Movvement : public Skill
class Move1 : public Movement
class Character //contains a Movement*, which will be Move1* in practice
class Squad //contains an array of Character
with the following relationships:
Character* in CastInfo should point to the Character who owns the Skill which
is the owner of CastInfo
when assigning the Skill to Character, the Character* in CastInfo points to that Character
the Character in Squad's array should be copied, so there will be 2 instances and the Character* in CastInfo should also point to Character in Squad's array not original instance
The expecting result is:
move1 != ch1.move1 != squad.ch[0].move1 (this is already satisfied)
ch1.move1->cast_info.caster == &ch1 != squad.ch[0].move1->caster_info.caster (this is the problem)
There are 2 cases (tried) of the output:
In the Squad's constructor,:
if using
characters_[i] = characters[i];
the character is correctly copied, but the skill is at same address
move1: 00000270E6093500
ch1: 000000BC6DCFF378
ch1.move1: 00000270E6093E60
ch1.move1->cast_info.caster: 000000BC6DCFF378
squad.ch[0]: 000000BC6DCFF3E0
squad.ch[0].move1: 00000270E6093E60
squad.ch[0].move1->cast_info.caster: 000000BC6DCFF378
if using
characters_[i] = Character(characters[i]);
the character is correctly copied, but the skill is missing (pointing to some weird location)
move1: 00000230FDCEF080
ch1: 00000058A11DF548
ch1.move1: 00000230FDCEF260
ch1.move1->cast_info.caster: 00000058A11DF548
squad.ch[0]: 00000058A11DF5B0
squad.ch[0].move1: 00000230FDCEF0E0
squad.ch[0].move1->cast_info.caster: 00000058A11DF378
In the first case, I guess it is probably because I did not overload operator=, so only address is copied. I tried to overload it but it caused more problem. (Such as when using Builder.Build() )
In the second case, I expect it first call copy constructor, which triggers SetMove1(), which calls SetCaster(). move1 is cloned as shown, but I cannot understand why caster is not updated correctly. (Though is calls operator= after construnction, the address should remain the same.)
The following code should reproduce the problem:
motion.h
#pragma once
class Character;
struct CastInfo
{
Character* caster;
int coeff;
};
class Skill
{
public:
CastInfo cast_info;
Skill() {};
~Skill() {};
virtual void DoSomething() = 0;
};
class Movement : public Skill
{
public:
Movement();
~Movement();
virtual void DoSomething() { ; }
virtual Movement* Clone() const { return new Movement(*this); }
};
class Move1 : public Movement
{
public:
Move1() { cast_info.coeff = 123; }
void DoSomething() { ; }
virtual Move1* Clone() const { return new Move1(*this); }
};
class Move2 : public Movement
{
public:
void DoSomething() { ; }
};
motion.cpp:
#include "motion.h"
Movement::Movement() { }
Movement::~Movement() { }
test.h:
#pragma once
#include <string>
#include <vector>
#include "motion.h"
#define SQUAD_SIZE 6
extern Movement* null_movement;
class Character
{
public:
class Builder;
Character();
~Character();
Character(const Character& character);
Character& SetMove1(Movement* skill);
public:
int id_;
Movement* move1_ = null_movement;
Movement* move2_ = null_movement;
Character(int id) : id_(id) { ; }
void SetCaster();
};
class Character::Builder : public Character
{
public:
Builder& SetId(int i) { id_ = i; return *this; }
Character Build() { return Character(id_); }
};
class Squad
{
public:
class Builder;
Squad() { }
Squad(const Squad& squad);
~Squad() { }
public:
Character characters_[SQUAD_SIZE];
Squad(Character* characters);
};
class Squad::Builder :public Squad
{
public:
Builder& SetCharacter(const Character& character, const int position) { characters_[position] = character; return *this; }
Squad Build() { return Squad(characters_); }
};
test.cpp
#include <iostream>
#include "test.h"
Movement* null_movement = new Move2();
Character::Character() : id_(0) { }
Character::~Character() {}
Character::Character(const Character& character) {
id_ = character.id_;
SetMove1(character.move1_);
}
Character& Character::SetMove1(Movement* move1) {
if (!move1) return *this;
move1_ = move1->Clone();
SetCaster();
return *this;
}
void Character::SetCaster() {
if (move1_ != NULL) move1_->cast_info.caster = this;
}
Squad::Squad(const Squad& squad) {
*this = squad;
}
Squad::Squad(Character* characters) {
for (int i = 0; i < SQUAD_SIZE; i++) {
//characters_[i] = characters[i]; //character copied, skill same address
characters_[i] = Character(characters[i]); //character copied, skill missing
}
}
main.cpp
#include <iostream>
#include "test.h"
#include "motion.h"
int main() {
Move1* move1 = new Move1();
std::cout << "move1: " << move1 << std::endl;
Character ch1 = Character::Builder().SetId(1).Build();
Character ch2 = Character::Builder().SetId(2).Build();
ch1.SetMove1(move1);
std::cout << "ch1: " << &ch1 << std::endl;
std::cout << "ch1.move1: " << (ch1.move1_) << std::endl;
std::cout << "ch1.move1->cast_info.caster: " << (ch1.move1_->cast_info.caster) << std::endl;
Squad squad = Squad::Builder().SetCharacter(ch1, 0).SetCharacter(ch2, 1).Build();
std::cout << "squad.ch[0]: " << &(squad.characters_[0]) << std::endl;
std::cout << "squad.ch[0].move1: " << (squad.characters_[0].move1_) << std::endl;
std::cout << "squad.ch[0].move1->cast_info.caster: " << (squad.characters_[0].move1_->cast_info.caster) << std::endl;
system("PAUSE");
return 0;
}
As previously mentioned, I have a workaround to reach my goal:
By creating another method, which iterates through the array in Squad, and call each Character's SetCaster() method.
void Squad::SetCaster() {
for (int i = 0; i < SQUAD_SIZE; i++) {
characters_[i].SetCaster();
}
}
But I think this is dirty because every time after Builder::Builder(), SetCaster() must be called, which is unintuitive and error-prone.
I think I found the problem, as illustrated below:
The problem is in
Squad::Squad(Character* characters) {
for (int i = 0; i < SQUAD_SIZE; i++) {
//characters_[i] = characters[i]; //character copied, skill same address
characters_[i] = Character(characters[i]); //character copied, skill missing
}
}
As mentioned in question, using the commented line is just copying the values, which is incorrect.
What
characters_[i] = Character(characters[i]); //character copied, skill missing
does is as follows:
create a Character, by calling Character's constructer, this object is at address A. SetMove1() is called, SetCaster() is called. The pointer in cast_info is pointing at A correctly.
assign the object to characters_[i], whose address is at address B because the address of characters_ is assigned when Squad is created. As I did not overload Character::operator=, the pointer is still pointing to address A
Constructor done, Squad returned.
This is the reason
std::cout << "squad.ch[0].move1->cast_info.caster: " << (squad.characters_[0].move1_->cast_info.caster) << std::endl;
shows a third address (address A) which is neither &(character[0]) (address B) nor &ch1 (original character's address)
The solution is either to overload operator= or put my "workaround" (Squad::SetCaster()) in constructor right after the for loop.
Please correct me if there is anything wrong, or if there is any better solution.
I have a class called StateMachine, which controls all possible Entity States.
I then have a class called State which is a base class for unique Entity classes, e.g Attack State, Flee State.
When a new unique State is created (within the StateMachine), it passes in a StateMachine pointer, which will then be stored in the State base class, so that each unique State created can access its State Machine.
When I attempt to access the pointers members (using -> operator) it simple doesn't come up with any public methods, and I don't know why.
If anyone has any clue it would be greatly appreciated.
StateMachine.h
using STATE_PTR = std::shared_ptr<State>;
// Class to implement a finite state machine using the state desing pattern
class StateMachine
{
public:
StateMachine();
~StateMachine();
void OnEnter(STATE_NAME sn);
void OnExit();
void OnEvent(STATE_SYMBOL & ss);
void OnTick(float st);
void ChangeState(STATE_NAME const & sn);
void RegisterState(ENTITY_CLASS const & ec);
typedef std::map<STATE_NAME, STATE_PTR> STATE_REGISTRY;
private:
STATE_REGISTRY state_registry;
STATE_NAME current_state;
};
StateMachine.cpp
using namespace::std;
StateMachine::StateMachine()
: state_registry()
{
current_state = STATE_NAME::UNKNOWN;
}
StateMachine::~StateMachine()
{
state_registry.clear();
}
void StateMachine::OnEnter(STATE_NAME sn)
{
current_state = sn;
if (state_registry[current_state] != nullptr)
{
state_registry[current_state]->OnEnter();
}
}
void StateMachine::OnExit()
{
if (state_registry[current_state] != nullptr)
{
state_registry[current_state]->OnExit();
}
}
void StateMachine::OnTick(float st)
{
}
void StateMachine::OnEvent(STATE_SYMBOL & ss)
{
state_registry[current_state]->OnEvent(ss);
}
void StateMachine::RegisterState(ENTITY_CLASS const & ec)
{
switch (ec)
{
case ENTITY_CLASS::PLAYER_TANK :
state_registry.insert(std::make_pair(STATE_NAME::STATE_1, std::make_shared<PlayerTankState1>(this)));
state_registry.insert(std::make_pair(STATE_NAME::STATE_2, std::make_shared<PlayerTankState2>(this)));
break;
case ENTITY_CLASS::ENEMY_TANK :
state_registry.insert(std::make_pair(STATE_NAME::STATE_3, std::make_shared<EnemyTankState1>(this)));
state_registry.insert(std::make_pair(STATE_NAME::STATE_4, std::make_shared<EnemyTankState2>(this)));
state_registry.insert(std::make_pair(STATE_NAME::STATE_5, std::make_shared<EnemyTankState3>(this)));
break;
default:
break;
}
}
void StateMachine::ChangeState(STATE_NAME const & sn)
{
state_registry[current_state]->OnExit();
current_state = sn;
state_registry[current_state]->OnEnter();
}
State.h
class StateMachine; // Forward decloration of the StateMachine class
// Base class for all states of the game system
class State
{
protected:
State(StateMachine * p)
: mp_Owner(p)
{}
public:
virtual ~State() {}
virtual void OnEnter() = 0;
virtual void OnExit() = 0;
virtual void OnTick(float) = 0;
virtual void OnEvent(STATE_SYMBOL) = 0;
StateMachine * mp_Owner;
};
EnemyTankState.cpp (Unique State)
EnemyTankState1::EnemyTankState1(StateMachine * p)
: State(p)
{
}
EnemyTankState1::~EnemyTankState1()
{
}
void EnemyTankState1::OnEnter()
{
cout << "Hi From Enemy Tank: Partolling State" << endl;
}
void EnemyTankState1::OnExit()
{
cout << "Bye From Enemy Enemy Tank: Partolling State" << endl;
}
void EnemyTankState1::OnTick(float dt)
{
}
void EnemyTankState1::OnEvent(STATE_SYMBOL ss)
{
switch (ss)
{
// Takes Enemy Tank to Attacking State
case STATE_SYMBOL::SYMBOL_2 :
mp_Owner->
break;
}
}
Within the code sample above, the line mp_Owner-> is what is giving me grief, as it is not opening up a list of public methods as you would expect when using a class pointer.
Any help would be much appreciated. Sorry for the long chunks of code, I couldn't think of any other way of getting my problem across.
I'm making a state machine for switching game states (playing->menu->setup) in my game engine, but I'm getting a segmentation fault. I can change the game state fine from upper level, but how can I change the game state from within a game state?
Here's a minimal code example:
#include <iostream>
#include <vector>
class GameStateManager;
class GameState {
public:
GameState(GameStateManager* StateManager) {
StateManager = stateManager;
};
virtual ~GameState();
virtual void update() = 0;
GameStateManager* stateManager;
};
class GameStateManager {
public:
GameStateManager();
~GameStateManager();
void changeGameState(GameState* state) {
if(!running) {
running = true;
}
// Cleanup the current state
if(!gameStates.empty()) {
for(unsigned int i = 0; i < gameStates.size(); i++) {
delete gameStates[i];
}
gameStates.clear();
std::cout << "Cleaning up GameState" << std::endl;
}
// Store and initialize the new game state
gameStates.push_back(state);
};
void update() {
if(!gameStates.empty()) {
gameStates.back()->update();
}
};
std::vector<GameState*> gameStates;
bool running;
};
class PlayState : public GameState {
public:
PlayState(GameStateManager* stateManager) : GameState(stateManager) {};
~PlayState();
void update() override {
// On some flag, initiate the next level
nextLevel();
};
void nextLevel() {
stateManager->changeGameState(new PlayState(stateManager));
};
};
int main() {
GameStateManager stateManager;
stateManager.changeGameState(new PlayState(&stateManager));
while(stateManager.running) {
for(unsigned int i = 0; i < 10000; i++) {
std::cout << "Round: " << i << std::endl;
// Segmentation fault here
stateManager.update();
// This works
//stateManager.changeGameState(new PlayState(&stateManager));
}
stateManager.running = false;
}
return 0;
}
for some reason the stateManager is not being set in the GameState constructor.
so try this:
class GameState {
public:
GameState(GameStateManager* StateManager) : stateManager(StateManager)
{
};
virtual ~GameState();
virtual void update() = 0;
GameStateManager* stateManager;
};