C++ Boost

Boost Statechart 庫

常見問題(FAQs)


What's so cool about state-local storage? 狀態局部存儲有什麼好處?
How can I hide the inner workings of a state machine from its clients? 如何向客戶隱藏狀態機的內部工作?
Is it possible to inherit from a given state machine and modify its layout in the subclass? 有可能從給定狀態機進行派生並修改子類的佈局嗎?
What about UML2.0 features? UML2.0的特性如何?
Why do I get an assert when I access the state machine from a state destructor? 為什麼我從 state 的析構函數中訪問狀態機時會得到一個斷言?
Is Boost.Statechart suitable for embedded applications? Boost.Statechart 適合於嵌入式應用嗎?
Is your library suitable for applications with hard real-time requirements? 你的庫適合於有嚴格實時性要求的應用嗎?
With templated states I get an error that 'inner_context_type' is not defined. What's wrong? 對於模板化狀態,我得到一個'inner_context_type'未定義的錯誤,有什麼問題?
My compiler reports an error in the library code. Is this a bug in Boost.Statechart? 我的編譯器對庫代碼報告了一個錯誤,是不是 Boost.Statechart 有缺陷?
Is it possible to disable history for a state at runtime? 可以在運行期關閉一個狀態的歷史嗎?
How can I compile a state machine into a dynamic link library (DLL)? 如何將狀態機編譯為動態鏈接庫(DLL)?
Does Boost.Statechart support polymorphic events? Boost.Statechart 是否支持多態事件?
Why are exit-actions called in the wrong order when I use multiple inheritance? 為什麼在使用多重繼承時,對退出動作的調用順序有錯?

What's so cool about state-local storage? 狀態局部存儲有什麼好處?

This is best explained with an example:
以下例子是最好的解釋:

struct Active;
struct Stopped;
struct Running;
struct StopWatch : sc::state_machine< StopWatch, Active >
{
// startTime_ remains uninitialized, because there is no reasonable default
// startTime_ 保留為未初始化,因為沒有合理的缺省值
StopWatch() : elapsedTime_( 0.0 ) {}
~StopWatch()
{
terminate();
}

double ElapsedTime() const
{
// Ugly switch over the current state. 一段難看的對當前狀態的switch。
if ( state_cast< const Stopped * >() != 0 )
{
return elapsedTime_;
}
else if ( state_cast< const Running * >() != 0 )
{
return elapsedTime_ + std::difftime( std::time( 0 ), startTime_ );
}
else // we're terminated 我們已被終止了
{
throw std::bad_cast();
}
}

// elapsedTime_ is only meaningful when the machine is not terminated
// elapsedTime_ 僅當狀態機未被終止時有意義
double elapsedTime_;
// startTime_ is only meaningful when the machine is in Running
// startTime_ 僅當狀態機運行時有意義
std::time_t startTime_;
};

struct Active : sc::state< Active, StopWatch, Stopped >
{
typedef sc::transition< EvReset, Active > reactions;

Active( my_context ctx ) : my_base( ctx )
{
outermost_context().elapsedTime_ = 0.0;
}
};

struct Running : sc::state< Running, Active >
{
typedef sc::transition< EvStartStop, Stopped > reactions;

Running( my_context ctx ) : my_base( ctx )
{
outermost_context().startTime_ = std::time( 0 );
}

~Running()
{
outermost_context().elapsedTime_ +=
std::difftime( std::time( 0 ), outermost_context().startTime_ );
}
};

struct Stopped : sc::simple_state< Stopped, Active >
{
typedef sc::transition< EvStartStop, Running > reactions;
};

This StopWatch does not make any use of state-local storage while implementing the same behavior as the tutorial StopWatch. Even though this code is probably easier to read for the untrained eye, it does have a few problems that are absent in the original:
這個 StopWatch 沒有使用狀態局部存儲,實現了與 "指南"中的 StopWatch 相同的行為。雖然這段代碼可能對於未經訓練的人而言更易讀,但是它確實存在一些原來的例子沒有的問題:

Both points are not much of a problem in a small example like this, which can easily be implemented in a single translation unit by a single programmer. However, they quickly become a major problem for a big complex machine spread over multiple translation units, which are possibly even maintained by different programmers.
在一個像這個例子的小程序中,這兩點都不是什麼大問題,因為小程序可以很容易由單個程序員在單個編譯單元中實現。但是,對於較大的,被分開多個編譯單元的 複雜狀態機,它們很快就會成為大問題,因為程序可能要由不同的程序員來維護。

How can I hide the inner workings of a state machine from its clients? 如何向客戶隱藏狀態機的內部工作?

To see why and how this is possible it is important to recall the following facts:
先來看看為何以及如何這是可能的,我們重溫一下以下兩個事實:

The class template member function state_machine<>::initiate() creates an object of the initial state. So, the definition of this state must be known before the compiler reaches the point where initiate() is called. To be able to hide the initial state of a state machine in a .cpp file we must therefore no longer let clients call initiate(). Instead, we do so in the .cpp file, at a point where the full definition of the initial state is known.
類模板成員函數 state_machine<>::initiate() 會創建一個初始狀態的對象。因此,該狀態的定義必須在編譯器到達調用 initiate() 的地方之前完成。為了能夠將狀態機的初始狀態隱藏在一個 .cpp 文件中,我們必須不能讓客戶調用 initiate(). 相反,我們在 .cpp 文件中來調用它,調用的位置應該在初始狀態被完全定義的地方。

Example:
例子:

StopWatch.hpp:

// define events ... 定義各個事件 ...

struct Active; // the only visible forward 只是一個前向聲明
struct StopWatch : sc::state_machine< StopWatch, Active >
{
StopWatch();
};

StopWatch.cpp:

struct Stopped;
struct Active : sc::simple_state< Active, StopWatch, Stopped >
{
typedef sc::transition< EvReset, Active > reactions;
};

struct Running : sc::simple_state< Running, Active >
{
typedef sc::transition< EvStartStop, Stopped > reactions;
};

struct Stopped : sc::simple_state< Stopped, Active >
{
typedef sc::transition< EvStartStop, Running > reactions;
};

StopWatch::StopWatch()
{
// For example, we might want to ensure that the state
// machine is already started after construction.
// Alternatively, we could add our own initiate() function
// to StopWatch and call the base class initiate() in the
// implementation. 例如,我們可能想要確保狀態機在構造之後就開始運行。
// 另一方面,我們也可以為 StopWatch 增加我們自己的 initiate() 函數,
// 並在其中調用基類的 initiate()
initiate();
}

The PingPong example demonstrates how the inner workings of an asynchronous_state_machine<> subclass can be hidden.
例子 PingPong 示範了一個 asynchronous_state_machine<> 子類的內部工作是如何被隱藏的。

Is it possible to inherit from a given state machine and modify its layout in the subclass? 有可能從給定狀態機進行派生並修改子類的佈局嗎?

Yes, but contrary to what some FSM code generators allow, Boost.Statechart machines can do so only in a way that was foreseen by the designer of the base state machine:
是的,但是與一些FSM代碼生成器所允許的相反,Boost.Statechart 狀態機只允許按基類狀態機的設計者所預見的方式來這樣做:

struct EvStart : sc::event< EvStart > {};

struct Idle;
struct PumpBase : sc::state_machine< PumpBase, Idle >
{
virtual sc::result react(
Idle & idle, const EvStart & ) const;
};

struct Idle : sc::simple_state< Idle, PumpBase >
{
typedef sc::custom_reaction< EvStart > reactions;

sc::result react( const EvStart & evt )
{
return context< PumpBase >().react( *this, evt );
}
};
struct Running : sc::simple_state< Running, PumpBase > {};

sc::result PumpBase::react(
Idle & idle, const EvStart & ) const
{
return idle.transit< Running >();
}


struct MyRunning : sc::simple_state< MyRunning, PumpBase > {};

struct MyPump : PumpBase
{
virtual sc::result react(
Idle & idle, const EvStart & ) const
{
return idle.transit< MyRunning >();
}
};

What about UML 2.0 features? UML2.0的特性如何?

The library was designed before 2.0 came along. Therefore, if not explicitly noted otherwise, the library implements the behavior mandated by the UML1.5 standard. Here's an incomplete list of differences between the 2.0 semantics & Boost.Statechart semantics:
這個庫是在UML2.0出來之前設計的,如果沒有明顯的說明,本庫是按照UML1.5的標準來實現的。以下是關於2.0語義與 Boost.Statechart 語義之間的區別的一個不完整的列表:

Why do I get an assert when I access the state machine from a state destructor? 為什麼我從 state 的析構函數中訪問狀態機時會得到一個斷言?

When compiled with NDEBUG undefined, running the following program results in a failed assert:
如果編譯時沒有定義 NDEBUG,運行以下程序會得到一個失敗斷言:

#include <boost/statechart/state_machine.hpp>
#include <boost/statechart/simple_state.hpp>
#include <iostream>

struct Initial;
struct Machine : boost::statechart::state_machine< Machine, Initial >
{
Machine() { someMember_ = 42; }
int someMember_;
};

struct Initial : boost::statechart::simple_state< Initial, Machine >
{
~Initial() { std::cout << outermost_context().someMember_; }
};

int main()
{
Machine().initiate();
return 0;
}

The problem arises because state_machine<>::~state_machine inevitably destructs all remaining active states. At this time, Machine::~Machine has already been run, making it illegal to access any of the Machine members. This problem can be avoided by defining the following destructor:
這個問題是因為 state_machine<>::~state_machine 不可避免要析構所有剩下的活動狀態。這時,Machine::~Machine 已經運行了,訪問 Machine 的任何成員都是非法的。這個問題可以通過如下定義析構函數來避免:

~Machine() { terminate(); }

Is Boost.Statechart suitable for embedded applications? 適合於嵌入式應用嗎?

It depends. As explained under Speed versus scalability tradeoffs on the Performance page, the virtually limitless scalability offered by this library does have its price. Especially small and simple FSMs can easily be implemented so that they consume fewer cycles and less memory and occupy less code space in the executable. Here are some obviously very rough estimates:
這要視具體情況而定。正如"性能"一篇中的 速度與可擴 展性的權衡 所解釋的,本庫所說的無限擴展性實際上是有代價的。較小的和簡單的FSM可以很容易實現,所以它們只消耗較少的CPU週期和內存,在可執行代碼中所佔的代 碼空間也較少。以下顯然是一些很粗略的 估算:

As mentioned above, these are very rough estimates derived from the use of the library on a desktop PC, so they should only be used to decide whether there is a point in making your own performance tests on your target platform.
以上描述只是從桌面PC上的使用所得到的粗略評估,所以它們只能用於決定是否有必要在你的目標平台上進行你自己的性能測試。

Is your library suitable for applications with hard real-time requirements? 你的庫適合於有嚴格實時性要求的應用嗎?

Yes. Out of the box, the only operations taking potentially non-deterministic time that the library performs are calls to std::allocator<> member functions and dynamic_casts. std::allocator<> member function calls can be avoided by passing a custom allocator to event<>, state_machine<>, asynchronous_state_machine<>, fifo_scheduler<> and fifo_worker<>. dynamic_casts can be avoided by not calling the state_cast<> member functions of state_machine<>, simple_state<> and state<> but using the deterministic variant state_downcast<> instead.
是的。在交付時,本庫中唯一不能確定執行時間的操作只有對 std::allocator<> 成員函數和 dynamic_casts 的調用。對 std::allocator<> 成員函數的調用可以通過向 event<>, state_machine<>, asynchronous_state_machine<>, fifo_scheduler<>fifo_worker<> 傳入一個定制的分配器來避免。而 dynamic_casts 則可以通過不要調用 state_machine<>, simple_state<>state<>state_cast<> 成員函數來避免,可以使用更具確定性的 state_downcast<> 來代替。

With templated states I get an error that 'inner_context_type' is not defined. What's wrong? 對於模板化狀態,我得到一個'inner_context_type'未定義的錯誤,有什麼問題?

The following code generates such an error:
以下代碼會就會生成這個錯誤:

#include <boost/statechart/state_machine.hpp>
#include <boost/statechart/simple_state.hpp>

namespace sc = boost::statechart;

template< typename X > struct A;
struct Machine : sc::state_machine< Machine, A< int > > {};

template< typename X > struct B;
template< typename X >
struct A : sc::simple_state< A< X >, Machine, B< X > > {};

template< typename X >
struct B : sc::simple_state< B< X >, A< X > > {};

int main()
{
Machine machine;
machine.initiate();
return 0;
}

If the templates A and B are replaced with normal types, the above code compiles without errors. This is rooted in the fact that C++ treats forward-declared templates differently than forward-declared types. Namely, the compiler tries to access member typedefs of B< X > at a point where the template has not yet been defined. Luckily, this can easily be avoided by putting all inner initial state arguments in an mpl::list<>, as follows:
如果用普通的類型來替代模板 AB, 以上代碼可以通過編譯。其根源是,C++將前向聲明的模板與前向聲明的類型視為不同。即,編譯器會在模板尚未定義之時就試圖訪問 B< X > 的成員 typedef。幸好,這很容易避免,只要將所有內層初始狀態參數放入一個 mpl::list<> 即可,如下:

struct A : sc::simple_state<
A< X >, Machine, mpl::list< B< X > > > {};

See this post for technical details.
有關技術細節請見 這 張帖子

My compiler reports an error in the library code. Is this a bug in Boost.Statechart? 我的編譯器對庫代碼報告了一個錯誤,是不是 Boost.Statechart 有缺陷?

Probably not. There are several possible reasons for such compile-time errors:
可能不是。對於這類編譯期錯誤有以下幾個可能的原因:

  1. Your compiler is too buggy to compile the library, see here for information on the status of your compiler. If you absolutely must use such a compiler for your project, I'm afraid Boost.Statechart is not for you.
    你的編譯器對於編譯本庫而言具有太多缺陷,有關你的編譯器的狀態信息請見 這裡。如果你絕對必須在你的項 目中使用這個編譯器,我恐怕 Boost.Statechart 就不能適合你了。
  2. The error is reported on a line similar to the following:
    錯誤報告類似於下面這行信息:
    BOOST_STATIC_ASSERT( ( mpl::less<
    orthogonal_position,
    typename context_type::no_of_orthogonal_regions >::value ) );
    Most probably, there is an error in your code. The library has many such compile-time assertions to ensure that invalid state machines cannot be compiled (for an idea what kinds of errors are reported at compile time, see the compile-fail tests). Above each of these assertions there is a comment explaining the problem. On almost all current compilers an error in template code is accompanied by the current "instantiation stack". Very much like the call stack you see in the debugger, this "instantiation stack" allows you to trace the error back through instantiations of library code until you hit the line of your code that causes the problem. As an example, here's the MSVC7.1 error message for the code in InconsistentHistoryTest1.cpp:
    很可能是你的代碼有錯誤。本庫中有許多這樣的編譯期斷言來確保無效的狀態機不能通過編譯(要瞭解哪些類型的錯誤會在編譯期報告,請見"編譯失敗"的測 試)。以上這些斷言每個都帶有註釋以解釋有什麼問題。當前差不多所有編譯器對於模板代碼的錯誤都是以這種"實例化棧"的方式顯示的。它們非常類似於你在調 試器中見到的調用棧,"實例化棧"讓你可以通過庫代碼的實例化來跟蹤錯誤,直到你找到引起問題的代碼行。例如,以下是 InconsistentHistoryTest1.cpp 的代碼在 MSVC7.1 下的錯誤信息:
    ...\boost\statechart\shallow_history.hpp(34) : error C2027: use of undefined type 'boost::STATIC_ASSERTION_FAILURE<x>'
    with
    [
    x=false
    ]
    ...\boost\statechart\shallow_history.hpp(34) : see reference to class template instantiation 'boost::STATIC_ASSERTION_FAILURE<x>' being compiled
    with
    [
    x=false
    ]
    ...\boost\statechart\simple_state.hpp(861) : see reference to class template instantiation 'boost::statechart::shallow_history<DefaultState>' being compiled
    with
    [
    DefaultState=B
    ]
    ...\boost\statechart\simple_state.hpp(599) : see reference to function template instantiation 'void boost::statechart::simple_state<MostDerived,Context,InnerInitial>::deep_construct_inner_impl_non_empty::deep_construct_inner_impl<InnerList>(const boost::statechart::simple_state<MostDerived,Context,InnerInitial>::inner_context_ptr_type &,boost::statechart::simple_state<MostDerived,Context,InnerInitial>::outermost_context_base_type &)' being compiled
    with
    [
    MostDerived=A,
    Context=InconsistentHistoryTest,
    InnerInitial=boost::mpl::list<boost::statechart::shallow_history<B>>,
    InnerList=boost::statechart::simple_state<A,InconsistentHistoryTest,boost::mpl::list<boost::statechart::shallow_history<B>>>::inner_initial_list
    ]
    ...\boost\statechart\simple_state.hpp(567) : see reference to function template instantiation 'void boost::statechart::simple_state<MostDerived,Context,InnerInitial>::deep_construct_inner<boost::statechart::simple_state<MostDerived,Context,InnerInitial>::inner_initial_list>(const boost::statechart::simple_state<MostDerived,Context,InnerInitial>::inner_context_ptr_type &,boost::statechart::simple_state<MostDerived,Context,InnerInitial>::outermost_context_base_type &)' being compiled
    with
    [
    MostDerived=A,
    Context=InconsistentHistoryTest,
    InnerInitial=boost::mpl::list<boost::statechart::shallow_history<B>>
    ]
    ...\boost\statechart\simple_state.hpp(563) : while compiling class-template member function 'void boost::statechart::simple_state<MostDerived,Context,InnerInitial>::deep_construct(const boost::statechart::simple_state<MostDerived,Context,InnerInitial>::context_ptr_type & ,boost::statechart::simple_state<MostDerived,Context,InnerInitial>::outermost_context_base_type &)'
    with
    [
    MostDerived=A,
    Context=InconsistentHistoryTest,
    InnerInitial=boost::mpl::list<boost::statechart::shallow_history<B>>
    ]
    ...\libs\statechart\test\InconsistentHistoryTest1.cpp(29) : see reference to class template instantiation 'boost::statechart::simple_state<MostDerived,Context,InnerInitial>' being compiled
    with
    [
    MostDerived=A,
    Context=InconsistentHistoryTest,
    InnerInitial=boost::mpl::list<boost::statechart::shallow_history<B>>
    ]
    Depending on the IDE you use, it is possible that you need to switch to another window to see this full error message (e.g. for Visual Studio 2003, you need to switch to the Output window). Starting at the top and going down the list of instantiations you see that each of them is accompanied by a file name and a line number. Ignoring all files belonging to the library, we find the culprit close to the bottom in file InconsistentHistoryTest1.cpp on line 29.
    取決於你使用的IDE,你可能需要切換到另一個窗口才能看到完整的錯誤信息(如,對於 Visual Studio 2003, 你需要切換到 Output窗口)。從最上面開始,向下查看實例化列表,你可以看到每一個都是帶有文件名和行號。忽略所有屬於庫的文件,我們在接近底部找到了問題所在, InconsistentHistoryTest1.cpp 第29行。
  3. The error is reported on a line nowhere near a BOOST_STATIC_ASSERT. Use the technique described under point 2 to see what line of your code causes the problem. If your code is correct then you've found a bug in either the compiler or Boost.Statechart. Please send me a small but complete program showing the problem. Thank you!
    錯誤報告與 BOOST_STATIC_ASSERT 相差很遠。請使用前面第2點所介紹的技術,找到你的代碼中哪一行引發了問題。如果你的代碼是正確的,那麼你就是發現了編譯器或 Boost.Statechart 的一個缺陷。請 發給我們 一個出現該問題的,短小但完整的程序。謝謝你!

Is it possible to disable history for a state at runtime? 可以在運行期關閉一個狀態的歷史嗎?

Yes, see simple_state::clear_shallow_history() and simple_state::clear_deep_history(). Calling these functions is often preferable to introducting additional normal transitions when ...
可以,請見 simple_state::clear_shallow_history()simple_state::clear_deep_history(). 在以下情況下,調用這些函數通常比引入額外的普通轉換更好:

How can I compile a state machine into a dynamic link library (DLL)? 如何將狀態機編譯為動態鏈接庫(DLL)?

Invisible to the user, the library uses static data members to implement its own speed-optimized RTTI-mechanism for event<> and simple_state<> subtypes. Whenever such a subtype is defined in a header file and then included in multiple TUs, the linker later needs to eliminate the duplicate definitions of static data members. This usually works flawlessly as long as all these TUs are statically linked into the same binary. It is a lot more complex when DLLs are involved. The TuTest*.?pp files illustrate this:
本庫使用了靜態數據成員來為 event<>simple_state<> 的子類型實現了自己的速度優化的RTTI機制,這一點對於用戶是透明的。只要這樣的子類型是定義在一個頭文件中且被多個TUs所包含,鏈接器就需要消除這 些靜態數據成員的重複定義。只要所有TUs是靜態鏈 接到同一個二進制文件中,這是沒有問題的。但如果引入了DLLs,就會有一點複雜。TuTest*.?pp 文件示範了這一情形:

Without any precautions (e.g. __declspec(dllexport) on MSVC compatible compilers), on most platforms both binaries (exe & dll) now contain their own instance of the static data member. Since the RTTI mechanism assumes that there is exactly one object of that member at runtime, the mechanism fails spectacularly when the process running the exe also loads the dll. Different platforms deal differently with this problem:
沒有任何預警(如,在MSVC兼容的編譯器中的 __declspec(dllexport)), 在多數平台上,這兩個二進制文件(exe & dll)都各自包含了該靜態數據成員的實例。由於RTTI機制假定在運行時該成員只有一個對象,所以在運行exe文件且裝入dll文件時,這個機制顯然會 失效。不同的平台處理該問題的方法有所不同:

Does Boost.Statechart support polymorphic events? Boost.Statechart 是否支持多態事件?

No. Although events can be derived from each other to write common code only once, reactions can only be defined for most-derived events.
不。雖然事件可以相互派生,共同的代碼只須編寫一次,但是 反 應 只能為最底層的事件進行定義。

Example:
例子:

template< class MostDerived >
struct EvButtonPressed : sc::event< MostDerived >
{
// common code 公共代碼
};

struct EvPlayButtonPressed :
EvButtonPressed< EvPlayButtonPressed > {};
struct EvStopButtonPressed :
EvButtonPressed< EvStopButtonPressed > {};
struct EvForwardButtonPressed :
EvButtonPressed< EvForwardButtonPressed > {};

/* ... */

// We want to turn the player on, no matter what button we
// press in the Off state. Although we can write the reaction
// code only once, we must mention all most-derived events in
// the reaction list. 我們希望在 Off 狀態下,無論按哪個鍵都可以打開播放器。
// 雖然我們可以只寫一次反應代碼,但是我們必須在反應列表中提及所有最底層的事件。
struct Off : sc::simple_state< Off, Mp3Player >
{
typedef mpl::list<
sc::custom_reaction< EvPlayButtonPressed >,
sc::custom_reaction< EvStopButtonPressed >,
sc::custom_reaction< EvForwardButtonPressed >
> reactions;

template< class MostDerived >
sc::result react( const EvButtonPressed< MostDerived > & )
{
// ...
}
};

Why are exit-actions called in the wrong order when I use multiple inheritance? 為什麼在使用多重繼承時,對退出動作的調用順序有錯?

Update: The implementation has changed considerably in this area. It is still possible to get this behavior under rare circumstances (when an action propagates an exception in a state machine with orthogonal regions and if the statechart layout satisfies certain conditions), but it can no longer be demonstrated with the example program below. However, the described workaround is still valid and ensures that this behavior will never show up.
更新:在這一方面,庫的實現已經有了相當大的變化。這一現象在極少數的情況下(在一個帶有正交區域的狀態機中,當 一個動作拋出異常時,如果 狀態圖的佈局滿足特定的條件)仍然可能發生,但是已不再是以下例子所示範的那樣。不過,下面所介紹的解決方法依然有效,可以確保這一行為不會出現。

They definitely aren't for the simple_state<> and state<> subtypes, but the destructors of additional bases might be called in construction order (rather than the reverse construction order):
它們不是 simple_state<>state<> 的子類,但是這些額外基類的析構函數可能會以構造的順序調用(而不是以與構造順序相反的順序):

#include <boost/statechart/state_machine.hpp>
#include <boost/statechart/simple_state.hpp>

namespace sc = boost::statechart;

class EntryExitDisplayer
{
protected:
EntryExitDisplayer( const char * pName ) :
pName_( pName )
{
std::cout << pName_ << " entered\n";
}

~EntryExitDisplayer()
{
std::cout << pName_ << " exited\n";
}

private:
const char * const pName_;
};

struct Outer;
struct Machine : sc::state_machine< Machine, Outer > {};
struct Inner;
struct Outer : EntryExitDisplayer, sc::simple_state<
Outer, Machine, Inner >
{
Outer() : EntryExitDisplayer( "Outer" ) {}
};

struct Inner : EntryExitDisplayer,
sc::simple_state< Inner, Outer >
{
Inner() : EntryExitDisplayer( "Inner" ) {}
};

int main()
{
Machine myMachine;
myMachine.initiate();
return 0;
}

This program will produce the following output:
這個程序將產生以下輸出:

Outer entered
Inner entered
Outer exited
Inner exited

That is, the EntryExitDisplayer base class portion of Outer is destructed before the one of Inner although Inner::~Inner() is called before Outer::~Outer(). This somewhat counter-intuitive behavior is caused by the following facts:
即,OuterEntryExitDisplayer 基類部分Inner 的基類部分之前被析構,雖然 Inner::~Inner()Outer::~Outer() 之前被調用。這個有點違反直覺的行為是由以下事實引起的:

So, when the Outer destructor is called the call stack looks as follows:
所以,在調用 Outer 析構函數時,調用棧應該是這樣的:

Outer::~Outer()
simple_state< Inner, ... >::~simple_state()
Inner::~Inner()

Note that Inner::~Inner() did not yet have a chance to destroy its EntryExitDisplayer base class portion, as it first has to call the destructor of the second base class. Now Outer::~Outer() will first destruct its simple_state< Outer, ... > base class portion and then do the same with its EntryExitDisplayer base class portion. The stack then unwinds back to Inner::~Inner(), which can then finally finish by calling EntryExitDisplayer::~EntryExitDisplayer().
注意,Inner::~Inner() 還沒有機會銷毀它的 EntryExitDisplayer 基類部分,它必須首先調用第二個基 類的析構函數。現在 Outer::~Outer() 將首先析構它的 simple_state< Outer, ... > 基類部分,然後析構它的 EntryExitDisplayer 基類部分。然後調用棧展開回 Inner::~Inner(), 它將調用 EntryExitDisplayer::~EntryExitDisplayer() 完成最後的任務。

Luckily, there is an easy work-around: Always let simple_state<> and state<> be the first base class of a state. This ensures that destructors of additional bases are called before recursion employed by state base destructors can alter the order of destruction.
幸好,有一個很容易的解決方法:總是把 simple_state<>state<> 作為一個狀態的第一個基類。這樣可以確保其它基類的析構函數在遞歸調用可能影響析構順序的狀態基類的析構函數之前被調用。


Valid HTML 4.01 Transitional

Revised 05 January, 2008

Copyright c 2003-2007 Andreas Huber Donni

Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)