2

I'm trying to implement a State Pattern in C++, but have problems with the circular dependency. I have read other related material here - unfortunately it didn't help me. I don't have a lot of experience with C++, so bear with me. The following code is developed on a Ubuntu 10.10 machine in Eclipse Helios CDT:

ConcreteSystem.h

#ifndef CONCRETESYSTEM_H_
#define CONCRETESYSTEM_H_

class SystemState;

class ConcreteSystem {
public:
    ConcreteSystem();
    void SelfTestFailed();
    void Restart();
private:
    friend class SystemState;
    SystemState *currentState;
    void ChangeState(SystemState *state);
};

#endif /* CONCRETESYSTEM_H_ */

ConcreteSystem.cpp

#include "ConcreteSystem.h"
#include "SystemState.h"

ConcreteSystem::ConcreteSystem() {
    currentState = SelfTest::GetInstance();
}

void ConcreteSystem::SelfTestFailed() {
    currentState->SelfTestFailed(this);
}

void ConcreteSystem::Restart() {
    currentState->Restart(this);
}

void ConcreteSystem::ChangeState(SystemState *state){
    currentState = state;
}

SystemState.h

#ifndef SYSTEMSTATE_H_
#define SYSTEMSTATE_H_

class ConcreteSystem;

class SystemState {
public:
    virtual void Restart(ConcreteSystem *cs);
    virtual void SelfTestFailed(ConcreteSystem *cs);
protected:
    virtual void ChangeState(ConcreteSystem *cs, SystemState *state);
};

#endif /* SYSTEMSTATE_H_ */

SystemState.cpp

#include "SystemState.h"
#include "ConcreteSystem.h"

void SystemState::Restart(ConcreteSystem *cs) {

}

void SystemState::SelfTestFailed(ConcreteSystem *cs) {

}


void SystemState::ChangeState(ConcreteSystem *cs, SystemState *state) {
    cs->ChangeState(state);
}

SelfTest.h

#ifndef SELFTEST_H_
#define SELFTEST_H_

#include "SystemState.h"


class SelfTest : public SystemState {
public:
    SelfTest();
    void SelfTestFailed(ConcreteSystem* cs);
    static SystemState* GetInstance();
private:
    static SystemState* instance;
};

#endif /* SELFTEST_H_ */

SelfTest.cpp

#include "SelfTest.h"
#include "Failure.h"

SystemState* SelfTest::instance = 0;

SelfTest::SelfTest() {

}

void SelfTest::SelfTestFailed(ConcreteSystem *cs) {
    ChangeState(cs, Failure::GetInstance());
}

SystemState* SelfTest::GetInstance() {
    if (instance == 0) {
        instance = new SelfTest();
    }
    return instance;
}

Failure.h

#ifndef FAILURE_H_
#define FAILURE_H_

#include "SystemState.h"

class SelfTest;

class Failure : public SystemState {
public:
    Failure();
    void Restart(ConcreteSystem* t);
    static SystemState* GetInstance();
private:
    static SystemState* instance;
};

#endif /* FAILURE_H_ */

Failure.cpp

#include "Failure.h"
#include "SelfTest.h"

SystemState* Failure::instance = 0;

Failure::Failure() {

}

void Failure::Restart(ConcreteSystem* t) {
    ChangeState(t, SelfTest::GetInstance());
}

SystemState* Failure::GetInstance() {
    if (instance == 0) {
        instance = new Failure();
    }
    return instance;
}

I have problem with the includes, which gives me some weird compiler errors. Anyone with a good solution to this problem?

aagaard
  • 1,596
  • 2
  • 14
  • 28
  • If you have circular dependencies, you should learn about *forward declarations*. The chosen answer here (http://stackoverflow.com/questions/553682/when-to-use-forward-declaration) is an excellent writeup of the topic. – Emile Cormier Mar 04 '11 at 21:59
  • See also: http://stackoverflow.com/questions/183898/forward-referencing-or-declaration-in-c – Emile Cormier Mar 04 '11 at 22:02
  • You need to post the exact errors you get. Also, please try to post the *minimum* amount of source code that reproduces your problem. – Björn Pollex Mar 04 '11 at 22:55
  • I tried to take the minimum of code, but I think it is necessary have to whole pattern represented. – aagaard Mar 05 '11 at 08:37

1 Answers1

3

From the looks of the code you've posted, you'll have classes being redefined. Looking at your Failure.cpp file, you have:

#include "Failure.h"
#include "SelfTest.h"

Which will include both of those files, and each of those files include the SystemState.h file. Since the SystemState.h file is included more than once, it tries to redefine the SystemState class. At the top of each of your header files, you should do something like this:

// SystemState.h
#ifndef SystemState_h
#define SystemState_h
.. class definition ..
#endif // close the if statement from above.

As an aside on the design, I think it's bad form for the states to know about each other - use your ConcreteSystem as a state controller and then base the state on the return value of the last state operation.

Also, if you're relatively inexperienced with C++, I would recommend looking at this as a great source of learning material (in addition to StackOverflow, of course!).

spbots
  • 1,513
  • 1
  • 15
  • 22
  • 2
    The #ifndef / #define / #endif technique is called a *header guard*. – Emile Cormier Mar 04 '11 at 20:30
  • @Emile Cormier: Thanks, I couldn't remember the name – spbots Mar 04 '11 at 21:45
  • Thanks for the answer. I tried the header guard, but the problem lies the multi-inclusion to gain access to its other classes. My pattern is based on the GoF State Pattern, [like this website](http://www.codeproject.com/Articles/38962/State-Design-Pattern.aspx?msg=3160835), so I need to access them. – aagaard Mar 04 '11 at 21:45
  • Can you please post the compiler errors you're getting as part of your question? It will help everyone help you :) – spbots Mar 04 '11 at 21:51
  • I get an error in SystemState.h:15 ‘ConcreteSystem’ has not been declared. – aagaard Mar 04 '11 at 22:12
  • In the declaration of SystemState::Restart, you say `virtual void Restart(class ConcreteSystem* cs);` where you should really have `virtual void Restart(ConcreteSystem* cs);` – spbots Mar 04 '11 at 22:20
  • I have corrected that now. I also have edited post, to my current version, which gives two types of error. In the _Failure.cpp: undefined reference to Failure::instance_ in GetInstance() and in the SelfTest.cpp _undefined reference to SelfTest::instance_. – aagaard Mar 04 '11 at 22:50
  • You need to define the variables in your .cpp files. For example, in Failure.cpp, under your includes, put `SystemState* Failure::instance = 0;`. This will actually initialize the value to something. See http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 (found from the link in my answer). – spbots Mar 04 '11 at 23:03
  • Thanks for the answer, that was exactly the problem. I have updated the code with by last modifications, and it seems to work smoothly now! – aagaard Mar 05 '11 at 08:39
  • No problem! Since the code you've posted now works, you should probably close it up. – spbots Mar 05 '11 at 21:45