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:

  1. Calls subobject initializer functions, if the object has subobjects.
  2. Sets links for association relations.
  3. Executes user code that is entered for the body of a constructor. This code must include initializations of the data for the object.
  4. Initializes aggregated framework objects (for example, RiCTask, RiCReactive, and RiCMonitor 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.