SHARE
TWEET

To michimikochi

a guest Jan 27th, 2016 43 in 16 days
  1. /* michimikochiさんへ(4)C++ バージョン(遷移ID追加)
  2.  
  3.  
  4. 【お知らせ】
  5. ・前回からまたあちこち変更しています。
  6.  
  7. 【修正内容】
  8. 2016.01.27:
  9. 遷移ID追加。
  10. 状態一覧の遷移表示をIDのみに変更。
  11. MACHINEクラスのインスタンスをローカル変数に変更。
  12. コード推考
  13.  
  14. 2016.01.26:
  15. output_machine機能追加。
  16. コード推敲
  17.  
  18.  
  19. 質問URL
  20. http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q12155160442
  21. http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q12155022762
  22. ------------------------------------------------------------
  23. 【入力テキストの条件】
  24.  
  25. 状態名、イベント名、[ガード条件]、/アクション
  26. にはスペースを含む事はできません。
  27.  
  28. イベント名、[ガード条件]、/アクション は1つの遷移に対して各1つずつしか指定できません。
  29.  
  30.  
  31. ------------------------------------------------------------
  32. 【メモ】
  33.  [*] が開始状態 or 終了状態を表す
  34.  [*] が含まれているとPlantUMLが状態マシン図と判断する
  35.  
  36.  
  37. イベント  遷移が起こるきっかけとなる出来事
  38. ガード条件  遷移が許される条件。
  39. アクション  遷移が起きた時に実行される操作
  40. 各要素は省略することができます。
  41. ただし、省略しているとイベントかアクションか区別がつかないので、
  42. "/" を残すことがあります。
  43.  
  44.    イベント名 [ガード条件] /アクション
  45. 状態A-------------------→状態B
  46.  
  47. ------------------------------------------------------------
  48. -----stm M1.txt の中身-----
  49. @startuml{stm_M1.png}
  50.  
  51. [*] -> S1
  52. S1 -> S2 : a
  53. S2 -> S1 : [b==0]
  54. S2 -> S3
  55. S3 --> [*]
  56.  
  57. @enduml
  58.  
  59. -----実行結果-----
  60. ----- プログラムで生成した stm M1-2.txt の中身 -----
  61. @startuml{stm_M1.png}
  62.  
  63. [*] -> S1
  64. S1 -> S2 : a
  65. S2 -> S1 : [b==0]
  66. S2 -> S3
  67. S3 --> [*]
  68.  
  69. @enduml
  70.  
  71. ----- コンソールの表示 -----
  72. -----
  73. 遷移元=初期状態
  74. 遷移矢印=->
  75. 遷移先=S1
  76. -----
  77. 遷移元=S1
  78. 遷移矢印=->
  79. 遷移先=S2
  80. コロン:
  81. イベント名=a
  82. -----
  83. 遷移元=S2
  84. 遷移矢印=->
  85. 遷移先=S1
  86. コロン:
  87. ガード=[b==0]
  88. -----
  89. 遷移元=S2
  90. 遷移矢印=->
  91. 遷移先=S3
  92. -----
  93. 遷移元=S3
  94. 遷移矢印=-->
  95. 遷移先=終了状態
  96. ----------遷移一覧----------
  97. {id=T1,src=初期状態,arw=->,tgt=S1,evt=,grd=,act=}
  98. {id=T2,src=S1,arw=->,tgt=S2,evt=a,grd=,act=}
  99. {id=T3,src=S2,arw=->,tgt=S1,evt=,grd=[b==0],act=}
  100. {id=T4,src=S2,arw=->,tgt=S3,evt=,grd=,act=}
  101. {id=T5,src=S3,arw=-->,tgt=終了状態,evt=,grd=,act=}
  102. ----------状態一覧----------
  103. {id=初期状態,src={},tgt={T1}}
  104. {id=S1,src={T1,T3},tgt={T2}}
  105. {id=S2,src={T2},tgt={T3,T4}}
  106. {id=S3,src={T4},tgt={T5}}
  107. {id=終了状態,src={T5},tgt={}}
  108. 続行するには何かキーを押してください . . .
  109.  
  110.  
  111.  */
  112.  
  113. #include <iostream>
  114. #include <fstream>
  115. #include <sstream>
  116. #include <vector>
  117. #include <string>
  118.  
  119. using namespace std;
  120.  
  121. //デフォルトの「コピーコンストラクタ」と「代入演算子」を禁止するマクロ
  122. //※これを private: エリアに置くことで上記を禁止に出来る
  123. //※禁止する理由は、デフォルトのコピーコンストラクタと代入演算子は、
  124. // memcpy()のようにメモリを単純コピーするだけなので、
  125. // 動的メモリを管理するメンバがいる場合、メモリリーク等を引き起こす危険があるため
  126. #define DISARROW_COPY_AND_ASSGIN(ClassName) \
  127.     ClassName(const ClassName&); \
  128.     void operator=(const ClassName&);
  129.  
  130. class TRANSITION;//構造体の前方宣言(構造体の名前だけコンパイラに教える)
  131.  
  132. //遷移動的配列文字列化関数のプロトタイプ宣言
  133. string TransVectorToString(const vector<TRANSITION*> &tpv,const string &separator);
  134. string TransVectorToIdString(const vector<TRANSITION*> &tpv,const string &separator);
  135.  
  136. //------------------------------------------------------------
  137. //状態を管理するクラス
  138. class STATE {
  139. private:
  140.     //デフォルトの「コピーコンストラクタ」と「代入演算子」を禁止するマクロ
  141.     DISARROW_COPY_AND_ASSGIN(STATE);
  142.  
  143. public:
  144.     string id;//状態の名前
  145.     vector<TRANSITION*> src;//受けている遷移ポインタ動的配列
  146.     vector<TRANSITION*> tgt;//出ている遷移ポインタ動的配列
  147.  
  148.     //------------------------------
  149.     //コンストラクタ(指定文字列でIDを初期化する)
  150.     STATE(const string &arg_id):id(arg_id){}
  151.  
  152.     //------------------------------
  153.     //デストラクタ(破棄される時に実行される。デバッグ用)
  154.     ~STATE(){
  155.         cout << "~STATE()\n";
  156.     }
  157.  
  158.     //------------------------------
  159.     //STATEの表示用文字列を返す
  160.     string ToString()const{
  161.         ostringstream os;//文字を溜め込むことが出来る
  162.  
  163.         os << "{id=" << id << ","
  164.             "src={" << TransVectorToIdString(src,",") << "},"
  165.             "tgt={" << TransVectorToIdString(tgt,",") << "}}";
  166.  
  167.         return os.str();//溜め込んだ文字を返す
  168.     }
  169.  
  170. };
  171.  
  172. //------------------------------------------------------------
  173. //遷移情報を管理するクラス
  174. class TRANSITION {
  175. private:
  176.     //デフォルトの「コピーコンストラクタ」と「代入演算子」を禁止するマクロ
  177.     DISARROW_COPY_AND_ASSGIN(TRANSITION);
  178.  
  179. public:
  180.  
  181.     string id;//遷移の名前
  182.     STATE *src;//遷移元の状態ポインタ
  183.     string arw;//★矢印(追加しました)
  184.     STATE *tgt;//遷移先の状態ポインタ
  185.     string evt;//イベント
  186.     string grd;//ガード
  187.     string act;//アクション
  188.  
  189.     //------------------------------
  190.     //コンストラクタ(特に何もしない)
  191.     TRANSITION(){}
  192.  
  193.     //------------------------------
  194.     //デストラクタ(デバッグ用)
  195.     ~TRANSITION(){
  196.         cout << "~TRANSITION()\n";
  197.     }
  198.  
  199.     //------------------------------
  200.     //TRANSITIONの表示用文字列を返す
  201.     string ToString()const{
  202.         ostringstream os;//文字を溜め込むことが出来る
  203.  
  204.         os << "{id="<<id<<",src="<<src->id<<",arw="<<arw<<",tgt="<<tgt->id
  205.             <<",evt="<<evt<<",grd="<<grd<<",act="<<act<<"}";
  206.  
  207.         return os.str();//溜め込んだ文字を返す
  208.     }
  209.  
  210. };
  211.  
  212.  
  213. //------------------------------------------------------------
  214. //クラス(状態と遷移の動的メモリを管理する)
  215. class MACHINE{
  216. private://-----プライベート・メンバ--------------------
  217.  
  218.     //デフォルトの「コピーコンストラクタ」と「代入演算子」を禁止するマクロ
  219.     DISARROW_COPY_AND_ASSGIN(MACHINE);
  220.  
  221.     //-----プライベート・メンバ変数-----
  222.     vector<STATE *> mStatePtrVector;//状態一覧動的配列(st)
  223.     vector<TRANSITION *> mTransPtrVector;//遷移一覧動的配列(tr)
  224.     STATE *init;//初期状態へのポインタ
  225.     string mFileHeader;//ファイルのヘッダ?の行
  226.     int mNextTransIdNumber;
  227.  
  228.  
  229.     //------------------------------
  230.     //指定IDの状態ポインタを取得する(無い時、DBに登録される。ポインタをDB以外でdeleteしてはいけない)
  231.     STATE *GetState(const string &id)
  232.     {
  233.         for(size_t i=0;i<mStatePtrVector.size();i++){
  234.             if(mStatePtrVector[i]->id == id){
  235.                 //既に同じ状態名が存在する時
  236.                 return mStatePtrVector[i];//状態のポインタを返す
  237.             }
  238.         }
  239.  
  240.         //同じ状態名が存在しなかった時
  241.         STATE *sp = new STATE(id);//指定IDを持つ状態を生成する
  242.         mStatePtrVector.push_back(sp);//動的配列に登録する
  243.  
  244.         if(sp->id == "初期状態"){
  245.             //初期状態を返すタイミングでinitに記憶する
  246.             init = sp;
  247.         }
  248.         return sp;//状態のポインタを返す
  249.     }
  250.  
  251.     //------------------------------
  252.     //遷移を生成登録し、そのポインタを取得する(必ずDBに登録される)
  253.     TRANSITION *AddTrans(STATE *srcState,const string &arrow,STATE *targetState,
  254.                             const string &event,const string &guard,const string &action){
  255.  
  256.         TRANSITION *tp = new TRANSITION();//遷移メモリを生成する
  257.  
  258.         //遷移にデータを書き込む
  259.         tp->id = NewTransID();
  260.         tp->src = srcState;
  261.         tp->arw = arrow;
  262.         tp->tgt = targetState;
  263.         tp->evt = event;
  264.         tp->grd = guard;
  265.         tp->act = action;
  266.  
  267.         mTransPtrVector.push_back(tp);//DBの動的配列に登録する
  268.  
  269.         return tp;//作成した遷移のポインタを返す
  270.     }
  271.  
  272.     string NewTransID(){
  273.         ostringstream os;
  274.         os << "T" << ++mNextTransIdNumber;
  275.         return os.str();
  276.     }
  277.  
  278. public://-----公開メンバ--------------------
  279.     //------------------------------
  280.     //コンストラクタ
  281.     MACHINE(){
  282.         init = NULL;
  283.         mNextTransIdNumber=0;
  284.     }
  285.  
  286.     //------------------------------
  287.     //デストラクタ(MACHINEが破棄されるタイミングで呼び出される)
  288.     ~MACHINE(){
  289.         //状態データを破棄する
  290.         for(size_t i=0;i<mStatePtrVector.size();i++){
  291.             delete mStatePtrVector[i];//new で作成したメモリを解放する
  292.         }
  293.         mStatePtrVector.clear();//状態一覧動的配列を空にする
  294.         init = NULL;//★初期状態へのポインタを無効にする
  295.  
  296.         //遷移データを破棄する
  297.         for(size_t i=0;i<mTransPtrVector.size();i++){
  298.             delete mTransPtrVector[i];//new で作成したメモリを解放する
  299.         }
  300.         mTransPtrVector.clear();//遷移一覧動的配列を空にする
  301.     }
  302.  
  303.  
  304.     //------------------------------
  305.     //作成した全状態情報を表示する
  306.     void PrintStateALL(void)
  307.     {
  308.         printf("----------状態一覧----------\n");
  309.         for(size_t i=0;i<mStatePtrVector.size();i++){
  310.             cout << mStatePtrVector[i]->ToString() << endl;
  311.         }
  312.     }
  313.  
  314.     //------------------------------
  315.     //作成した全遷移を表示する
  316.     void PrintTransALL(void)
  317.     {
  318.         printf("----------遷移一覧----------\n");
  319.         cout << TransVectorToString(mTransPtrVector,"\n") << endl;
  320.  
  321.     }
  322.  
  323.     //------------------------------
  324.     //指定ファイルのPlantUML情報を読み込みます。
  325.     void input_machine(string file_name)//MACHINEのメンバ関数にしました
  326.     {
  327.         //-----ファイル内容の例-----
  328.         //startuml{stm_M1.png}
  329.         //
  330.         //[*] -> S1
  331.         //S1 -> S2 : a
  332.         //S2 -> S1 : [b==0]
  333.         //S2 -> S3
  334.         //S3 --> [*]
  335.         //
  336.         //@enduml
  337.  
  338.         ifstream ifs(file_name.c_str());//入力ファイルストリーム
  339.  
  340.         if(ifs.fail()){
  341.             //読み込みファイルのオープンに失敗した時
  342.             cerr << "Input file(" << file_name << ") open error.\n";
  343.             return;
  344.         }
  345.  
  346.         for(string buff;getline(ifs,buff);){
  347.             //ファイルから行の読み込みに成功している間(buffには改行コードは読込まれない)
  348.  
  349.             if(buff.length() == 0)continue;//1文字も無い時、次の行へ
  350.  
  351.  
  352.             if(buff[0] == '@'){//先頭が@の時
  353.                 const string headerStr = "@startuml";
  354.                 if(buff.substr(0,headerStr.length()) == headerStr){
  355.                     //読込んだ行の先頭部分がヘッダだった時
  356.                     mFileHeader = buff;//ファイルヘッダ行を保存
  357.                 }
  358.                 continue;//次の行へ
  359.             }
  360.  
  361.             printf("-----\n");
  362.             //行の先頭が@および改行以外の時(遷移情報の時)
  363.  
  364.             //遷移元、矢印、遷移先、コロンを読み込む
  365.             string src,arrow,target,colon,event,guard,action;
  366.             istringstream iss(buff);//読込んだ1行を入力文字列ストリームにする
  367.             iss >> src >> arrow >> target >> colon;
  368.  
  369.             if(src=="[*]")src="初期状態";//初期状態の名称変更処理
  370.             if(target=="[*]")target="終了状態";//終了状態の名称変更処理
  371.  
  372.             cout << "遷移元="<<src<<endl;
  373.             cout << "遷移矢印="<<arrow<<endl;
  374.             cout << "遷移先="<<target<<endl;
  375.  
  376.             if(colon == ":"){
  377.                 //コロンがあった時
  378.                 cout << "コロン"<<colon<<endl;
  379.  
  380.                 //イベント名、[ガード条件]、/アクション があれば読み込む
  381.                 int k;
  382.                 for(k=0;k<3;k++){
  383.                     string p;
  384.                     iss >> p;
  385.                     if(p.length()==0)break;//何も読み込めなかった時
  386.  
  387.                     switch(p[0]){
  388.                     case '[':// [ガード条件]
  389.                         guard = p;
  390.                         cout<<"ガード="<<guard<<endl;
  391.                         break;
  392.                     case '/':// /アクション
  393.                         action = p;
  394.                         cout<<"アクション="<<action<<endl;
  395.                         break;
  396.                     default://イベント名
  397.                         event = p;
  398.                         cout<<"イベント名="<<event<<endl;
  399.                         break;
  400.                     }
  401.                 }
  402.             }
  403.  
  404.  
  405.             //-----読込データを元に「状態」と「遷移」を作成する-----
  406.  
  407.             //指定IDの状態ポインタを取得する(無い時、DBに登録される)
  408.             STATE *srcState = GetState(src);
  409.  
  410.             //指定ID状態ポインタを取得する(無い時、DBに登録される)
  411.             STATE *targetState = GetState(target);
  412.  
  413.             //遷移を生成登録し、そのポインタを取得する(必ずDBに登録される)
  414.             TRANSITION *tp = AddTrans(srcState,arrow,targetState,event,guard,action);
  415.  
  416.             //状態に遷移を追加する
  417.             //遷移元状態には遷移をターゲット遷移として登録
  418.             srcState->tgt.push_back(tp);
  419.  
  420.             //遷移先状態には遷移をソース遷移として登録
  421.             targetState->src.push_back(tp);
  422.  
  423.         }
  424.  
  425.     }
  426.  
  427.     //------------------------------
  428.     //指定ファイルにPlantUMLテキストを書き込みます
  429.     void output_machine(string file_name){
  430.         ofstream ofs(file_name.c_str());
  431.  
  432.         if(ofs.fail()){
  433.             //書込みファイルのオープンに失敗した時
  434.             cerr << "Output file(" << file_name << ") open error.\n";
  435.             return;
  436.         }
  437.  
  438.         ofs << mFileHeader << endl;
  439.         ofs << endl;
  440.        
  441.         for(size_t i=0;i<mTransPtrVector.size();i++){
  442.             //遷移の数だけ
  443.             const TRANSITION *trans = mTransPtrVector[i];
  444.  
  445.             ofs << (trans->src->id == "初期状態" ? (string)"[*]" : trans->src->id);
  446.             ofs << " " << trans->arw;
  447.             ofs << " " << (trans->tgt->id == "終了状態" ? (string)"[*]" : trans->tgt->id);
  448.  
  449.             //イベント・ガード・アクションの文字列を作る
  450.             string evtGrdActStr;
  451.             if(trans->evt.length()) evtGrdActStr += " " + trans->evt;
  452.             if(trans->grd.length()) evtGrdActStr += " " + trans->grd;
  453.             if(trans->act.length()) evtGrdActStr += " " + trans->act;
  454.             if(evtGrdActStr.length()){
  455.                 //イベント・ガード・アクション文字列がある時
  456.                 //コロンに続いて、イベント・ガード・アクション文字列を追加
  457.                 ofs << " :" << evtGrdActStr;
  458.             }
  459.             ofs << endl;//改行
  460.         }
  461.  
  462.         ofs << endl;//改行のみ
  463.         ofs << "@enduml\n";//
  464.     }
  465.  
  466.  
  467. };
  468.  
  469. //------------------------------------------------------------
  470. //遷移の動的配列をstringに変換する
  471. string TransVectorToString(const vector<TRANSITION*> &tpv,const string &separator)
  472. {
  473.     ostringstream os;//文字を溜め込むことが出来る
  474.  
  475.     for(size_t i=0;i<tpv.size();i++){
  476.         if(i)os << separator;//初回以外は、セパレータを挿入する
  477.         os << tpv[i]->ToString();
  478.     }
  479.  
  480.     return os.str();//溜め込んだ文字を返す
  481. }
  482.  
  483. //------------------------------------------------------------
  484. //遷移の動的配列をstringに変換する
  485. string TransVectorToIdString(const vector<TRANSITION*> &tpv,const string &separator)
  486. {
  487.     ostringstream os;//文字を溜め込むことが出来る
  488.  
  489.     for(size_t i=0;i<tpv.size();i++){
  490.         if(i)os << separator;//初回以外は、セパレータを挿入する
  491.         os << tpv[i]->id;
  492.     }
  493.  
  494.     return os.str();//溜め込んだ文字を返す
  495. }
  496.  
  497. //------------------------------------------------------------
  498. int main(void){
  499.  
  500.     MACHINE Machine;//データベースクラスのインスタンス(実体=オブジェクト=メモリ)
  501.  
  502.     Machine.input_machine("stm M1.txt");
  503.     Machine.output_machine("stm M1-2.txt");
  504.  
  505.     Machine.PrintTransALL();
  506.     Machine.PrintStateALL();
  507.  
  508.     system("pause");
  509.  
  510.     return 0;
  511. }
RAW Paste Data
Top