Почему мое использование context<State>(). Method() нарушает утверждение диаграммы состояний?
Я разработал некоторый концептуальный код для проекта, над которым я буду работать в ближайшее время. Проект поддается разработке конечного автомата, и я думаю, что boost::statechart будет работать хорошо. Однако я столкнулся с препятствиями, когда попытался использовать context(). Вот пример (я рад выложить больше кода, но я думаю, что это важная часть):
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
}
}
};
Состояние Active имеет методы, называемые "[over|under]flow_condition", которые просто возвращают true в этой точке. Помимо проблем с моим дизайном, я получаю следующую ошибку утверждения, когда создаю экземпляр таким образом:
int main(void) {
Throttler my_throttler;
my_throttler.initiate();
return 0;
}
и вот утверждение:
утверждение "get_pointer( stt.pContext_)!= 0" не выполнено
Я посмотрел это утверждение в файле "/usr/include/boost/statechart/simple_state.hpp", строка 689 (повышение 1.45), и в комментариях говорится, что оно предотвращает использование контекстов simple_state. Это озадачило меня, когда я вернулся к примеру с секундомером и увидел, что этот пример делает то, что я пытался сделать. Таким образом, я скомпилировал его, и это неудивительно, что это утверждение не нарушается кодом секундомера. Я что-то пропустил? Может быть, есть что-то еще в коде, который я пропустил? Вот весь заголовок (пожалуйста, помните, что это концептуальный код... Я не буду выпускать его на волю, пока он не будет полностью обобщен):
#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
1 ответ
Как вы отметили в своем комментарии, это связано с попыткой доступа к внешнему контексту из конструктора, что недопустимо для simple_state
,
От simple_state.hpp
:
// This assert fails when an attempt is made to access the state machine
// from a constructor of a state that is *not* a subtype of state<>.
// To correct this, derive from state<> instead of simple_state<>.
BOOST_ASSERT( get_pointer( pContext_ ) != 0 );
Таким образом, вы должны иметь возможность доступа к внешнему контексту из конструктора, если вы основываете свои состояния на state
класс (а не simple_state
).
Тем не менее, я не уверен, какое влияние это может оказать на ваши штаты. Если на этот вопрос есть ответ, он также может быть вам полезен (:
Из того, что я понимаю, вам нужно будет изменить Wait
извлечь из state
:
struct Wait : fsm::state< Wait, Active > {
а затем изменить Wait()
конструктор что-то вроде
typedef fsm::state< Wait, Active > my_base;
Wait( my_context ctx ) : my_base( ctx )
// and any other pre-constructor initialisation...
my_context
тип определяется (как защищенный) внутри state<>
и должен быть передан из конструктора производного класса.