《C++11 模板超程式設計 - 構建DSL》
C++11 模板超程式設計 - 構建DSL
MagicBowen 0.562016.09.17 21:22:10字數 3,459閱讀 3,626
C++是一門非常適合用來構建DSL(Domain Specific Language)的語言,它的多正規化特點為它提供了豐富的工具,尤其是C++提供了:
- 一個靜態型別系統;
- 近似於零抽象懲罰的能力(包括強大的優化器);
- 預處理巨集,能夠以文字替換的方式操縱原始碼;
- 一套豐富的內建符號運算子,它們可以被過載,且對過載的語義幾乎沒有任何限制;
- 一套圖靈完備的模板計算系統(模板超程式設計),可以用於:
- 生成新的型別和函式;
- 在編譯期執行任何計算;
- 提供靜態反射的能力;
結合這些武器,使得通過C++構建兼顧語法表達力及執行時效率的DSL成為了可能。可以說,模板超程式設計是上述所有武器中最為重要的,接下來我們使用模板超程式設計設計一個用於描述有限狀態機(FSM)的DSL。該設計最初來自於《C++模板超程式設計》一書,由於作者在書中所舉狀態機的例子比較晦澀,而且實現使用了更晦澀的boost mpl庫,為了讓這個設計更加易懂並且程式碼更加清晰,我對例子和程式碼進行了重新設計。
有限狀態機(FSM)是計算機程式設計中非常有用的工具,它通過抽象將紊亂的程式邏輯轉換成更易於理解的形式化的表達形式。
有限狀態機的領域模型由三個簡單的元素構成:
-
狀態(state):FSM某一時刻總是處於一個狀態中,不同狀態決定了FSM可響應的事件型別以及響應的方式。
-
事件(event):事件觸發FSM狀態的改變,事件可以攜帶具體資訊。
-
轉換(transition):一個轉換標記了在某個事件的激勵下FSM從一個狀態到另一個狀態的躍遷。通常轉換還會有一個關聯動作(action),表示在狀態躍遷時進行的操作。將所有的轉換放在一起可以構成一個狀態轉換表(State Transition Table,STT)。
我們假設有一個跳舞機器人,它的狀態轉換關係如下圖:
圖可能是表示FSM最直觀的工具了,但是如果圖中出現太多的細節就會導致很凌亂,例如上圖為了簡潔就沒有標示每個轉換對應的action。為了讓FSM的表示更加形式化,我們將其裝換成如下表格的形式:
Current State | Event | Next State | Action |
---|---|---|---|
closed | open | opened | sayReady |
opened | close | closed | sayClosed |
opened | play | dancing | doDance |
dancing | stop | opened | sayStoped |
dancing | close | closed | sayClosed |
如上,對於跳舞機器人,它有三種狀態:closed,opened,dancing;它可以接收四種事件:close,open,play,stop;它有四個action:sayReady,sayClosed,doDance,sayStoped。上表中的每一行表示了一種可以進行的轉換關係。用表格來表示FSM同樣易於理解,而且這種表示是相對形式化的,且容易通過程式碼來描述。
對於這樣一個由FSM表示的跳舞機器人,最常見的實現如下:
// Events
struct Close {};
struct Open {};
struct Play
{
std::string name;
};
struct Stop {};
// FSM
struct DanceRobot
{
void processEvent(const Open& event)
{
if(state == closed)
{
sayReady(event);
state = opened;
}
else
{
reportError(event);
}
}
void processEvent(const Close& event)
{
if(state == opened)
{
sayClosed(event);
state = closed;
}
else if(state == dancing)
{
sayClosed(event);
state = closed;
}
else
{
reportError(event);
}
}
void processEvent(const Play& event)
{
if(state == opened)
{
doDance(event);
state = dancing;
}
else
{
reportError(event);
}
}
void processEvent(const Stop& event)
{
if(state == dancing)
{
sayStoped(event);
state = opened;
}
else
{
reportError(event);
}
}
private:
// Actions
void sayReady(const Open&)
{
std::cout << "Robot is ready for play!" << std::endl;
}
void sayClosed(const Close&)
{
std::cout << "Robot is closed!" << std::endl;
}
void sayStoped(const Stop&)
{
std::cout << "Robot stops playing!" << std::endl;
}
void doDance(const Play& playInfo)
{
std::cout << "Robot is dancing (" << playInfo.name << ") now!" << std::endl;
}
template<typename Event>
void reportError(Event& event)
{
std::cout << "Error: robot on state(" << state
<< ") receives unknown event( "
<< typeid(event).name() << " )" << std::endl;
}
private:
// States
enum