13.5 Argument Passing Considerations

The mapping of parameter passing modes attempts to balance the need for both efficiency and simplicity. For primitive types, enumerations, and object references,

the modes are straightforward, passing the type P for primitives and enumerations and the type A_ptr for an interface type A.

Aggregate types are complicated by the question of when and how parameter memory is allocated and deallocated. Mapping in parameters is straightforward because the parameter storage is caller-allocated and read-only. The mapping for out and inout parameters is more problematic. For variable-length types, the callee must allocate some if not all of the storage. For fixed-length types, such as a Point type

represented as a struct containing three floating point members, caller allocation is preferable (to allow stack allocation).

To accommodate both kinds of allocation, avoid the potential confusion of split allocation, and eliminate confusion with respect to when copying occurs, the mapping is T & for a fixed-length aggregate T and T* & for a variable-length T. This approach has the unfortunate consequence that usage for structs depends on whether the struct is fixed- or variable-length; however, the mapping is consistently T_var & if the caller uses the managed type T_var.

The mapping for out and inout parameters additionally requires support for deallocating any previous variable-length data in the parameter when a T_var is passed. Even though their initial values are not sent to the operation, the Oracle Tuxedo includes out parameters because the parameter could contain the result from a previous call. The provision of the T_out types is intended to give implementations the hooks necessary to free the inaccessible storage while converting from the T_var types. The following examples demonstrate the compliant behavior:

// IDL
struct S { string name; float age; };
void f(out S p);
// C++
S_var s;
f(s);
// use s
f(s);            // first result will be freed
S *sp;          // need not initialize before passing to out
f(sp);
// use sp
delete sp;     // cannot assume next call will free old value
f(sp);

The implicit deallocation of previous values for out and inout parameters works only with T_var types, not with other types:

// IDL
void q(out string s);
// C++
char *s;
for (int i = 0; i < 10; i++)
q(s);           // memory leak!

Each call to the q function in the loop results in a memory leak because the caller is not invoking string_free on the out result. There are two ways to fix this, as shown below:

// C++
char *s;
String_var svar;
for (int i = 0 ; i < 10; i++) {
    q(s);
    string_free(s);   // explicit deallocation
    // OR:
    q(svar);         // implicit deallocation
}

Using a plain char* for the out parameter means that the caller must explicitly deallocate its memory before each reuse of the variable as an out parameter, while using a String_var means that any deallocation is performed implicitly upon each use of the variable as an out parameter.

Variable-length data must be explicitly released before being overwritten. For example, before assigning to an inout string parameter, the implementor of an operation may first delete the old character data. Similarly, an inout interface parameter must be released before being reassigned. One way to ensure that the parameter storage is released is to assign it to a local T_var variable with an automatic release, as in the following example:

// IDL
interface A;
void f(inout string s, inout A obj);
// C++
void Aimpl::f(char *&s, A_ptr &obj) {
    String_var s_tmp = s;
    s = /* new data */;
    A_var obj_tmp = obj;
    obj = /* new reference */
}

For parameters that are passed or returned as a pointer (T*) or as a reference to a pointer (T*&), an application is not allowed to pass or return a NULL pointer; the result of doing so is undefined. In particular, a caller may not pass a NULL pointer under any of the following circumstances:

  • in and inout string
  • in and inout array (pointer to first element)

However, a caller may pass a reference to a pointer with a NULL value for out parameters, because the callee does not examine the value, but overwrites it. A callee may not return a NULL pointer under any of the following circumstances:

  • out and return variable-length struct
  • out and return variable-length union
  • out and return string
  • out and return sequence
  • out and return variable-length array, return fixed-length array
  • out and return any