[statechart] Template States, Compile Error
The attached testcase doesn't compile with g++ 4.0.2 or g++ 3.2.3. The compiler generates a series of error messages like this: boost/statechart/simple_state.hpp:189: error: no type named 'inner_context_type' in 'struct Test::NormalMode<int>' I hastily sketched this out based on a larger code. If there are typos or other mistakes, let me know but the main problem I'm trying to solve is the "no type named" problem. I typedef it right in the template definition for NormalMode! This is using the current CVS for statechart, 1.33.1 for mpl. Is it possibly a compiler bug? Thanks! -Dave ---------------- Testcase #include <boost/statechart/state_machine.hpp> #include <boost/statechart/simple_state.hpp> #include <boost/statechart/event.hpp> #include <boost/statechart/custom_reaction.hpp> #include <boost/statechart/state.hpp> #include <boost/statechart/transition.hpp> #include <boost/mpl/list.hpp> #include <iostream> // Switch finite state machine for multiplexor schedule policy namespace Test { namespace sc = boost::statechart; namespace mpl = boost::mpl; // Inner states template <typename Type> struct On; template <typename Type> struct Off; template <typename Type> struct Error; // Outer states template <typename Type> struct NormalMode; template <typename Type> struct ErrorMode; // Events struct End : public sc::event<End> {}; struct UnconsumedEvent : public sc::event<UnconsumedEvent> { std::string st; UnconsumedEvent(const std::string &s) : st(s) {}; const std::string &state(void) const { return(st); }; }; template<typename Derived, typename Type> struct SwitchEvent : public sc::event<Derived> { typedef Type value_type; value_type ev; SwitchEvent(value_type e) : ev(e) {}; value_type value(void) const { return(ev); } }; template<typename Type> struct TurnOn : public SwitchEvent<TurnOn<Type>, Type> { TurnOn(Type value) : SwitchEvent<TurnOn<Type>, Type>(value) {}; }; template<typename Type> struct TurnOff : public SwitchEvent<TurnOff<Type>, Type> { TurnOff(Type value) : SwitchEvent<TurnOff<Type>, Type>(value) {}; }; template<typename Type> struct Switch; namespace detail { template<typename Type> struct typedefs { typedef Type value_type; typedef Switch<value_type> switch_context; typedef TurnOn<value_type> turn_on; typedef TurnOff<value_type> turn_off; typedef On<value_type> on; typedef Off<value_type> off; }; }; // The state machine template<typename Type> struct Switch : public sc::state_machine<Switch<Type>, NormalMode<Type> >, public detail::typedefs<Type> { typedef sc::state_machine<Switch<Type>, NormalMode<Type> > Base; typedef Switch<Type> This; std::string state(void) const { if (this->template state_cast<const typename This::on *>()) { return("ON"); } else if (this->template state_cast<const typename This::off *>()) { return("OFF"); } else { std::cerr << "Unknown state" << std::endl; std::abort(); return("ERROR"); } }; // Abort on unexpected events void unconsumed_event(const sc::event_base &event) { std::cerr << "Unexpected event in state " << state() << std::endl; std::abort(); this->post_event(new UnconsumedEvent(state())); }; }; // Define states // Outer template<typename Type> struct NormalMode : public sc::simple_state<NormalMode<Type>, Switch<Type>, Off<Type> >, public detail::typedefs<Type> { typedef sc::simple_state<NormalMode<Type>, Switch<Type>, Off<Type> > state_base; typedef typename state_base::inner_context_type inner_context_type; }; template<typename Type> struct ErrorMode : public sc::simple_state<ErrorMode<Type>, Switch<Type>, Error<Type> >, public detail::typedefs<Type> { typedef sc::simple_state<ErrorMode<Type>, Switch<Type>, Error<Type> > state_base; typedef typename state_base::inner_context_type inner_context_type; }; // Inner template<typename Type> struct Off : public sc::simple_state<Off<Type>, NormalMode<Type> >, public detail::typedefs<Type> { typedef Off<Type> This; typedef mpl::list< sc::custom_reaction<typename This::on>, sc::custom_reaction<End> > reactions; sc::result react(const typename This::on &) { this->template context<typename This::switch_context>().transit("OFF", "ON"); return(this->template transit<typename This::on>()); }; sc::result react(const End &) { this->terminate(); }; }; template<typename Type> struct On : public sc::simple_state<On<Type>, NormalMode<Type> >, public detail::typedefs<Type> { typedef On<Type> This; typedef mpl::list< sc::custom_reaction<typename This::off>, sc::custom_reaction<End> > reactions; sc::result react(const typename This::off &) { this->template context<typename This::switch_context>().transit("ON", "OFF"); return(this->template transit<typename This::off>()); }; sc::result react(const End &) { this->terminate(); }; }; template<typename Type> struct Error : sc::simple_state<Error<Type>, ErrorMode<Type> >, public detail::typedefs<Type> { }; }; int main(void) { Test::Switch<int> machine; machine.initiate(); machine.process_event( Test::TurnOn<int>(0) ); std::cout << "Now in state " << machine.state() << std::endl; machine.process_event( Test::TurnOff<int>(0) ); std::cout << "Now in state " << machine.state() << std::endl; machine.process_event( Test::TurnOff<int>(0) ); // Should produce error std::cout << "Now in state " << machine.state() << std::endl; machine.process_event( Test::End() ); }
Hi Dave
The attached testcase doesn't compile with g++ 4.0.2 or g++ 3.2.3. The compiler generates a series of error messages like this:
boost/statechart/simple_state.hpp:189: error: no type named 'inner_context_type' in 'struct Test::NormalMode<int>'
MSVC7.1 gives a similar diagnostic.
I hastily sketched this out based on a larger code. If there are typos or other mistakes, let me know but the main problem I'm trying to solve is the "no type named" problem. I typedef it right in the template definition for NormalMode!
Strange indeed. As you might have noticed, I'm using templated states and machines quite extensively in examples & tests. This should work in principle. I'll look into it.
This is using the current CVS for statechart, 1.33.1 for mpl.
Is it possibly a compiler bug?
I don't think so, see above. Later, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
Andreas Huber wrote:
Strange indeed. As you might have noticed, I'm using templated states and machines quite extensively in examples & tests. This should work in principle. I'll look into it.
Thanks. Could you keep me posted on what you find? Boost.Statechart is perfect for the project I'm working on right now and it would be a shame to have to drop it due to this issue. -Dave
David Greene wrote:
Andreas Huber wrote:
Strange indeed. As you might have noticed, I'm using templated states and machines quite extensively in examples & tests. This should work in principle. I'll look into it.
Thanks. Could you keep me posted on what you find?
Will do, I'm working on it right now. Later, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
It is getting late here in Switzerland. I've tried several things, nothing worked and I don't have much of a clue at the moment. The fact that two independent compilers choke on this pretty much rules out a compiler bug. I think a library bug isn't likely either as everything works when you make the innermost state non-templated. Moreover, the newest version of TransitionTest.cpp (checked in yesterday) seems to work perfectly on a number of platforms. That test does something very similar to what you try to do. I can't currently see any other explanation than that we must both be missing something. The attached program is what I got after throwing out all the unnecessary stuff. It compiles on MSVC, but as soon as you uncomment the commented parts it complains with "'inner_context_type' : is not a member of 'NormalMode<Type>'". Tomorrow I'll try to reduce it to something that hasn't anything to do with Boost.Statechart... #include <boost/statechart/state_machine.hpp> #include <boost/statechart/simple_state.hpp> namespace sc = boost::statechart; namespace mpl = boost::mpl; template< typename Type > struct NormalMode; struct Switch : sc::state_machine< Switch, NormalMode< int > > {}; /*template< typename Type >*/ struct Off; template< typename Type > struct NormalMode : sc::simple_state< NormalMode< Type >, Switch, Off/*< Type >*/ > { }; //template< typename Type > struct Off : sc::simple_state< Off/*< Type >*/, NormalMode< int > > { }; int main() { Switch machine; machine.initiate(); return 0; } -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
Andreas Huber wrote:
It is getting late here in Switzerland. I've tried several things, nothing worked and I don't have much of a clue at the moment. The fact that two independent compilers choke on this pretty much rules out a compiler bug. I think a library bug isn't likely either as everything works when you make the innermost state non-templated. Moreover, the newest version of TransitionTest.cpp (checked in yesterday) seems to work perfectly on a number of platforms. That test does something very similar to what you try to do. I can't currently see any other explanation than that we must both be missing something. The attached program is what I got after throwing out all the unnecessary stuff. It compiles on MSVC, but as soon as you uncomment the commented parts it complains with "'inner_context_type' : is not a member of 'NormalMode<Type>'". Tomorrow I'll try to reduce it to something that hasn't anything to do with Boost.Statechart...
Believe me, I share your pain, bewilderment and frustration. I wonder if anyone else on the Boost developers list or someone on comp.lang.c++.moderated would have any insight. -Dave
Hi Dave The problem you stumbled upon is (or was :-() well known. It was discussed during review and I even tried to find a workaround for it not too long ago. The solution is very simple and is even described in the reference manual under the requirements for the InnerInitial parameter: <quote> An mpl::list<> containing models of the SimpleState or State concepts or instantiations of the shallow_history or deep_history class templates. If there is only a single inner initial state _that_is_not_a_template_instantiation_ then it can also be passed directly, without wrapping it into an mpl::list<>. [...] </quote> (_Note_the_underlined_part_). So, the only thing you need to do is to wrap every templated InnerInitial parameter into an mpl::list and everything will work as expected (see attached example). The reason why compilers rightly choke on the code lies in the fact that simple_state internally checks whether the InnerInitial parameter already is an mpl::list. This check works perfectly even on forward-declared classes. However, as soon as InnerInitial is a class template instantiation, for some reason that template needs to be fully instantiated. The problem is described in more detail here: <http://article.gmane.org/gmane.comp.lib.boost.devel/128741> I guess it was simply a little too late yesterday, sorry... #include <boost/statechart/state_machine.hpp> #include <boost/statechart/simple_state.hpp> #include <boost/mpl/list.hpp> namespace sc = boost::statechart; namespace mpl = boost::mpl; template< typename Type > struct NormalMode; struct Switch : sc::state_machine< Switch, NormalMode< int > > {}; template< typename Type > struct Off; template< typename Type > struct NormalMode : sc::simple_state< // Note that Off< Type > is wrapped into an mpl::list NormalMode< Type >, Switch, mpl::list< Off< Type > > > { }; template< typename Type > struct Off : sc::simple_state< Off< Type >, NormalMode< Type > > { }; int main() { Switch machine; machine.initiate(); return 0; } HTH, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
Andreas Huber wrote:
(_Note_the_underlined_part_). So, the only thing you need to do is to wrap every templated InnerInitial parameter into an mpl::list and everything will work as expected (see attached example). The reason why
Aha, I see. Yes, that makes sense. Thanks for doing the digging. Perhaps this should be part of a Statechart FAQ? I certainly glossed right over the part of the manual that noted this. It's easy to miss a single phrase like that. -Dave
David Greene wrote:
Aha, I see. Yes, that makes sense. Thanks for doing the digging.
Perhaps this should be part of a Statechart FAQ?
Already on my to-do list :-)... Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
participants (3)
-
Andreas Huber
-
David A. Greene
-
David Greene