Often, the models created with QM are intended for entire families of related products (product lines), as opposed to a single end-product only. The designers of such product lines often find that big pieces of the model, such as state machines, can be similar in all individual products, except for some parts that vary.
One popular way to manage the variations at the C/C++ code level is through conditional compilation, meaning that blocks of code are surrounded by the preprocessor statements #if<expr>
/ #endif
and are actually compiled only when the preprocessor expression <expr>
evaluates to true at compile time.
QM inherently supports inserting any user code into file-templates, which means that you can very easily surround blocks of code of your choice (including the Code-Generation Directives ) with conditional compilation statements.
However, to achieve a fine-granularity conditional compilation inside classes you need to use the special support for conditional compilation built-into QM. This section describes how to add conditional compilation statements for individual attributes, operations, states, and transitions, including regular transitions, internal transitions, transitions with guards, and CATCH_ALL
transitions.
qpc
, qpcpp
, and qpn
) as well as all state machine types (subclasses of QMsm
and QHsm
base classes in QP5).To mark a state or a transition as conditionally compiled, you need to append a question mark (?
) followed by the preprocessor expression to the name of the attribute, state, or the transition in the Property Editor. The following two screen shots provide examples for a conditional state and conditional transition, respectively. The highlighted text after the state name and transition trigger, respectively, corresponds to the pre-processor expression.
The conditional expression that you add to the name property of a state or transition can be any valid expression accepted by the C/C++ pre-processor (excluding the ?
, obviously).
For example, a conditional compilation expression ?def FOO_BAR
will lead to the generation of the following pre-processor statements around a declaration or definition of a state:
if
, so that statements of the form #ifdef FOO_BAR
and #ifundef FOO_BAR
can be easily generated from the conditions ?def FOO_BAR
and ?undef FOO_BAR
, respectively.However, your pre-processor expressions might be also more complex and might include logical operators &&
, ||
, etc. For example, the condition ? (defined FOO_BAR) && (VERSION >= 531)
will will lead to the generation of the following pre-processor statements:
As shown in the screen shots below, the QM™ tool will give you a visual indication by displaying the question mark (?
) in front of the state name or the transition trigger in the State Diagram as well as in the Model Explorer :
The QM™ code generator applies the conditional compilation consistently and in a way that most developers should find intuitive.
For example, for a conditionally-compiled state, both the state declaration and state definition will be surrounded by the corresponding #if<expr>
/ #endif
statements. Similarly, all substates of a conditionally-compiled state will be surrounded by the same #if<expr>
/#endif
statements as the superstate. The QM code generator will even correctly nest the #if
/#endif
statements for nested states, as one would expect. (NOTE: The following sub-sections describe how conditional compilation is applied in various special cases).
This section describes the special cases of conditionally-compiled states.
If both a superstate and some of its substates are conditionally compiled (each with their own conditional expressions), the QM code generator will correctly nest the #if
/#endif
statements around the substates.
For example, if a superstate state1
with a condition ?def FOO_BAR
has a substate state2
with a condition ?(VERSION > 531)
, QM will generate the following conditional compilation statements for state1
and state2
.
If a conditionally compiled state has a history, the QM code generator will correctly surround the declaration and the initialization of the state history variable. This will prevent allocating space and thus wasting memory (RAM) for the history attribute, in case the corresponding state is "compiled out".
For example, if state1
with a condition ?def FOO_BAR
has a history connector, QM will generate the following conditional compilation statements around the declaration and initialization of the his_state1
history attribute.
A conditional compilation condition appended to the trigger name works for all transition types supported by QM, including regular transitions, internal transitions, transitions with guards, multi-trigger transitions, and CATCH_ALL
transitions. The following sub-sections describe the special cases for conditionally-compiled transitions.
Due to the way transitions are implemented in QP, there is no declaration of transition, so the conditional compilation applies only to the definition of a transition inside a state-handler function. The following snippet of code shows the conditional transition TRIG2
with the condition ? (defined FOO_BAR) && (VERSION >= 531)
at line 6:
if
/#endif
preprocessor statements will naturally nest, which will produce the expected result. (A transition will only be "compiled in" when both the conditional expression for the sate as well as the actual conditional expression for the transition evaluate to true).A multi-trigger transition is a transition labeled with a (comma-separated) list of triggers, any one of which can trigger the transition. The conditional compilation works for this type of transition as well. In that case, you simply append the list of triggers with the question-mark (?
) and the pre-processor expression. The following screen shot illustrates the situation:
The special CATCH_ALL transition is triggered by any not-explicitly handled trigger by a given state (so it "catches" all triggers). The conditional compilation works for this type of transition as well. In that case, you simply append the special CATCH_ALL
pre-defined trigger with the question-mark (?
) and the pre-processor expression. The following screen shot illustrates the situation:
Due to the CATCH_ALL transitions are implemented in QP, the preprocessor #if
statement has also the #else
branch in that case, so that there is no "catch all" behavior when such transition is "compiled out". The following code snippet illustrates the conditional transition in a QMsm
sub-class (please note the #else
conditional branch at line 23):
The following code snippet illustrates the conditional transition in a QHsm
sub-class (please note the #else
conditional branch at line 31):
Next: QM Compiler (qmc)