Operations
Operations in UML2 are usually part of classes, as such the code generation works as follows.
For methods, a parameter is inserted which should point to an instance of the generated C structure. The default name of this parameter is me
(in C++, the object orientation support of the language is used, so this reference is called this). For method calls in activity or state machine diagrams, this parameter is automatically assigned to me
. For details how the name of an operation is generated, see the Naming chapter.
For static operations (functions), there is no such automatic parameter introduction. Find out more about Static Operations! For singletons, the situation is similar. Find out more about Singletons!
Naming
Naming of Operations is a very important topic for C-Code generation, especially if you use the generated code from hand-written code.
Keep the following simple rules in mind, and you won't have any problems with operation naming:
- If a method is defined by an interface (either directly, or because it's part of the realization of an interface), the method name will not be altered.
- If the class has the Stereotype - executable entry point and the classifier behavior points to the method, the method name will not be altered.
- Otherwise, the method name will be prepended by the class name and an underscore, e.g.
ClassName_MethodName
.
If you need a method to have the same name in C as in UML, you can just create an interface and "realize" it via a realization Dependency. It's good practice that if you depend on a name, it should be defined in an interface.
ClassName Prefix
You can use the model setting Suppress operation prefix to generate operations without the ClassName
prefix.
Parameters
Parameters can also be augmented with properties allowed by UML, Stereotypes and/or Tagged Values, but one of the most common issue regarding Parameters is on how to set pointers, constants and types and all their combinations, with all their different meanings.
Stackoverflow: Difference between const int, int const and const int pointer,...
We think the currently best/fastest/easiest solution to this issue is to use the a Primitive Element to set its name to any one of the needed combination and use this Primitive Element as the type for the Parameter.
Call Chain
For operation calls, the code generator tries to resolve the value of the parameters in the order of the call chain.
What is a call chain?
It's a succession of calls where one behavior just calls another which calls another and so on. The first behavior of a call chain is the caller, and the last behavior is the behavior for which code will be generated (the intermediate behaviors are not visible in the generated code), the callee.
Parameters must have the same name in every part of the call chain; the value can be defined by specifying the Default Value for that parameter, or by using parameter nodes in activities. If a parameters value is defined more than once in the call chain, the first definition is used and subsequent ones are ignored.
To define parameters for transition effects, where the effect is a behavior, the effect must either be written literally as code or an intermediate behavior without parameters must be introduced where you can define the parameter values.
Parameter Containment
When modeling operations for classes the containment of a parameter can be set by setting the Direction
to inout
.
This can also be achieved by adding a ReferenceType tagged value with the prefered reference type to the parameter.
The ReferencType
tagged value can also be used to overwrite/change the reference type generated by the inout Direction
.
Static Operations
Static operations get a special treatment in Embedded Engineer. While the generated code will be very similar to that of methods, keep the following differences in mind:
- Static operations have no access to an instance of the class they're defined in. There might not even be an instance if they are part of a static class.
- When you define static operations in singleton classes, you also don't have access to the instance.
Initialization Code
You can define an initialization method by giving an operation in your class the stereotype constructor
. If you do so, the method will be generated as usual, but it will also contain code to initialize the state machines and the default values of the class attributes.
Function Preambles
To fully define a function, it is sometimes neccessary to add code in front of the function implementation.
For example, if you want to define an interrupt handler, you might need to add a #pragma
directive or other macro invocation in front of the function.
If you want to add such a preamble, add the FunctionPreamble
tagged value to your operation. The text you add there will be written directly in front of the generated function implementation. You can also do this with the help of the Toolbox.
This will result in the following code (given that the return type is void
, there are no parameters, and the operation is static):
#pragma vector=TIMER0_A0_VECTOR
__interrupt void HandleTimerInterrupt(void)
{
}
Warning
What you define in the FunctionPreamble is written directly before the operation to enable the common use cases. Please take care when using this functionality and check that your FunctionPreamble is defined correctly.
Behavior
You can add behavior to an operation in two different ways:
- By adding verbatim code to an operation, which is inserted in the generated method body. This will result in a function which contains the code you type in the field Initial Code (check your Model Setting
User Code storage field
). No code will be added after your code, but the code generator might insert code neccessary to support the singleton pattern in front of the user supplied code.
- By referencing an activity which will be called when the operation is called. This will result in a function which calls the supplied activity. If the activity has a return type, and this type corresponds to the return type of the operation, a return statement will be added and therefore the return value will be propagated to the caller.
- Adding a behavior in the Method property of the operation.
- When clicking on the "edit" button you can either select or create a new behavior.
- Supported behavior types:
- OpaqueBehavior
- FunctionBehavior
- Activities
Warning
If multiple behaviors are defined only the first will be selected by the Code generation.
Function pointer
To generate a function pointer, multiple elements need to be generated and linked
- Generate a type definition for the function pointer
- Create an operation
- Add parameters
- Add the stereotype
typedef
- Generate a function pointer Type
- Create a
DataType
element - Add the
fncPointer
Stereotype - Set the
Signature
Tagged Value to reference thetypedef
Operation from step 1.
- Create a
- Now you can use the new
DataType
to define a function pointer for e.g.: an Attribute or a Parameter
Warning
We updated the way to define a function pointer with features other than only Attributes
.
The "old way" using the FunctionPointer
Stereotype is deprecated and will result in a code generation warning.
Generated code
/* Operation 'FncPointerDefinition' of class 'Class1' */
typedef void (*FncPointerDefinition)(int, byte, char);
typedef struct Class1Struct
{
FncPointerDefinition FncPointerAttr;
} Class1;