Generating C++ Code
Since version 1.5 sinelabore supports the generation of C++ code. C++ offers many features that should be used with care in embedded systems. Therefore, the code generator uses only a very small subset of C++ by default. In fact, only classes, public/protected/private member variables and methods are used. More modern features such as virtual functions, templates, etc. are only used if they are enabled. With the various parameters, the generated code can be very flexibly adapted to one's own needs. Some of the new parameters require a compiler that supports at least the 2011 C++ standard (e.g. –std=c++11 for g++ or clang++).
To generate C++ code call the code generator with the command line flag ’-l cppx’
.
The generated code does not follow the state pattern as you might expect (if you are familiar with common design patterns). The reason is that the machine code is completely generated and no hand-coding is involved. The following class diagram shows the structure of the generated classes.
The code generator supports code generation from models with regions. A region is an orthogonal part of a state. Regions allow to express parallelism within a state. A state can have two or more regions. Region contains states and transitions. To add a region in astah* right click to a state and select Add region from the context menu.
An example state diagram with regions is shown below. Imagine how much effort it would be to manually write the code for this machine. And even worse how difficult it would be to maintain it over time when new states/events are added.
Available parameters and options:
- Basic signature:
int processEvent(const TESTCASE_EVENT_T msg);
- Using a template parameter that allows elegant and flexible parameter handover:
template <typename T> int processEvent(const TESTCASE_EVENT_T msg, const T& userdata);
- Without any parameter:
int processEvent(void);
- Command line parameter
-Trace
enables trace code generation - …
All details about the C++ backend are available in section “Generating C++ Code” of the manual.
Example code that generated from a state diagram as shown above:
/* Command line options: -v -p CADIFRA -l cppx -o testcase testcase.cdd */ /* This file is generated from testcase.cdd - do not edit manually */ /* Generated on: Thu Feb 13 19:50:07 CET 2020 / version 4.1 */ #include "testcase.h" #include "testcase_ext.h" #include <stdio.h> extern unsigned char msg; extern unsigned char condition; namespace simulator { testcase::testcase(void) { m_initialized=static_cast<int>(0U); } /* State names */ const char states[]= "S3\0S2\0S21\0S22\0S11\0S1\0S12\0"; const unsigned short state_idx[]={ 0,3,6,10,14,18,21,25}; /* Event names */ const char events[]= "evA\0NO_MSG\0"; const unsigned short evt_idx[]={ 0,4}; const char* testcase::getNameByState(const unsigned short state) const { return states+state_idx[state]; } const char* testcase::getNameByEvent(const TESTCASE_EVENT_T evt) const { return events+evt_idx[evt]; } // Helper(s) to reset history void testcase::resetHistoryS1(void){ stateVars.stateVarS1=S11; } void testcase::resetHistoryS2(void){ stateVars.stateVarS2=S21; } // Helper(s) to find out if the machine is in a certain state bool testcase::isInS3(void) const {return(((stateVars.stateVar== S3)) ? (true) : (false));} bool testcase::isInS2(void) const {return(((stateVars.stateVar== S2)) ? (true) : (false));} bool testcase::isInS21(void) const {return(((stateVars.stateVarS2== S21)&&(stateVars.stateVar== S2)) ? (true) : (false));} bool testcase::isInS22(void) const {return(((stateVars.stateVarS2== S22)&&(stateVars.stateVar== S2)) ? (true) : (false));} bool testcase::isInS11(void) const {return(((stateVars.stateVarS1== S11)&&(stateVars.stateVar== S1)) ? (true) : (false));} bool testcase::isInS1(void) const {return(((stateVars.stateVar== S1)) ? (true) : (false));} bool testcase::isInS12(void) const {return(((stateVars.stateVarS1== S12)&&(stateVars.stateVar== S1)) ? (true) : (false));} // Helper to get id of innermost active state testcase::States testcase::getInnermostActiveState(void) const { testcase::States state = NUM_STATES; if(isInS12()){ state = S12; }else if(isInS11()){ state = S11; }else if(isInS22()){ state = S22; }else if(isInS21()){ state = S21; }else if(isInS3()){ state = S3; }else{ // intentionally left blank } return state; } // Initialize method. Must be called once to init the machine void testcase::initialize(){ //call on entry code of default states if(m_initialized==static_cast<int>(0U)){ m_initialized=static_cast<int>(1U); //Create copy of statevar stateVarsCopy = stateVars; // Set state vars to default states stateVarsCopy.stateVar = S1; /* set init state of top state */ stateVarsCopy.stateVarS1 = S11; /* set init state of S1 */ stateVarsCopy.stateVarS2 = S21; /* set init state of S2 */ // Copy state variables back stateVars = stateVarsCopy; } } // State machine event handler int testcase::processEvent(const TESTCASE_EVENT_T msg){ int evConsumed = 0U; if(m_initialized==static_cast<int>(0U)){ return static_cast<int>(0); } //Create copy of statevar stateVarsCopy = stateVars; /* action code */ switch (stateVars.stateVar) { case S1: switch (stateVars.stateVarS1) { case S11: /* action code */ if(msg==evA){ /* Transition from S11 to S3 */ evConsumed=1U; /* OnExit code of state S11 */ /* OnExit code of state S1 */ /* Action code for transition */ /* OnEntry code of state S3 */ /* adjust state variables */ stateVarsCopy.stateVar = S3; stateVarsCopy.stateVarS1 = S11; }else{ /* Intentionally left blank */ } /*end of event selection */ break; /* end of case S11 */ case S12: break; /* end of case S12 */ default: /* Intentionally left blank */ break; } /* end switch S1 */ break; /* end of case S1 */ case S3: /* action code */ if(condition==1){ /* Transition from S3 to S11 */ evConsumed=16U; /* OnExit code of state S3 */ /* Action code for transition */ /* OnEntry code of state S1 */ /* OnEntry code of state S11 */ /* adjust state variables */ stateVarsCopy.stateVar = S1; stateVarsCopy.stateVarS1 = S11; }else{ /* Intentionally left blank */ } /*end of event selection */ break; /* end of case S3 */ case S2: switch (stateVars.stateVarS2) { case S21: break; /* end of case S21 */ case S22: break; /* end of case S22 */ default: /* Intentionally left blank */ break; } /* end switch S2 */ break; /* end of case S2 */ default: /* Intentionally left blank */ break; } /* end switch stateVar_root */ // Copy state variables back stateVars = stateVarsCopy; return evConsumed; } // end processEvent } //namespace simulator