next up previous contents
Next: Interface declarations in the Up: Contents Previous: Contents

Type declarations in the powerRPC IDL

 

The powerRPC IDL is an extension of the C type and function declarations. The IDL is made as close to C as possible, therefore almost every valid C declaration is also a valid powerRPC declaration, with a few exceptions. Function pointer is such an exception, it is an error to declare a pointer to function as a member of struct or union in powerRPC, for obvious reasons.

Primitive types

You can use any primitive types, such as

      char, short, int, long, float, double, enum
in the IDL. When applicable, you can also use the unsigned specifier before the types.

However, currently, powerRPC does not support non-standard types such as long long, long double. Anyway, you should avoid using such types for portability.

Pointers

In powerRPC IDL, a declaration of the form

            T * ptr;
is always taken as a pointer to a single object of type T. To declare ``pointer arrays", please refer to section 1.5.

Example:

      struct link_t {
           struct link_t * next;
           double val;
      };
struct link_t can be used as a singly linked list of doubles.

The powerRPC generated XDR function will recursively chase the pointers when encoding/decoding an object. For the above declaration, when next is not NULL, the generated function xdr_link_t() will be called for *next. This implies that we can not use self-referencing types such as double linked lists as RPC arguments, since the pointers form a circle, which would cause infinite recursion in the XDR function.

When use a pointer as an RPC argument, you must make sure that it is properly initialized. Otherwise, either a memory fault or a memory leak may occur.

Suppose you have an RPC function foo(out int * ptr) (where the out keyword declares that the argument is used to receive result from server), the following lines of code

         int * pi; // not initialized 
         foo(pi);
will probably cause a fault since a dangling reference is passed, while this line of code
         
        foo(0); // passed a NUL pointer
would results in a memory leak, even if the server implementation of foo() does not use the ptr argument at all.

struct

structs are very important in C, since it is the only way to support aggregated types. powerRPC IDL allows structs that consists of data fields of any types, such as primitive types, typedefs, pointers, arrays, structs, etc.

Array

In making an RPC, the function arguments are sent to the server, which resides in different address space, usually on a remote host. We want to minimize the data transfered to reduce the overhead. When the data to be sent is an array, we should only send those needed elements. In powerRPC, when an argument is declared as char msg[1024] with a constant 1024 as the array length, exactly 1024 characters will be sent to/from the server. To tell powerRPC to be more efficient, we can define the size attribute for a fixed array, as shown below,

    struct msg_t 
    {
       int len;
       char msg[size=len, 1024];
    };
where we specify that the number of needed elements in array msg is len.

Or even this,

    struct msg_t2 
    {
       char msg[size = strlen(msg)+1, 1024];
    };
The size attribute is used only during the code generation of the XDR routines, the generated header file contains the C declaration with the size = expression part stripped out.

One important restriction on the expression used for the size attribute: one can not use global variables. This is because the XDR routines are used by both the server and client symmetrically, a variable in one address space is different from one in another address space.

PowerRPC does not recognize declaration of extern variables or functions, and it does not check the type of the expression used for the size attribute.

pointer array

  The C declaration of a pointer is ambiguous. A char * means (at least) two different things, 1) a pointer to a single char, or 2) a pointer to the first element of an array. A C programmer, of course, had decided the meaning in his mind when he wrote the code. However, powerRPC (or a maintainer of other people's code too?) is not able to guess this implicit meaning. The C programmer usually writes a comment such as ``this is a string", so the reader of his code knows what a char* really means. We must do the same thing for powerRPC.

In powerRPC IDL, a T* will always be interpreted as a pointer to a single object of type T. To declare a ``pointer array", you must used the following syntax,

Notice the position of ``[size_attrib]" is before the identifier ptr. The C declaration corresponding to the above is simply T *ptr, obtained by replacing the ``[size_attrib]" before the identifier ptr with a *.

We have mentioned about the size attributes for fixed arrays. For ``pointer array", we should have an additional attribute, maxsize to specify the maximum number of elements, using the maxsize = expression syntax within the subscript operator []. You may also specify the maximum size with a integer constant, without using the maxsize = expression syntax. Example:

struct msg_t2 {
       int  maxlen;
       char [size = (msg? strlen(msg)+1 : 0), maxsize=maxlen] msg;
       char [256] msg2;
       char msg3[256];
};

Notice that we did not specify the size attribute for msg2, in this case powerRPC will take the maxsize, which is 256, as the size. However, you must specify at least one of the sizes for an array, otherwise an error will be reported by powerRPC.

The C declaration of the above is

     struct msg_t2 {
         int maxlen;
         char * msg;
         char * msg2;
         char msg3[256];
     };

It is your responsibility of to make sure that msg and msg2 are initialized and pointing to allocated memory before using it as RPC arguments. You must be very careful, or a memory fault will occur, for example, the declaration claims member msg2 is an array of 256 elements, so powerRPC will try to faithfully deliver 256 chars. If msg2 was a NULL pointer, a fault would be inevitable.

typedef

Typedefs are very useful in powerRPC. A generated XDR function for type T is always of the following prototype,

                int xdr_T(XDR*, T* pT);
i.e., it takes only two arguments, the first is a pointer to an XDR object, and the second is the pointer to the object(of type T) to be Marshalled. To encapsulate the size information of an array in an XDR function, we need to use typedef.

For example, we can define an array of 512 chars,

 typedef char c_arr512[512];

To define a C string (char*) with a maximum length of 1023,

 typedef char [size = strlen(*this)+1, 1024] str1024;
here we used the powerRPC keyword this, which can be used in a type declaration (typedef, struct or union) to signify the pointer pT passed to the XDR function of the typedef.

The corresponding C declaration for str1024 is simply char *, however, the elaborate size specification above actually defines str1024 as a C string. In powerRPC, instead of being provided a fixed number of predefined types such as string8 (meaning C string), you can use its expressive IDL to defined your own types. Suppose you need to declare an array of long integers ending with a zero, you can do it yourself as follows,

        typedef long [size =strlen32(*this)+1, 1024] string32;
provided that you define the strlen32() function somewhere like this:
        int strlen32(long*la) {
             int i;
             for(i=0; la[i]; i++);
             return i;
         }

discriminated unions

Union is a space saver construct.

A discriminated union must be declared in a struct, and must use an integral expression as the discriminator. The C switch statement syntax is used to select from the choices, as shown in this example,

         struct primitive_t 
         {
              char choice;
              union switch( choice ) 
              {
               case 'i': int ival;
               case 'c': char cval;
               case 'd': double dval;
              } value;
         };
A discriminated union must be defined without a tag name, to prevent it from being used outside of the struct.

The corresponding C declaration of the above is,

         struct primitive_t 
         {
              char choice;
              union  
              {
                    int ival;
                    char cval;
                    double dval;
              } value;
         };

To use the type primitive_t, you must assign the choice field, and the corresponding union member, as shown in the following example,

        struct primitive_t aprim;
        /* we are using it as a double */
        aprim.choice = 'd';
        aprim.value.dval   = 9.9 ;

        /* now we can use aprim in an RPC */
Sometime we may want to use a union to represent optional data. To do this, we simply set the discriminator to a case not listed in the ``switch statement". Thus, if we set the choice to a undefined case,
        aprim.choice = -1;
no data will be transfered when aprim is later used in an RPC argument.

A usual C union declaration is also allowed. However, it will be treated as opaque data in powerRPC, that is the raw bytes (non-portable) of the union will be transfered across the network.

multi-dimensional arrays

Multi-dimensional arrays or pointer arrays can be used in powerRPC, however, only the size of the first dimension can be a variable. To use multi-dimensional arrays with variable sizes at second dimension and above, you can use typedefs.

For example, we can use the str1024 to define a array of strings.

      struct string_array 
      {
             int len;
             str1024 [size=len] strArray;
      };


next up previous contents
Next: Interface declarations in the Up: Contents Previous: Contents

Copyright (C) Netbula LLC, 1996