Several CASE tools provides the capability to generate C++ code based upon a state diagram of a switch as shown below. There are four sets of code generation scripts with a simple state diagram consisting of an OffState and OnState with the events turnOn and turnOff. The events have a parameter int aNumber to show the code generation for event parameters. The class diagram of the generated C++ code is provided. In these examples, the there is a true or false condition. In these examples a simple action is shown, e.g. cout << "Turned On";
| State Name | Event Name | Transition Condition | Transition Action | Transition Next State |
| OffState | turnOn(int aNumber) | condition == "true" | cout << "Turned On"; | OnState |
| OffState | turnOn(int aNumber) | condition == "false" | cout << "False Condition - No TurnOn"; | OffState |
| OnState | turnOff() | 1 - Default condition for code generation | cout << "Turned Off"; | Termination |
The following are the steps to create your first state diagram
and generate C++ code from the state diagram:
Step 1. Create the directory, firststat.
Step 2. Run the CASE tool from Windows.
Step 3. Create the state diagram by placing states and connecting
states with transitions.
Step 4. Enter state names in the state symbols and enter event
names on the transition arrows.
Step 5. Fill in the state and transition specification forms. Ensure the following:
- All states have a unique name, e.g. ReadyState.
- Event names do not have to be unique, however, each event name and condition should be unique, e.g.
start [gas > 0] and start [gas == 0] are unique.
- Each transition condition should be entered in C++ form, e.g. gasQuantity == 0.
- For code generation, enter a default transition condition of 1.
- Transition Actions should be compilable C++ statements, e.g. motor1.start();
- Class Name holding the states should be entered in all states, e.g. Controller
- Enter the initial state name in all states, e.g. ReadyState.
Step 4. Select "Generate Generate State Code".
Step 5. Enter the header file name, e.g. Contrllr.h.
Step 6. Select the header code generation script, e.g. STAHEAD1.SCT.
Step 7. Select "Edit - Edit File" and open Contrllr.h
for review.
Step 8. Select "Generate Generate State Code".
Step 9. Enter the source file name, e.g. Contrllr.cpp.
Step 10. Select the source code generation script, e.g. STAFUNC1.SCT.
Step 11. Select "Edit - Edit File" and open Contrllr.cpp
for review.
Step 12. In your C++ environment, create a project, e.g. FIRSTSTAT.MAK
and add the CONTRLLR.CPP and FIRSMAIN.CPP. Create the main function
in FIRSMAIN.CPP to create an object of the Controller Class, e.g.
Controller controller1;. In the main function
pass events, e.g. controller1.process (turnOn);
Step 13. Compile and run the program.
Step 14. Go back to With Class and update the state
diagram with new states, events, conditions, and actions and repeat
the entire process.
There are four major ways to code classes with finite state logic from a state diagram:
- single controller class with single process function (if..else),
- single controller class with single process function (switch .. case),
- single controller class with a function for each event,
- multiple state classes with superstate base class
and substate derived classes.
There is no single best way to code state logic. Many would argue that the fourth way - multiple state classes with superstate base class and substate derived classes is the most object oriented and therefore the most modifiable. In practice we use the third way - single controller class with a function for each event. In the Car Simulation System and the TV Control System we used the third way by creating a single controller class with a function for each event. We'll provide the scripts to generate C++ using these four ways.
One of the easiest ways to code finite state logic in C++ is to create a Controller class shown below with a single process function the uses if..else statements. States and events are enumerated types. The process function takes an event as an argument. Based upon the value of the current state, the value of the event, and the true/false status of the condition, the process function determines the value of the next state and executes the action. The scripts, generated C++ code, and a main (test) function are shown below. The class diagram is from reversed code and shows the data members, function members, constructor, and destructor.
Script to Generate Controller Class Declaration
SCRIPT_NOREPEAT_HEADER_BEGIN
///////////////////////////Class Declaration TRUNCATE_EIGHT$STATE_CLASS_NAME$.h///////////////////////////
// Class: STATE_CLASS_NAME
#ifndef __$CAPITALIZE_ALL$TRUNCATE_EIGHT$STATE_CLASS_NAME$_H //Required for STATE_CLASS_NAME
#define __$CAPITALIZE_ALL$TRUNCATE_EIGHT$STATE_CLASS_NAME$_H
#include <CString.h>
enum States { [NO_RETURN STATE_LIST_NAME,DELETE_LAST_SYMBOL] };
enum Events { [NO_RETURN EVENT_LIST_NAME,DELETE_LAST_SYMBOL]
};
class STATE_CLASS_NAME
{
States currentState;
string condition;
public:
STATE_CLASS_NAME () : currentState ( STATE_INITIAL_NAME ), condition ("true") { }
void process ( Events anEvent ); //throw (string)
~ STATE_CLASS_NAME () { }
};
#endif
SCRIPT_NOREPEAT_HEADER_END
Script to Generate Controller Class Source File
SCRIPT_NOREPEAT_HEADER_BEGIN
/////////////////////////Function Definitions TRUNCATE_EIGHT$STATE_CLASS_NAME$.h///////////
// Class: STATE_CLASS_NAME
#include "$TRUNCATE_EIGHT$STATE_CLASS_NAME$.h"
#include <iostream.h>
void STATE_CLASS_NAME$::process ( Events anEvent ) //throw (string)
{
SCRIPT_NOREPEAT_HEADER_END
[
if ( ( currentState == TRANSITION_CURRENT_STATE ) && ( anEvent == TRANSITION_EVENT ) && ( TRANSITION_CONDITION ) )
{ currentState = TRANSITION_NEXT_STATE ; TRANSITION_ACTION return; }
]
SCRIPT_NOREPEAT_FOOTER_BEGIN
else { string eventError ("Event Error"); throw eventError; }
}
SCRIPT_NOREPEAT_FOOTER_END
Generated C++ Controller Class Declaration
///////////////////////////Class Declaration Contrllr.h///////////////////////////
// Class: Controller
#ifndef __CONTRLLR_H //Required for Controller
#define __CONTRLLR_H
#include <CString.h>
enum States { OffState, OnState, Termination };
enum Events { turnOff, turnOn };
class Controller
{
States currentState;
string condition;
public:
Controller () : currentState ( OffState ), condition ("true") { }
void process ( Events anEvent ); //throw (string)
~ Controller () { }
};
#endif
Generated C++ Class Source File
/////////////////////////Function Definitions Contrllr.h///////////
// Class: Controller
#include "Contrllr.h"
#include <iostream.h>
void Controller::process ( Events anEvent ) //throw (string)
{
if ( ( currentState == OffState ) && ( anEvent == turnOn ) && ( condition == "true" ) )
{ currentState = OnState ; cout << "Turned
On"; return; }
if ( ( currentState == OffState ) && ( anEvent == turnOn ) && ( condition == "false" ) )
{ currentState = OffState ; cout << "False
Condition - No TurnOn"; return; }
if ( ( currentState == OnState ) && ( anEvent == turnOff ) && ( 1 ) )
{ currentState = Termination ; cout <<
"Turned Off"; return; }
else { string eventError ("Event Error"); throw eventError; }
}
C++ Main (Test) File
#include "Contrllr.h"
#include <iostream.h>
#include <CString.h>
int main ()
{
Controller controller1;
try {
controller1.process (turnOn);}
catch (string eventError) { cout << eventError
<< "Cannot process event" << endl; }
try {
controller1.process (turnOff); }
catch (string eventError) { cout << eventError
<< "Cannot process event" << endl; }
return 0;
}
//Sample Output: Turned On Turned Off
The second way to code finite state logic in C++ is to create a Controller class shown below with a single process function with switch case statements. States and events are enumerated types. The process function takes an event as an argument. Based upon the value of the current state, the value of the event, and the true/false status of the condition, the process function determines the value of the next state and executes the action. The reversed class diagram, scripts, generated C++ code, and a main (test) function are shown below.
Script to Generate Controller Class Declaration
SCRIPT_NOREPEAT_HEADER_BEGIN
///////////////////////////Class Declaration TRUNCATE_EIGHT$STATE_CLASS_NAME$.h///////////////////////////
// Class: STATE_CLASS_NAME
#ifndef __$CAPITALIZE_ALL$TRUNCATE_EIGHT$STATE_CLASS_NAME$_H //Required for STATE_CLASS_NAME
#define __$CAPITALIZE_ALL$TRUNCATE_EIGHT$STATE_CLASS_NAME$_H
#include <CString.h>
enum States { [NO_RETURN STATE_LIST_NAME,DELETE_LAST_SYMBOL] };
enum Events { [NO_RETURN EVENT_LIST_NAME,DELETE_LAST_SYMBOL]
};
class STATE_CLASS_NAME
{
States currentState;
string condition;
public:
STATE_CLASS_NAME () : currentState ( STATE_INITIAL_NAME ), condition ("true") { }
void process ( Events anEvent ); // throw (string)
~ STATE_CLASS_NAME () { }
};
#endif
SCRIPT_NOREPEAT_HEADER_END
Script to Generate Controller Class Source Code
File
SCRIPT_NOREPEAT_HEADER_BEGIN
/////////////////////////Function Definitions TRUNCATE_EIGHT$STATE_CLASS_NAME$.h///////////
// Class: STATE_CLASS_NAME
#include "$TRUNCATE_EIGHT$STATE_CLASS_NAME$.h"
#include <iostream.h>
void STATE_CLASS_NAME$::process ( Events anEvent ) //throw (string)
{
switch (currentState)
{
SCRIPT_NOREPEAT_HEADER_END
case TRANSITION_CURRENT_STATE:
[
if (( TRANSITION_CONDITION ) && ( anEvent == TRANSITION_EVENT ))
{
// Sequence number TRANSITION_SEQUENCE_NUMBER
// Transition current state TRANSITION_CURRENT_STATE
TRANSITION_ACTION
currentState = TRANSITION_NEXT_STATE;
break;
}] //Comment out any unneeded exceptions
else { string eventError ("Event Error"); throw eventError; }
break;
SCRIPT_NOREPEAT_FOOTER_BEGIN
default: { string eventError ("Event Error"); throw eventError; }
}
}
SCRIPT_NOREPEAT_FOOTER_END
Generated C++ Controller Class
///////////////////////////Class Declaration Contrllr.h///////////////////////////
// Class: Controller
#ifndef __CONTRLLR_H //Required for Controller
#define __CONTRLLR_H
#include <CString.h>
enum States { OffState, OnState, Termination };
enum Events { turnOff, turnOn };
class Controller
{
States currentState;
string condition;
public:
Controller () : currentState ( OffState ), condition ("true") { }
void process ( Events anEvent ); // throw (string)
~ Controller () { }
};
#endif
Generated C++ Source Code File
/////////////////////////Function Definitions Contrllr.cpp//////////
// Class: Controller
#include "Contrllr.h"
#include <iostream.h>
void Controller::process ( Events anEvent ) //throw (string)
{
switch (currentState)
{ case OffState:
if (( condition == "true" ) && ( anEvent == turnOn ))
{ // Sequence number 1
// Transition current state OffState
cout << "Turned On";
currentState = OnState;
break;
}
if (( condition == "false" ) && ( anEvent == turnOn ))
{ // Sequence number 1
// Transition current state OffState
cout << "False Condition - No TurnOn";
currentState = OffState;
break;
} //Comment out any unneeded exceptions
else { string eventError ("Event Error"); throw eventError; }
break;
case OnState:
if (( 1 ) && ( anEvent == turnOff ))
{ // Sequence number 1
// Transition current state OnState
cout << "Turned Off";
currentState = Termination;
break;
} //Comment out any unneeded exceptions
else { string eventError ("Event Error"); throw eventError; }
break;
case Termination:
//Comment out any unneeded exceptions
break;
default: { string eventError ("Event Error"); throw eventError; }
}
}
C++ Main (Test) File
#include "Contrllr.h"
#include <iostream.h>
#include <CString.h>
int main ()
{
Controller controller1;
try {
controller1.process (turnOn);}
catch (string eventError) { cout << eventError
<< "Cannot process event" << endl; }
try {
controller1.process (turnOff); }
catch (string eventError) { cout << eventError << "Cannot process event" << endl; }
return 0;
}
//Sample Output: Turned On Turned Off
The third way to code finite state logic in C++ is to create a Controller class shown below with a function for each event. States are enumerated types. There is an function for each event. The function may have arguments. Based upon the value of the current state, the function evaluates the true/false status of the condition, determines the value of the next state and executes the action. The reversed class diagram, scripts, generated C++ code, and a main (test) function are shown below.
Script to Generate Controller Class Declaration
SCRIPT_NOREPEAT_HEADER_BEGIN
///////////////////////////Class Declaration TRUNCATE_EIGHT$STATE_CLASS_NAME$.h///////////////////////////
// Class: STATE_CLASS_NAME
#ifndef __$CAPITALIZE_ALL$TRUNCATE_EIGHT$STATE_CLASS_NAME$_H //Required for STATE_CLASS_NAME
#define __$CAPITALIZE_ALL$TRUNCATE_EIGHT$STATE_CLASS_NAME$_H
#include <CString.h>
enum States { [NO_RETURN STATE_LIST_NAME,DELETE_LAST_SYMBOL]
};
class STATE_CLASS_NAME
{
States currentState;
string condition;
public:
STATE_CLASS_NAME () : currentState ( STATE_INITIAL_NAME ), condition ("true") { }
//Remove any duplicate function declarations
SCRIPT_NOREPEAT_HEADER_END
[ void TRANSITION_EVENT_WITH_PARAMETERS; // throw (string) ]
SCRIPT_NOREPEAT_FOOTER_BEGIN
~ STATE_CLASS_NAME () { }
};
#endif
SCRIPT_NOREPEAT_FOOTER_END
Script to Generate Controller Class Source Code
File
SCRIPT_NOREPEAT_HEADER_BEGIN
/////////////////////////Function Definitions TRUNCATE_EIGHT$STATE_CLASS_NAME$.h///////////
// Class: STATE_CLASS_NAME
#include "$TRUNCATE_EIGHT$STATE_CLASS_NAME$.h"
#include <iostream.h>
//Remove duplicate functions and update functions for conditions
//by copying if..else statements for an event into a single function
SCRIPT_NOREPEAT_HEADER_END
[void STATE_CLASS_NAME$::$TRANSITION_EVENT_WITH_PARAMETERS //throw (string)
{if ( ( currentState == TRANSITION_CURRENT_STATE ) && (TRANSITION_CONDITION) )
{ TRANSITION_ACTION currentState = TRANSITION_NEXT_STATE; return;
}
else { string eventError ("Event Error"); throw eventError; }
}]
Generated C++ Controller Class
///////////////////////////Class Declaration Contrllr.h///////////////////////////
// Class: Controller
#ifndef __CONTRLLR_H //Required for Controller
#define __CONTRLLR_H
#include <CString.h>
enum States { OffState, OnState, Termination
};
class Controller
{
States currentState;
string condition;
public:
Controller () : currentState ( OffState ), condition ("true") { }
//Remove any duplicate function declarations
void turnOn(int aNumber); // throw (string)
void turnOff(); // throw (string)
~ Controller () { }
};
#endif
Generated C++ Source Code File
/////////////////////////Function Definitions Contrllr.cpp///////////
// Class: Controller
#include "Contrllr.h"
#include <iostream.h>
void Controller::turnOn(int aNumber) //throw (string)
{if ( ( currentState == OffState ) && (condition == "true") )
{ cout << "Turned On"; currentState = OnState; return;
}
else { string eventError ("Event Error"); throw eventError; }
}
void Controller::turnOff() //throw (string)
{if ( ( currentState == OnState ) && (1) )
{ cout << "Turned Off"; currentState = Termination; return;
}
else { string eventError ("Event Error"); throw eventError; }
}
C++ Main (Test) File
#include "Contrllr.h"
#include <iostream.h>
#include <CString.h>
int main ()
{
Controller controller1;
try {
controller1.turnOn (1); }
catch (string eventError) { cout << eventError
<< "Cannot process event" << endl; }
try {
controller1.turnOff (); }
catch (string eventError) { cout << eventError
<< "Cannot process event" << endl; }
return 0;
}
//Sample Output: Turned On Turned Off
The fourth way to code finite state logic in C++ is to create a superstate base class with a virtual function for each event and substate derived classes with a virtual function for each appropriate event. The class diagram is shown below. The virtual function may have arguments. Each virtual function returns a pointer to the next state. Each substate class is responsible to implement the event processing logic. The virtual function evaluates the true/false status of the condition, determines the pointer value of the next state, executes the action, and return the pointer to the next state. Immediately following the base class and derived class declarations for the state classes, extern pointers to each state are created, e.g.
extern State* pState;
extern OffState* pOffState;
extern OnState* pOnState;
In the source code file, objects of the state classes are created and pointers to the objects are created as listed below:
OffState theOffState ;
OnState theOnState ;
Termination theTermination ;
OffState* pOffState = &theOffState ;
OnState* pOnState = &theOnState ;
Termination* pTermination = &theTermination ;
State * pState = &theOffState ; //Initialize pState to
the Initial State
Alternatively, you could create dynamic object in the main function as listed below. This is not implemented in the script.
pOffState = new OffState();
pOnState = new OnState();
State *pCurrentState;
pCurrentState = pOffState;
pCurrentState = pCurrentState->turnOn();
The reversed class diagram, scripts, generated C++
code, and a main (test) function is shown below.
Script to Generate Controller Class Declaration
SCRIPT_NOREPEAT_HEADER_BEGIN
///////////////////////////Class Declaration TRUNCATE_EIGHT$STATE_CLASS_NAME$.h///////////////////////////
// Class: STATE_CLASS_NAME
#ifndef __$CAPITALIZE_ALL$TRUNCATE_EIGHT$STATE_CLASS_NAME$_H //Required for STATE_CLASS_NAME
#define __$CAPITALIZE_ALL$TRUNCATE_EIGHT$STATE_CLASS_NAME$_H
#include <CString.h>
class State {
protected:
string condition;
public:
State () : condition ("true") { }
[ virtual State * EVENT_LIST_NAME_WITH_PARAMETERS { return this; } ]
virtual ~ State () { }
};
SCRIPT_NOREPEAT_HEADER_END
class STATE_NAME : public State
{
public:
STATE_NAME ( ) { }
//Delete duplicate function declarations
[ State * TRANSITION_EVENT_WITH_PARAMETERS; //throw (string) ]
~ STATE_NAME () { }
};
SCRIPT_NOREPEAT_FOOTER_BEGIN
extern State* pState;
[extern STATE_LIST_NAME$* p$STATE_LIST_NAME;
]
class Controller
{
public:
Controller () { }
//Delete duplicate function declarations
[void EVENT_LIST_NAME_WITH_PARAMETERS ; ]
~ Controller () { }
};
#endif
SCRIPT_NOREPEAT_FOOTER_END
Script to Generate Controller Class Source Code
File
SCRIPT_NOREPEAT_HEADER_BEGIN
/////////////////////////Function Definitions TRUNCATE_EIGHT$STATE_CLASS_NAME$.cpp//////////
// Class: STATE_CLASS_NAME
#include "$TRUNCATE_EIGHT$STATE_CLASS_NAME$.h"
#include <iostream.h>
[
STATE_LIST_NAME the$STATE_LIST_NAME ;
]
[
STATE_LIST_NAME$* p$STATE_LIST_NAME = &the$STATE_LIST_NAME ;
]
State * pState = &the$STATE_INITIAL_NAME
; //Initialize pState to the Initial State
//Remove duplicate functions and update functions for conditions
SCRIPT_NOREPEAT_HEADER_END
[
State * TRANSITION_CURRENT_STATE$::$TRANSITION_EVENT_WITH_PARAMETERS //throw (string)
{if (TRANSITION_CONDITION)
{ TRANSITION_ACTION
pState = (State*) p$TRANSITION_NEXT_STATE ;
return pState;
}
else { string eventError ("Event Error"); throw eventError; }
} ]
[ //Remove Parameter Type in Message
void Controller::TRANSITION_EVENT_WITH_PARAMETERS
{ try {
pState = pState -> TRANSITION_EVENT_WITH_PARAMETERS ; }
catch (string eventError) { throw eventError; }
} ]
Generated C++ Controller Class
///////////////////////////Class Declaration Contrllr.h///////////////////////////
// Class: Controller
#ifndef __CONTRLLR_H //Required for Controller
#define __CONTRLLR_H
#include <CString.h>
class State {
protected:
string condition;
public:
State () : condition ("true") { }
virtual State * turnOff() { return this; }
virtual State * turnOn(int aNumber) { return this; }
virtual ~ State () { }
};
class OffState : public State
{
public:
OffState ( ) { }
//Delete duplicate function declarations
State * turnOn(int aNumber); //throw (string)
~ OffState () { }
};
class OnState : public State
{
public:
OnState ( ) { }
//Delete duplicate function declarations
State * turnOff(); //throw (string)
~ OnState () { }
};
class Termination : public State
{
public:
Termination ( ) { }
//Delete duplicate function declarations
~ Termination () { }
};
extern State* pState;
extern OffState* pOffState;
extern OnState* pOnState;
extern Termination* pTermination;
class Controller
{
public:
Controller () { }
//Delete duplicate function declarations
void turnOff() ;
void turnOn(int aNumber) ;
~ Controller () { }
};
#endif
Generated Source Code File
/////////////////////////Function Definitions Contrllr.cpp///////////
// Class: Controller
#include "Contrllr.h"
#include <iostream.h>
OffState theOffState ;
OnState theOnState ;
Termination theTermination ;
OffState* pOffState = &theOffState ;
OnState* pOnState = &theOnState ;
Termination* pTermination = &theTermination
;
State * pState = &theOffState ; //Initialize
pState to the Initial State
State * OffState::turnOn(int aNumber) //throw (string)
{ if (condition == "true")
{ cout << "Turned On";
pState = (State*) pOnState ;
return pState;
}
if (condition == "false")
{ cout << "False Condition - No TurnOn";
pState = (State*) pOffState ;
return pState;
}
else { string eventError ("Event Error"); throw eventError; }
}
void Controller::turnOn(int aNumber)
{ try {
pState = pState -> turnOn(1) ; }
catch (string eventError) { throw eventError; }
}
State * OnState::turnOff() //throw (string)
{if (1)
{ cout << "Turned Off";
pState = (State*) pTermination ;
return pState;
}
else { string eventError ("Event Error"); throw eventError; }
}
void Controller::turnOff()
{ try {
pState = pState -> turnOff() ; }
catch (string eventError) { throw eventError; }
}
C++ Main (Test) File
#include "Contrllr.h"
#include <iostream.h>
#include <CString.h>
int main ()
{
Controller controller1;
try {
controller1.turnOn(1); }
catch (string eventError) { cout << eventError
<< "Cannot process event" << endl; }
try {
controller1.turnOff(); }
catch (string eventError) { cout << eventError
<< "Cannot process event" << endl; }
return 0;
}
//Sample Output: Turned On Turned Off
This chapter presents four C++ code generation scripts to generate C++ from state diagrams.