I've developed some concept code for a project that I will be working on shortly. The project lends itself to a state machine design and I think boost::statechart will do a good job. I hit a roadblock when I tried to use context() however. Here's a sample (I'm happy to put more code up, but I think this is the relevant part):
struct Wait : fsm::simple_state< Wait, Active > {
typedef mpl::list<fsm::transition< UnderflowEvent, Exec> > reactions;
public:
Wait()
: m_wait_op() {
std::cout << "entering wait state." << std::endl;
wait();
}
~Wait() { std::cout << "exiting wait state." << std::endl; }
private:
default_wait m_wait_op;
fsm::result wait() {
if(context<Active>().underflow_condition()) {
m_wait_op();
return transit<Wait>();
}
else if(context<Active>().overflow_condition()) {
return transit<Exec>();
}
else {
// undefined - keep waiting
}
}
};
The state Active has methods called "[over|under]flow_condition" which just return true at this point. Problems with my design aside, I am getting the following assertion failure when I instantiate thusly:
int main(void) {
Throttler my_throttler;
my_throttler.initiate();
return 0;
}
and here's the assertion:
assertion "get_pointer( stt.pContext_ ) != 0" failed
I looked this assertion up in file "/usr/include/boost/statechart/simple_state.hpp", line 689 (boost 1.45) and the comments say that it is there to prevent simple_state from using contexts. This puzzled me when I revisited the stopwatch example and saw that the example was doing the very thing I was trying to do. So I compiled it and this assertion is not violated by the stopwatch code unsurprisingly. Am I missing something? Maybe there's something elsewhere in the code that I missed? Here's the entire header (please remember it's concept code... I'm not releasing this into the wild until it's been thoroughly genericized):
#ifndef _THROTTLER_H_
#define _THROTTLER_H_
#include<queue>
#include<vector>
#include<ctime>
#include<boost/statechart/event.hpp>
#include<boost/statechart/transition.hpp>
#include<boost/statechart/state_machine.hpp>
#include<boost/statechart/simple_state.hpp>
#include<boost/mpl/list.hpp>
#include<iostream>
namespace mpl = boost::mpl;
namespace fsm = boost::statechart;
namespace {
unsigned int DEFAULT_WAIT_TIME(1000);
}
struct noop {
public:
noop() { m_priority = (1 << sizeof(int)); }
noop(unsigned int priority) { m_priority = priority; }
virtual ~noop() {}
bool operator()(void) {
return true;
}
friend bool operator<(noop a, noop b);
private:
unsigned int m_priority;
};
bool operator<(noop a, noop b) {
return a.m_priority < b.m_priority;
}
struct compare_noops {
bool operator()(noop a, noop b) {
}
};
struct default_wait {
void operator()(unsigned long msecs = DEFAULT_WAIT_TIME) {
std::clock_t endtime =
std::clock() + (msecs*1000*CLOCKS_PER_SEC);
while(clock() < endtime);
}
};
struct OverflowEvent : fsm::event< OverflowEvent > {};
struct UnderflowEvent : fsm::event< UnderflowEvent > {};
struct ResetEvent : fsm::event< ResetEvent > {};
struct Active;
struct Throttler : fsm::state_machine< Throttler, Active > {};
struct Wait;
struct Active : fsm::simple_state< Active, Throttler, Wait > {
public:
typedef mpl::list<fsm::transition< ResetEvent, Active> > reactions;
bool overflow_condition(void) { return true; }
bool underflow_condition(void) { return true; }
void queue_operation(noop op) {
m_operation_queue.push(op);
}
void perform_operation(void) {
noop op(m_operation_queue.top());
if(op())
m_operation_queue.pop();
else
throw;
}
private:
std::priority_queue<noop, std::vector<noop>, compare_noops > m_operation_queue;
private:
std::priority_queue<noop, std::vector<noop>, compare_noops > m_operation_queue;
};
struct Exec : fsm::simple_state< Exec, Active > {
typedef mpl::list<fsm::transition< OverflowEvent, Wait> > reactions;
Exec() { std::cout << "entering exec state." << std::endl; }
~Exec() { std::cout << "exiting exec state." << std::endl; }
};
struct Wait : fsm::simple_state< Wait, Active > {
typedef mpl::list<fsm::transition< UnderflowEvent, Exec> > reactions;
public:
Wait()
: m_wait_op() {
std::cout << "entering wait state." << std::endl;
wait();
}
~Wait() { std::cout << "exiting wait state." << std::endl; }
private:
default_wait m_wait_op;
fsm::result wait() {
if(context<Active>().underflow_condition()) {
m_wait_op();
return transit<Wait>();
}
else if(context<Active>().overflow_condition()) {
return transit<Exec>();
}
else {
// undefined - keep waiting
}
}
};
#endif
As you've noted in your comment, it's related to attempting to access the outer context from within the constructor, which is not allowed for a
simple_state
.From
simple_state.hpp
:So you should be able to access the outer context from the constructor if you base your states on the
state
class (rather than asimple_state
).That said, I'm not sure what impacts or implications this may have for your states. If this question gets an answer it may be helpful to you as well (:
From what I understand, you'll need to change
Wait
to derive fromstate
:and then change the
Wait()
constructor to something likeThe
my_context
type is defined (as protected) withinstate<>
, and needs to be passed in from the derived class's constructor.