Constructors and destructors
IBM® Engineering Systems Design Rhapsody® automatically generates operations to create, initialize, clean up, and destroy objects. Object constructors include creators and initializer. Object destructors include cleanup and destroy operations.
Object creator
The object creation operation creates an object and calls its initializer. Its name has the
format <object>_Create()
.
The creator allocates memory for an object, calls the initializer for the object, and returns a pointer to the object created.
For example, the following creator is generated for the object A
:
A * A_Create() {
A* me = (A *) malloc(sizeof(A));
if(me!=NULL)
{
A_Init(me);
}
return me;
}
For reactive objects, a pointer to a task is added to the end of the argument list for the creator. This pointer tells the reactive object which thread (task) it is running on. For example,
A * A_Create(RiCTask * p_task) {
A* me = (A *) malloc(sizeof(A));
if(me!=NULL)
{
A_Init(me, p_task);
}
DYNAMICALLY_ALLOCATED(me);
return me;
}
Because in C it is not possible to give an argument a default value, you can pass NULL value for the task to cause the instance to run in the main task.
The C_CG::Class::AllocateMemory
property and the
C_CG::Event::AllocateMemory
property specify the string that is generated to
allocate memory dynamically for objects or events. This string is used in the Create() operation.
The default value of this property is:
($cname*) malloc(sizeof($cname));
In generated code, the $cname
keyword is replaced with the name of the object or
event for which memory is being allocated.
Dynamic memory allocation
You can create an object dynamically by calling its creator function. For example,
B *new_B;
new_B = B_create();
You can delete an object dynamically by calling its delete function. For example,
B_Destroy(new_B);
Object initializer
The initialization function initializes the attributes and links of an instance. The initializer
assumes that memory has previously been allocated for the object (either statically or dynamically).
The object initializer name has the format
<object>_Init()
.
For example, the following example is the prototype of the initializer that is generated for the
object A
:
void A_Init(struct A_t* const me);
The first argument is a constant pointer to the object being initialized. The
const
keyword defines a constant pointer in ANSI C. Passing a constant pointer as
an argument allows the operation to change the value of the object that the pointer addresses, but
not the address that the argument me
contains.
The object initializer has the following responsibilities, which it performs in the following order:
- Calls subobject initializer functions, if the object has subobjects.
- Sets links for association relations.
- Executes user code that is entered for the body of a constructor. This code must include initializations of the data for the object.
- Initializes aggregated framework objects (for example,
RiCTask
,RiCReactive
, andRiCMonitor
objects).
Subobject initialization includes calling the initializers for each subobject of a composite
object. In the case of arrays, the initialization of each subobject can include the
$index
keyword.
By default, the initializer has no arguments (other than the me
argument). If
you create an initializer with arguments, you can enter initial values for the arguments in the
Object window. Rhapsody
generates initialization code for initializers with arguments from the values that are entered in
the Object window.
Initializing subobjects
Compositions are initialized with a call to initRelations()
in the initializer
of the parent. For example, the following initializer is generated for an object D
that has a subobject E
:
void D_Init(D* const me) {
initRelations(me);
}
The initRelations()
call in D
's initializer calls the
initializer for E
:
static void initRelations(D* const me) {
E_Init(&me->E);
}
If subobjects are implemented as an array (for example, because the subobject has a numeric
multiplicity greater than one), the subobjects are initialized by using a while()
loop in the initRelations()
operation. For example, if E
's
multiplicity is two, E
is implemented as a two‑element array inside
D
. The following example of a while()
loop is generated in
D
's initRelations()
operation to initialize both instances of
E
:
static void initRelations(D* const me) {
E_Init(&(me->E));
{
RhpInteger iter = 0;
while (iter < 5){
E_Init(&((me->itsE)[iter]));
iter++;
}
}
}
Setting Links
If related objects are not components of a composite object, you can have the main program
instantiate one of the objects by selecting it as an initial instance (in the Initialization tab for
the configuration). In that initializer for the object, you can create the related object explicitly
and then set the link to it. For example, if an object A
and an object
B
are related and the main()
function instantiates
A
as an initial instance, then in the body of A
's initializer
that you can write the following code to set its link to B
:
B *itsB = B_Create();
A_setItsB(me, itsB);
Setting a link to a to‑many relation involves calling the initializer for the container. In the
following code, the call to RiCCollection_Init()
sets the
Furnace
's link to three itsRooms
. Passing a value of
RiCTRUE
to RiCCollection_setFixedSize()
says that the collection
is of fixed size:
void Furnace_Init(Furnace* const me, RiCTask * p_task) {
RiCReactive_init(&me->ric_reactive, (void*)me,
p_task, &Furnace_reactiveVtbl);
RiCCollection_Init(&me->itsRoom, 3);
NOTIFY_REACTIVE_CONSTRUCTOR(me, NULL, Furnace,
Furnace, Furnace(), 0, Furnace_SERIALIZE);
{
RiCCollection_setFixedSize(&me->itsRoom,
RiCTRUE);
}
initStatechart(me);
NOTIFY_END_CONSTRUCTOR(me);
}
The NOTIFY_CONSTRUCTOR()
and NOTIFY_END_CONSTRUCTOR()
calls are
instrumentation macros that are generated when animation is enabled. The first macro notifies the
animator when the initializer is called and creates an animation instance. The second macro notifies
the animator when the initializer is about to exit.
Executing User Initialization Code
User code that is entered for the constructor includes initializations of the attributes of the object. You can specify the actual value for every parameter in the object constructor. The actual value is inserted verbatim as uninterpreted text.
User code is generated between the /*#[
and /*#]
symbols in the
code. For example, you might enter the following code in the Implementation
field for the initializer:
RiCString temp;
RiCString_Init(&temp, "Hello World");
A_print(me, temp);
This code is implemented as follows:
void A_Init(struct A_t* const me) {
NOTIFY_CONSTRUCTOR(me, NULL, A, A, A(), 0,
A_SERIALIZE);
me->itsB = NULL;
{
/*#[ operation A() */
RiCString temp;
RiCString_Init(&temp, "Hello World");
A_print(me, temp);
/*#]*/
}
NOTIFY_END_CONSTRUCTOR(me);
}
Object cleanup and object destructor
The object cleanup operation performs complementary operations to the initializer, releasing the links in the object to other objects in reverse order.
The destruction operation destroys an object. Its name has the format
<object>_Destroy()
.
The Destroy()
operation calls the object's Cleanup()
operation to clean up its links, then frees up any memory that is allocated for the object.
For example, the Destroy()
operation is generated for the object
A
:
void A_Destroy(A* const me) {
if(me!=NULL)
{
A_Cleanup(me);
}
free(me);
}
The C_CG::Class::FreeMemory
property and the
C_CG::Event::FreeMemory
property specify the string that is generated to freeup
memory that was previously allocated for objects or events. This string is used in the Destroy()
operation. The default value of this property is:
free($meName);
In generated code, the $meName
keyword is replaced with the name of the object
or event for which memory is being freed.