PreviousNext

Pointer Attributes

Pointers are used for several purposes, including implementing a parameter passing mechanism that allows a data value to be returned, and building complex data structures.

IDL offers three classes of pointers: reference pointers, full pointers, and unique pointers.

The attributes that indicate these pointers are as follows:

· ref: Indicates reference pointers. This is the default for top-level pointers used in parameters.

· ptr: Indicates full pointers.

· unique: Indicates unique pointers.

Pointer attributes are used in parameters, in structure and union members, and in type definitions. In some instances, IDL infers the applicable pointer class from its usage. However, most pointer declarations require that you specify a pointer class by using one of the following methods:

· Use the ref, ptr, or unique attribute in the pointer declaration.

· Use the pointer_default attribute in the IDL interface heading. The default pointer class is determined by the pointer_default attribute.

Pointer attributes are applied only to the top-level pointer within the declaration. If multiple pointers are declared in a single declaration, the pointer_default established applies to all but the top-level pointer. (See Pointer Attributes in Parameters.)

Examples of pointers are shown in Examples Using Pointers.

Reference Pointers
A reference pointer is the less complex form of pointer. The most common use for this class of pointer is as a passing mechanism; for example, passing an integer by reference. Reference pointers have significantly better performance than full pointers, but are restrictive; you cannot create a linked list using a reference pointer because a reference pointer cannot have a NULL value, and the list cannot be terminated.

A reference pointer has the following characteristics:

· It always points to valid storage; it can never have a NULL value.

· Its value does not change during a call; it always points to the same storage on return from the call as it did when the call was made.

· It does not support aliasing; it cannot point to a storage area that is pointed to by any other pointer used in a parameter of the same operation.

When a manager routine is entered, all the reference pointers in its parameters will point to valid storage, except those reference pointers that point neither to targets whose size can be determined at compile time nor to values that have been received from the client.

In the following example, the size of the targets of the reference pointers can be calculated at compilation time:

typedef [ref] long *rpl;

void op1( [in] long f,

[in] long l,

[in,first_is(f),last_is(l)] rpl rpla[10] );

For this example, when the manager is entered, all the pointers in rpla will point to usable storage, although only *rpla[f] through *rpla[l] will be the values received from the client.

Conversely, the size of the targets of the reference pointers cannot be calculated at compile time in the following example:

typedef [ref,string] char *rps;

void op1( [in] long f,

[in] long l,

[in,first_is(f),last_is(l)] rps rpsa[10] );

In this case, only rpsa[f] through rpsa[l], which point to values received from the client, will point to usable storage.

Full Pointers
A full pointer is the more complex form of pointer. It supports all capabilities associated with pointers. For example, by using a full pointer you can build complex data structures such as linked lists, trees, queues, or arbitrary graphs.

A full pointer has the following characteristics:

· Its value can change during a call; it can change from a NULL to non-NULL value, non-NULL to NULL, or from one non-NULL value to another non-NULL value.

· It supports aliasing; it can point to a storage area that is also pointed to by any other full pointer used in a parameter of the same operation. However, all such pointers must point to the beginning of the structure. There is no support for pointers to substructures or to overlapping storage areas. For example, if the interface definition code contains the following:

[uuid(0e256080-587c-11ca-878c-08002b111685), version(1.0)]
interface overlap
{
typedef struct {
long bill;
long charlie;
} foo;
typedef struct {
long fred;
foo ken;
} bar;
void op ( [in] foo *f, [in] bar *b );
}

and the client application code includes:

bar bb;

.
.
op ( &bb.ken, &bb );

then the server stub treats these two separate parameters as distinct, and the manager application code does not see them as overlapping storage.

· It allows dynamically allocated data to be returned from a call.

Note that you might need to take some extra steps if you use large linked lists in your application. Linked lists are marshalled and unmarshalled using recursion which can cause the stack size to grow. Linked lists usually do not cause problems in simple clients that do not spawn threads for remote procedure calls. In this case, the stack can grow as needed.

Large linked lists can cause problems in servers because the server's thread-stack usually cannot grow automatically. Large lists can overrun the stack, causing the server to crash.

DCE offers several ways to avoid this server memory problem while using large linked lists.

One method is to increase the server stack size using the rpc_mgmt_set_server_stack_size( ) routine. This method is useful when you suspect that the linked list is just slightly larger than the server stack. For information about using the rpc_mgmt_set_server_stack_size( ) routine, refer to the OSF DCE Application Development Reference.

If you suspect that the list size is much greater than the stack, you can convert the list to an array using the transmit_as idl attribute. Servers handle arrays by allocating memory from the heap rather than from the stack. For information about using the transmit_as idl attribute, refer to the topic entitled Attribute Confirmation Language.

Unique Pointers
A unique pointer is more flexible than a reference pointer. However, both types of pointers share several important characteristics.

· A unique pointer has the following characteristics:

· It can have a NULL value.

· It can change from NULL to non-NULL during a call. This change results in memory being allocated on return from the call, whereby the result is stored in the allocated memory.

· It can change from non-NULL to NULL during a call. This change can result in the orphaning of the memory pointed to on return from the call. Note that if a unique pointer changes from one non-NULL value to another non-NULL value, the change is ignored.

· It does not identify particular extents of memory, but only extents of memory that are suitable for storing the data. If it is important to know that the data is being stored in a specific memory location, then you should use a full pointer.

· If it has a value other than NULL, output data is placed in existing storage.

Unique pointers are similar to reference pointers in the following ways:

· No storage pointed to by a unique pointer can be reached from any other name in the operation. That is, a unique pointer does not allow aliasing of data within the operation.

· Data returned from the called subroutine is written into the existing storage specified by the unique pointer, if the pointer did not have the value NULL.

With regard to performance, unique pointers have an advantage over full pointers because unique pointers do not support the referencing of common data by more than one pointer (aliasing), and they are significantly more flexible than reference pointers because they can have a value of NULL.

Unique pointers are particularly suitable for creating optional parameters (because you can specify them as NULL) and for simple tree or singly linked-list data structures. You specify the three different levels of pointers by attributes, as follows:

[ref] reference pointers
[unique] unique pointers
[ptr] full pointers
The following example shows how a unique pointer can be used:

[

uuid(D37A0E80-5D23-11C9-B199-08002B13D56D)

] interface Unique_ptrs

{

typedef [ref] long *r_ptr;

typedef [unique] long *u_ptr;

typedef [ptr] long *f_ptr;

void op1 (

[ref,in,out,string] char *my_rname,

[unique,in,out,string] char *my_uname,

[ptr,in,out,string] char *my_pname

);

}