Skip to content

C++ code generator

itemis CREATE provides a wide range of options to generate C++ code. The main options are:

  • C++11 (and upwards) complient code is generated by default
  • C++98 complient code which is compatible with the YAKDINDU Statechart Tools 4.x C++ code generator
  • C++Qt which generated Qt compliant code based on the C++11 code generator

This chapter describes the required steps for generating C++ code with itemis CREATE. Furthermore, all components of the generated code will be described in detail and each configurable generator feature will be explained.


Table of content:

Statechart example model

We will use the following example to explain the code generation, the generated code and the integration with client code. The example model describes a light switch with two states and the following behavior:

  • The on_button event turns the light on and, upon repeated pushing, turns the brightness higher until the latter’s maximum is reached.
  • The off_button event turns the light off.
  • When the light is on, it automatically turns off after 30 seconds.
  • Whenever a state is entered, an outgoing event is raised.

The ‘C++ Code Generation’ example can be found in the example wizard:
File -> New -> Example... -> CREATE Statechart Examples -> Getting Started – Code Generation -> C++ Code Generation

Statechart example model

Statechart example model

Generating C++ code


Generating C++ code from a statechart requires a generator file (.sgen). It must at least specify the create::cpp generator, reference a statechart and define the targetProject and targetFolder in the Outlet feature. By specifying these attributes, C++ state machine code can be generated.

Example:

GeneratorModel for create::cpp {

	statechart LightSwitch {

		feature Outlet {
			targetProject = "org.yakindu.sct.examples.codegen.cpp"
			targetFolder = "src-gen"
		}
	}
}

The following generators can be specified:

  • create::cpp11 the generated code is compliant with the C++11 and newer standards
  • create::cpp is an alias for create::cpp11
  • create::cpp::qt the generated code is complient with Qt conventions. This means the generated state machine is a QObject and for incoming and outgoing events Qt signals and slots are generated.

You can create a generator model with the CREATE Statechart generator model wizard by selecting File → New → Code generator model.

The code generation is performed automatically whenever the statechart or the generator file is modified. See also chapter Running a generator for more information.

Generated code files

Generated code files can be categorized into base, api and library files.

The base source file is the implementation of the state machine model. It is generated into the folder defined by the targetFolder parameter. If the statechart defines a namespace, this namespace will be used for the class as well. The file name is derived from the statechart name, but can be overridden by the moduleName parameter.

  • LightSwitch.cpp: Implementation of the state machine. It implements the API defined in LightSwitch.h.

API files are the header files that expose the state machine’s API. They are generated into the apiTargetFolder or, if that one is not defined, into the targetFolder .

  • LightSwitch.h: Contains several data types as well as the API functions to run the state machine, raise events, access variables and so on. It always inherits from the abstract class StatemachineInterface, and based on the used features potentially from CycleBasedInterface and TimedInterface.

Library files are independent of the concrete state machine model. They are generated into the libraryTargetFolder , or if that one is not defined, into the targetFolder .

  • sc_statemachine.h: Contains the abstract class StatemachineInterface .
  • sc_cyclebased.h: Contains the abstract class CycleBasedInterface . It is only generated if the statechart is cycle-based.
  • sc_timer.h Contains the abstract classes TimedInterface and TimerServiceInterface. It is only generated if the statechart uses timed event triggers, like every or after clauses.
  • sc_timer_service.h: Default timer service. It is only generated if enabled in the generator model.
  • sc_timer_service.cpp: Implementation of the default timer service declared in sc_timer_service.h. It is only generated if enabled in the generator model.
  • sc_tracing.h: Contains the abstract class TraceObserver .
  • sc_rxcpp.h: Contains declarations of data types and functions used for the observer mechanism to react on outgoing events. It is generated only if it is required.
  • sc_types.h: Contains type definitions used by the statechart. Since the contents of this file is always the same for all statecharts, it will be generated only if it does not yet exist. And since it will never be overwritten, you can change or amend the definitions made there. For example, you might wish to adapt the types to better match your target platform.

Abstract class StatemachineInterface

Each generated state machine implements the interface StatemachineInterface. It is defined in the sc_statemachine.h header file and describes the API to enter and exit a state machine, as well as to check if the machine is active or final:

#ifndef SC_STATEMACHINE_H_
#define SC_STATEMACHINE_H_

namespace sc {

/*! \file 
Basic interface for state machines.
*/
class StatemachineInterface
{
	public:
	
		virtual ~StatemachineInterface() = 0;
		
		/*!
		Enters the state machine. Sets the state machine into a defined state.
		*/
		virtual void enter() = 0;
	
		/*!
		Exits the state machine. Leaves the state machine with a defined state.
		*/
		virtual void exit() = 0;
		
		/*!
		Checks whether the state machine is active. 
	 	A state machine is active if it has been entered. It is inactive if it has not been entered at all or if it has been exited.
	 	*/	
		virtual	bool isActive() const = 0;
		
		/*!
		Checks if all active states are final. 
	 	If there are no active states then the state machine is considered being inactive. In this case this method returns false.
	 	*/
		virtual bool isFinal() const = 0;
};

inline StatemachineInterface::~StatemachineInterface() {}

} /* namespace sc */

#endif /* SC_STATEMACHINE_H_ */


The interface defines the following methods:

  • The enter() method must be called to enter the state machine. It brings the state machine to a well-defined state.
  • The exit() method is used to leave a state machine statefully. If for example a history state is used in one of the top regions, the last active state is stored and the state machine is left via exit(). Re-entering it via enter() continues to work with the saved state.
  • The isActive() method checks whether the state machine is active, i.e. at least one state of the machine is active. A state machine is active if it has been entered. It is inactive if it has not been entered at all or if it has been exited.
  • The isFinal() method checks whether the all active states are final. If there are no active states then the state machine is considered being inactive. In this case this method returns false.

Abstract class CycleBasedInterface

In addition, state machines that use the cycle-based execution scheme implement the interface CycleBasedInterface which adds the runCycle() method to the API.

#ifndef SC_CYCLEBASED_H_
#define SC_CYCLEBASED_H_

#include "sc_statemachine.h"

namespace sc {

/*! \file
Interface for cycle-based state machines.
*/
class CycleBasedInterface : public sc::StatemachineInterface
{
	public:
	
		virtual ~CycleBasedInterface() = 0;
	
		/*! Start a run-to-completion cycle.
		*/
		virtual void runCycle() = 0;
};

inline CycleBasedInterface::~CycleBasedInterface() {}

} /* namespace sc */

#endif /* SC_CYCLEBASED_H_ */

The runCycle() method is used to trigger a run-to-completion step in which the state machine evaluates arising events and computes possible state changes. For event-driven statecharts, this method is called automatically when an event is received. For cycle-based statecharts, this methods needs to be called explicitly in the client code. See also chapter Execution schemes. Somewhat simplified, a run-to-completion cycle consists of the following steps:

  • Clear the list of outgoing events.
  • Check whether any events have occurred which are leading to a state change.
  • If a state change has to be done:
    • Make the present state inactive.
    • Execute exit actions of the present state.
    • Save history state, if necessary.
    • Execute transition actions, if any.
    • Execute entry actions of the new state.
    • Make the new state active.
  • Clear the list of incoming events.

Abstract class TimedInterface

State machines that use timed event triggers also implement the interface TimedInterface. This interface is defined in the sc_timer.h header file. It adds a few additional methods to the API in order to set a timer service and to raise time events. The timer service is used to start timers and informs the state machine with the raiseTimeEvent() method when such a timer is finished. Read more about how a state machine interacts with its timer service in section Time-controlled state machines.

#include "sc_types.h"
#include <cstddef>

namespace sc {
namespace timer {

class TimedInterface;
class TimerServiceInterface;

/*! \file
Interface for state machines which use timed event triggers.
*/
class TimedInterface {
	public:
	
		virtual ~TimedInterface() = 0;
		
		/*!
		Set the timer service for the state machine. It must be set
		externally on a timed state machine before a run cycle can be executed.
		*/
		virtual void setTimerService(sc::timer::TimerServiceInterface* timerService) = 0;
		
		/*!
		Return the currently used timer service.
		*/
		virtual sc::timer::TimerServiceInterface* getTimerService() = 0;
		
		/*!
		Callback method if a time event occurred.
		*/
		virtual void raiseTimeEvent(sc::eventid event) = 0;
		
		/*!
		Retrieve the number of time events that can be active at once in this state machine.
		*/
		virtual sc::integer getNumberOfParallelTimeEvents() = 0;
		
};
...

The state machine class

The state machine class implements at least the interface StatemachineInterface and potentially also the interfaces CycleBasedInterface and TimedInterface.

The state machine class defines a constructor in which all state machine variables are initialized to their respective default values or their initializations as defined in the statechart model.

Each named statechart interface is reflected by an inner class. For the example model above, the two statechart interfaces user and light are transformed into the two classes User and Light:

//! Inner class for user interface scope.
class User
{
	public:
		explicit User(LightSwitch* parent) noexcept;
		
		/*! Raises the in event 'on_button' of interface scope 'user'. */
		void raiseOn_button();
		/*! Raises the in event 'off_button' of interface scope 'user'. */
		void raiseOff_button();
		
	private:
		...
};

//! Inner class for light interface scope.
class Light
{
	public:
		explicit Light(LightSwitch* parent) noexcept;
		
		/*! Gets the value of the variable 'brightness' that is defined in the interface scope 'light'. */
		sc::integer getBrightness() const noexcept;
		
		/*! Sets the value of the variable 'brightness' that is defined in the interface scope 'light'. */
		void setBrightness(sc::integer brightness) noexcept;
		
		/*! Get observable for event 'on' of interface scope 'light'. */
		sc::rx::Observable<void>& getOn() noexcept;
		/*! Get observable for event 'off' of interface scope 'light'. */
		sc::rx::Observable<void>& getOff() noexcept;
		
	private:
		...
};

In this example code you can see the following:

  • Statechart variables are transformed into class members with according getters and setters (see getBrightness() and setBrightness(sc::integer value)).
  • Incoming events are transformed into methods to raise such an event from the client code (see raiseOn_button() and raiseOff_button()).
  • Outgoing events are transformed into observable objects to which the client code can subscribe (see getOn() and getOff).

A statechart can also define an unnamed interface. In this case, all the members and methods are defined directly in the state machine class.

The resulting class for the light switch example looks like the following. For documentation purposes we only show the public methods here:

class LightSwitch : public sc::timer::TimedInterface, public sc::EventDrivenInterface
{
	public:
		LightSwitch() noexcept;
		
		virtual ~LightSwitch();
		
		/*! Enumeration of all states. */
		enum class State
		{
			NO_STATE,
			main_region_Off,
			main_region_On
		};
		
		/*! The number of states. */
		static constexpr const sc::integer numStates {2};
		static constexpr const sc::integer scvi_main_region_Off {0};
		static constexpr const sc::integer scvi_main_region_On {0};
		
		/*! Enumeration of all events which are consumed. */
		enum class Event
		{
			NO_EVENT,
			User_on_button,
			User_off_button,
			_te0_main_region_On_
		};
		
		class EventInstance
		{
			public:
				explicit  EventInstance(Event id) noexcept : eventId(id){}
				virtual ~EventInstance() = default;
				const Event eventId;
		};
		
		//! Inner class for user interface scope.
		class User
		{
			// see above
		};
		
		/*! Returns an instance of the interface class 'User'. */
		User& user() noexcept;
		
		//! Inner class for light interface scope.
		class Light
		{
			// see above
		};
		
		/*! Returns an instance of the interface class 'Light'. */
		Light& light() noexcept;
		
		
		/*! Can be used by the client code to trigger a run to completion step without raising an event. */
		void triggerWithoutEvent() override;
		/*
		 * Functions inherited from StatemachineInterface
		 */
		 void enter() override;
		
		 void exit() override;
		
		/*!
		 * Checks if the state machine is active (until 2.4.1 this method was used for states).
		 * A state machine is active if it has been entered. It is inactive if it has not been entered at all or if it has been exited.
		 */
		 bool isActive() const noexcept override;
		
		
		/*!
		* Checks if all active states are final. 
		* If there are no active states then the state machine is considered being inactive. In this case this method returns false.
		*/
		 bool isFinal() const noexcept override;
		
		/*! 
		 * Checks if member of the state machine must be set. For example an operation callback.
		 */
		bool check() const noexcept;
		
		/*
		 * Functions inherited from TimedStatemachineInterface
		 */
		void setTimerService(sc::timer::TimerServiceInterface* timerService_) noexcept override;
		
		sc::timer::TimerServiceInterface* getTimerService() noexcept override;
		
		void raiseTimeEvent(sc::eventid event) override;
		
		sc::integer getNumberOfParallelTimeEvents() noexcept override;
		
		/*! Checks if the specified state is active (until 2.4.1 the used method for states was calles isActive()). */
		bool isStateActive(State state) const noexcept;
		
		//! number of time events used by the state machine.
		static const sc::integer timeEventsCount {1};
		
		//! number of time events that can be active at once.
		static const sc::integer parallelTimeEventsCount {1};
		
	protected:
		...
	private:
		...
};

The following code snippet demonstrates how to use the state machine API:

/*! Instantiate the state machine */
LightSwitch *sm = new LightSwitch();

/*! Subscribe to out events to be called when they are raised */
LightOnObserver *lightOnObserver = new LightOnObserver();
LightOffObserver *lightOffObserver = new LightOffObserver();
lightOnObserver->subscribe(lightSwitch->light()->getOn());
lightOffObserver->subscribe(lightSwitch->light()->getOff());

/*! Set the timer service */
sm->setTimerService(timerService);

/*! Enter the state machine; from this point the machine reacts to events */
sm->enter();

/*! Raise input events, this will cause a run-to-completion step */
sm->user()->raiseOn_button();

/*! Access variable via its getter/setter */
sm->light()->setBrightness(5);

/*! Exit the state machine */
sm->exit();

Please note in the code above, that in order to subscribe to outgoing events, you need to implement an observer. The following snippet shows a simple observer implementation for the on event:

/*! Observer with callback for the light.on event */
class LightOnObserver: public sc::rx::SingleSubscriptionObserver<void> {
	virtual void next() {
		cout << "Light is on." << endl;
	}
};


Serving operation callbacks

itemis CREATE support operations that are executed by a state machine as actions, but are implemented by client-side code.

As a simple example a function myOp can be defined in the definition section of the LightSwitch example:

interface:
operation myOp()

For state machines that define operations in their interface(s), the code generator creates an operation callback interface as inner class in the state machine class as well as a corresponding setter:

//! Inner class for default interface scope operation callbacks.
class OperationCallback
{
	public:
		virtual ~OperationCallback() = 0;
		
		virtual void myOp() = 0;
		
		
};

/*! Set the working instance of the operation callback interface 'OperationCallback'. */
void setOperationCallback(OperationCallback* operationCallback);


When the operation is called from within the state machine, the operation call is delegated to the operation callback member. Hence, the client code needs to:

  • provide an implementation of this interface and
  • pass an instance of it to the state machine via the setOperationCallback(OperationCallback* operationCallback) method.

An additional interface OperationCallback with the pure virtual function void myOp() has been generated. This interface has to be implemented, and an instance of the implementing class has to be provided to the state machine via the setOperationCallback(OperationCallback* operationCallback) function, so that the state machine can use it.

The virtual function myOp() can be implemented in a new class OCBImplementation with the .h file:

#ifndef OCBIMPLEMENTATION_H_
#define OCBIMPLEMENTATION_H_

#include "LightSwitch.h"

class OCBImplementation : public LightSwitch::OperationCallback{
public:
    OCBImplementation();
    virtual ~OCBImplementation();
    void myOp();
};

#endif /* OCBIMPLEMENTATION_H_ */

And the implementation in the .cpp file:

#include "OCBImplementation.h"

OCBImplementation::OCBImplementation() {
}

OCBImplementation::~OCBImplementation() {
}

void OCBImplementation::myOp(){
    // Your operation code should be placed here;
    return 0;
}

After this, the callback must be set before entering the state machine:

LightSwitch *sm = new LightSwitch();
sm->setOperationCallback(new OCBImplementation());
sm->enter();
}

Time-controlled state machines

As already mentioned, time-controlled state machines implement the interface TimedInterface and require a timer service to work properly.

The light switch example model is such a time-controlled state machine as it uses an after clause at the transition from state On to state Off.

To support time-controlled behavior, the abstract classes TimedInterface and TimerServiceInterface are generated in the sc_timer.h header file. Also, the state machine class gets two additional constants: timeEventsCount and parallelTimeEventsCount, which are the overall number of time events in the statechart and the maximum number of time events that can be active simultaneously. For example, time events in states within the same region can never be active in parallel, while time events in a parent state can be active together with the time events of child states. You can use these constants in a timer service to allocate sufficient memory for the timers.

The generated state machine class implements the interface TimedInterface and has a property timerService of type TimerServiceInterface. The client code must provide an TimerServiceInterface implementation to the state machine by calling the latter’s setTimerService() method before entering the state machine.

LightSwitch *sm = new LightSwitch();
sm->setTimerService(new TimerService());
sm->enter();

Timer functions generally depend on the hardware target used, therefore the proper time handling has to be implemented by the developer. In principle, for each hardware target a dedicated timer service class implementing the interface TimerServiceInterface has to be developed.

Default timer service implementation

The C++ code generator can create a default implementation of the TimerServiceInterface, and in many cases it will be sufficient.

To generate the default timer service class, set the timerService feature in the generator model to true. Example:

GeneratorModel for create::cpp {

   statechart LightSwitch {

    /* … */
   
    feature GeneralFeatures {
      timerService = true
      timerServiceTimeType = ""
    }
  }
}

Timer service

A timer service must implement the TimerServiceInterface interface and must be able to maintain a number of time events and the timers associated with them. A time event is identified by a numeric ID.

If suitable, an application can use the default timer service class TimerService, see section "Default timer implementation" for details.

Let’s have a look at the TimerServiceInterface interface:

/*! \file
Timer service interface.
*/
class TimerServiceInterface
{
	public:
		
		virtual ~TimerServiceInterface() = 0;
	
		/*!
		Starts the timing for a time event.
		*/ 
		virtual void setTimer(TimedInterface* statemachine, sc::eventid event, sc::integer time_ms, bool isPeriodic) = 0;
		
		/*!
		Unsets the given time event.
		*/
		virtual void unsetTimer(TimedInterface* statemachine, sc::eventid event) = 0;
};

Function setTimer

A state machine calls the setTimer(TimedInterface* statemachine, sc::eventid event, sc::integer time, bool isPeriodic) function to tell the timer service that it has to start a timer for the given time event and raise it after the period of time specified by the time_ms parameter has expired. In order to raise the time event the function raiseTimeEvent(int eventID) on the TimedInterface statemachine object is called.

It is important to only start a timer thread or a hardware timer interrupt within the setTimer() function and to avoid any time-consuming operations like extensive computations, or waiting. Otherwise the state machine execution might hang within the timer service or might not show the expected runtime behavior.

If the parameter isPeriodic is false, the timer service raises the time event only once. If isPeriodic is true, the timer service raises the time event every time milliseconds.

Function unsetTimer

The state machine calls the function unsetTimer(TimedInterface* statemachine, sc::eventid event) to notify the timer service to unset the timer for the given event ID.

Raising time events on a state machine

The interface TimedInterface specifies a method to raise time events: public void raiseTimeEvent(int eventID).

It is the timer service’s responsibility to actually raise a time event on a state machine. To do so, the timer service calls the state machine’s raiseTimeEvent()_ method and supplies the time event’s eventID as a parameter. The state machine recognizes the time event and will process it during the next run cycle.

For event-driven state machines, raising a time event is treated equally to raising an input event. This means, that a run-to-completion step is automatically invoked (i.e. the internal runCycle() method is called).

For cycle-based state machines, the runCycle() methods needs to be called as frequently as needed to process time events without too much latency. Consider, for example, a time event which is raised by the timer service after 500 ms. However, if the runtime environment calls the state machine’s runCycle() method with a frequency of once per 1000 ms only, the event will quite likely not be processed at the correct points in time.

Trace-observed state machine

By using the tracing feature the execution of the state machine can be observed. In detail, entered and exited states can be traced. For this, additional operation callbacks onStateEntered and onStateExited are generated in the sc_tracing.h header file:

namespace sc {
namespace trace {

template<typename T>
class TraceObserver
{
public:
	virtual ~TraceObserver(){}

	virtual void stateEntered(T state) = 0;

	virtual void stateExited(T state) = 0;
};
} /* namespace sc::trace */
} /* namespace sc */


The client code needs to:

  • provide an implementation of this interface and
  • pass an instance of it to the state machine via the setTraceObserver(TraceObserver<State> traceObserver) method.

The TraceObserver class can be implemented as following:

class TraceObserverImpl : public sc::trace::TraceObserver<LightSwitch::LightSwitchStates>{
	public:
		TraceObserverImpl();
		virtual ~TraceObserverImpl();
		void stateEntered(LightSwitch::LightSwitchStates state);
		void stateExited(LightSwitch::LightSwitchStates state);
	};
	TraceObserverImpl::TraceObserverImpl() {}
	TraceObserverImpl::~TraceObserverImpl() {}
	
	void TraceObserverImpl::stateEntered(LightSwitch::LightSwitchStates state) {
		// observe any entered state
	}
	void TraceObserverImpl::stateExited(LightSwitch::LightSwitchStates state) {
		// observe any exited state
	}
}


Finally, the trace observer has to be set in the state machine:

LightSwitch* sm = new LightSwitch();
TraceObserverImpl* observer = new TraceObserverImpl(); 
sm->setTraceObserver(observer);
sm->enter();


Memory management

The generated state machine code will never allocate any memory for cycle based state machines. Allocating the memory needed for the statechart structure is a responsibility of the client code. Event driven state machines currently dynamically allocate memory for events. This behavior is encapsulated. So the client code does not have to care about event allocation and deallocation.

C++ code generator features

Beside the general code generator features, there are language specific generator features, which are listed in the following chapter.

Outlet feature

The Outlet feature specifies target project and target folder for the generated artifacts. It is a required feature and has the parameters as described in Outlet feature .


The C++ code generator extends this feature by the following parameter:

Api Target Folder

  • apiTargetFolder (String, optional): The folder to write API code to, i.e. the statechart specific header files. If this parameter is not specified, these artifacts will be generated into the target folder (see Outlet feature ).

Example:

  apiTargetFolder = "api-gen"

GeneratorOptions feature

The GeneratorOptions feature allows to change the behavior of the C++ generator.

Example:

feature GeneratorOptions {
    innerFunctionVisibility = "protected"
    smartPointers = false
    staticOperationCallback = true
}


Inner Function Visibility

  • innerFunctionVisibility (String, optional): This parameter is used to change the visibility of inner functions and variables. By default private visibility is used. It can be changed to protected to allow function overriding for a class which inherits from the generated state machine base class.


Smart Pointers

  • smartPointers (Boolean, optional): This option only affects the generated code for C++11. It is not available for the C++98 or C code generator. The default value of it is false. If this parameter is set to true, the generated code will use smart pointers instead of plain pointers. It also implies that the generated code generally relies on dynamic memory allocation. This currently does not apply to state machine references in multi state machine scenarios. The generator option does not affect smart pointers declared or used in the statechart itself.


Static Operation Callback

  • staticOperationCallback (Boolean, optional): If this parameter is set to true, the callback function declaration for statechart operations is static and the functions are called statically by the state machine code.

Includes feature

The Includes feature allows to change the way include statements are generated.

Example:

feature Includes {
	useRelativePaths = false
	generateAllSpecifiedIncludes = false
}


Use Relative Paths

  • useRelativePaths (Boolean, optional): If this parameter is set to true, relative paths are calculated for include statements, otherwise simple includes are used. Default value is true.


Generate all Specified Includes

  • generateAllSpecifiedHeaders (Boolean, optional): If this parameter is set to true, all includes which are specified using the Deep-C integration are also included in the generated state machine header file. If it is set to false then only those headers are included which actually contain declarations which are actively used by the statechart. Default: false.


IdentifierSettings feature

The IdentifierSettings feature allows the configuration of module names and identifier character length.

Example:

feature IdentifierSettings {
    moduleName = "MyStatechart"
    statemachinePrefix = "myStatechart"
    separator = "_"
    headerFilenameExtension =  "h"
    sourceFilenameExtension =  "cpp"
}

Please note that the maxIdentifierLength option, which existed in older versions of itemis CREATE, has been removed in favor of a statechart annotation that is only available in the C/C++ domain bundled with itemis CREATE Professional Edition, see @ShortIdentifiers.


Module Name

  • moduleName (String, optional): Name for header and implementation. By default, the name of the statechart is used.


Statemachine Prefix

  • statemachinePrefix (String, optional): Prefix that is prepended to function, state, and type names. By default, the name of the statechart is used.


Separator

  • separator (String, optional): Character to replace whitespace and otherwise illegal characters in names.


Header Filename Extension

  • headerFilenameExtension (String, optional): Sets the filename extension for all header files. Default is “h”.


Source Filename Extension

  • sourceFilenameExtension (String, optional): Sets the filename extension for all source files. Default is “cpp”.

Tracing feature

The Tracing feature enables the generation of virtual tracing callback functions, which needs to be implemented.

Example:

feature Tracing {
    enterState = true
    exitState  = true
}


Enter State

  • enterState (boolean, optional): Specifies whether to generate a callback function that is used to notify about state-entering events.


Exit State

  • exitState (boolean, optional): Specifies whether to generate a callback that is used to notify about state-exiting events.

GeneralFeatures feature

The GeneralFeatures feature allows to configure additional services to be generated along with the state machine. Per default, all parameters are false, meaning to disable the corresponding features, respectively.

GeneralFeatures is an optional feature.

Example:

feature GeneralFeatures {
    timerService = true
    timerServiceTimeType = ""
}


Timer Service

  • timerService (Boolean, optional): Enables/disables the generation of a software timer service implementation.

Timer Service Type

  • timerServiceTimeType (String, optional): Sets the type for the sc_time identifier that is used throughout the Timer Service to sore the actual time that elapsed from start. All strings are accepted that represents a valid C type. The new sc_time is defined in the generated sc_types.h. The default value of sc_time is int32_t.

Generating Qt state machine code

The C++ code generator for Qt is based on the C++11 code generator. The concepts for the C++11 code generator described above also apply to the Qt code generator. The following differences are relevant:

  • Use the generator id create::cpp::qt in your sgen file to enable Qt specific code generation.
  • The generated code always applies smart pointers. So the option smartPointers can’t be set to false.
  • The generated timer service makes use of Qt timer classes.
  • The generated state machine classes inherit from QObject and can be used accordingly.