Go to the previous, next section.

Using ILU with ANSI C

Introduction

This document is for the ANSI C programmer who wishes to use ILU. The following sections will show how ILU is mapped into ANSI C constructs and how both ANSI C clients and servers are generated and built.

Using ILU with ANSI C is intended to be compatible with the OMG CORBA specification. That is, all of the naming and stub generation comply with the Common Object Request Broker Architecture, either revision 1.1 or 1.2, defaulting to 1.2. (8)

Note that ILU does not support non-ANSI variants of the C language. In particular, it relies on having prototypes, all ANSI C library functions, and the capabilities of the ANSI C pre-processor.

When functions are described in this section, they are sometimes accompanied by locking comments, which describe the locking invariants maintained by ILU on a threaded system. See the file `ILUSRC/runtime/kernel/iluxport.h' for more information on this locking scheme, and the types of locking comments used.

A number of macros are used in function descriptions, to indicated optional arguments, and ownership of potentially malloc'ed objects. The macro OPTIONAL(type-name) means that the value is either of the type indicated by type-name, or the value NULL. This macro may only be used with pointer values. The macro RETAIN(type-name) indicates, when used on a parameter type, that the caller retains ownership of the value, and when used on a return type, that the called function retains ownership of the value. The macro PASS(type-name) indicates, when used on a parameter type, that the caller is passing ownership of the storage to the called function, and when used on a return type, that the called function is passing ownership of the called value to the caller. The macro GLOBAL(type-name) means that neither the caller nor the calling function owns the storage.

The ISL Mapping to ANSI C

Names

In general, ILU constructs ANSI C names from ISL names by replacing hyphens with underscores. Type names and class names are prepended with their interface name. For example, for the ISL type T-1 in interface I, the generated name of the ANSI C type would be I_T_1.

Enumeration value names are formed by prepending the interface name and "_" to the ISL enumeration value name. Enumeration names and values are then cast into ANSI C enum statements.

Constant names are prepended with their interface name. They are implemented with the const declaration statements.

Method name prefixes are specified by CORBA to be module-name_interface-name. ANSI C function names for ISL methods are composed of the generated class name prepended to the method name. For example, if the interface name is X and the class type name is Y and the ISL method name is Z then the ANSI C callable method name will be X_Y_Z. ILU ANSI C servers for this method must implement a function called server_X_Y_Z.

For field names within records, hyphens are replaced with underscores.

Mapping Type Constructs Into ANSI C

Records

Records map directly into corresponding ANSI C structures.

Unions

Because of the somewhat baroque CORBA specification, unions may take one of several forms.

Generally, ILU unions in ANSI C consist of a record with two fields: the type discriminator, a field called "_d", and a union of the possible values. Since ILU does not name the fields of a union, the union field names are derived from the ILU data types which compose the union. For example, if the ILU type in interface I is TYPE u1 = UNION INTEGER, SHORT REAL END; the generated ANSI C struct would be

typedef struct _I_u1_union I_u1;
enum I_u1_allowableTypes {
  I_u1_integer,
  I_u1_shortreal
};
struct _I_u1_union {
  enum I_u1_allowableTypes _d;
  union {
    ilu_integer integer;
    ilu_shortreal shortreal;
  } _u;
};

Note the discriminator _d may take on the values of I_u1_integer or u_u1_shortreal indicating how to interpret the data in the union. Also note how the enumerated names are formed: with the interface name and the type name prepended to the enumeration element name.

In more complex union forms, the user may specify the type of the discriminator as well as the field names and which field corresponds to which discriminator value. Consider the following ISL example:

INTERFACE I;
TYPE e1 = ENUMERATION red, blue, green, yellow, orange END;
TYPE u1 = e1 UNION 
 a : INTEGER = red, green END,
 b : SHORT REAL = blue END,
 c : REAL
END;

The generated union is:

typedef struct _I_u1_union I_u1;
typedef enum {
  I_red = 0, 
  I_blue = 1, 
  I_green = 2, 
  I_yellow = 3, 
  I_orange = 4
} I_e1;
struct _I_u1_union {
  I_e1 _d;
  union {
    ilu_integer a;
    ilu_shortreal b;
    ilu_real c;
  } _u;
};

This example shows that the discriminator type is to be I_e1 and that the field names are to be a, b, and c. When the discriminator has the value I_red or I_green the field a has a valid value and the type is interpreted to be integer. When the discriminator has the value I_green the field b has a valid value and the type is interpreted to be shortreal. If the discriminator has any other value, the field c is expected to have a valid value and the type is interpreted to be ilu_real (double).

Discriminator types may be INTEGER, ENUMERATION, or SHORT INTEGER. The default for an unspecified discriminator is SHORT INTEGER.

Floating Point Values

The ISL SHORT REAL primitive type maps to the ANSI C float data type while REAL maps to double. The ISL LONG REAL primitive type currently doesn't map to anything real.

Sequences

Sequence type names, as most type definitions, are formed with the interface name and the type name. Sequence instances are represented to the ANSI C programmer as a pointer to the sequence descriptor structure. For each sequence type declared in the interface description, a pseudo-object sequence type is defined in ANSI C. These sequence types will hold any number of values of type sequence's primary type. For the sequence
INTERFACE I;
TYPE T2 = SEQUENCE OF T1;
the following functions are defined:

[ANSI C]: I_T2* I_T2_Create ( OPTIONAL(unsigned long) length, OPTIONAL(T1 *) initial-values )

This function creates and returns a pointer to an instance of T2. If length is specified, but initial-values is not specified, enough space for length values of type T1 is allocated in the sequence. If initial-values is specified, length is assumed to be the number of values pointed to by initial-values, and must be specified.

[ANSI C]: void I_T2_Append ( I_T2 * s, T1 value )

Appends value to the end of s.

[ANSI C]: void I_T2_Push ( I_T2 * s, T1 value )

Pushes value on to the beginning of the sequence.

[ANSI C]: void I_T2_Pop ( I_T2 * s, T1 * value-ptr )

Removes the first value from the sequence s, and places it in the location pointed to by value-ptr.

[ANSI C]: void I_T2_Every ( I_T2 * s, void (*func)(T1, void *), void * data )

Calls the function func on each element of s in sequence, passing data as the second argument to func.

[ANSI C]: void I_T2_Init ( I_T2 * s, OPTIONAL(unsigned long) length, OPTIONAL(T1 *) initial-values )

This function works like T2_Create, except that it takes a the address of an already-existing T2 to initialize. This can be used to initialize instances of T2 that have been stack-allocated.

[ANSI C]: void I_T2__Free ( I_T2 * s )

Frees allocated storage used internally by s. Does not free s itself.

All sequence types have the same structure, mandated by CORBA:

typedef struct I_T2 {
  unsigned long _maximum;
  unsigned long _length;
  long *_buffer;
} I_T2;

The field _maximum contains the number of elements pointed to by _buffer. The field _length indicates the number of valid or useful elements pointed to by _buffer.

For example, the ISL specification

INTERFACE I;
 
TYPE iseq = SEQUENCE OF INTEGER;
would have in its ANSI C mapping the type
typedef struct I_iseq {
  unsigned long _maximum;
  unsigned long _length;
  ilu_integer *_buffer;
} I_iseq;
In a client program, a pointer to this type would be instantiated and initialized by calling the type specific sequence creation function generated for the sequence, e.g.
        ...
    I_O h;
    ILU_C_ENVIRONMENT s;
    I_iseq *sq;
        ... 
    sq = I_iseq_Create( 0, NULL );
    I_iseq_Append (sq, 4);
        ...

Objects and Methods

As indicated earlier, method names are generated by prepending the interface name and the class name to the method name. The first argument to a method is an object instance. The object instance is an opaque pointer value returned from a class specific constructor function. All object types are subtypes for the type defined by ILU_C_OBJECT, a macro which expands to the appropriate CORBA object type for the version of CORBA being used. CORBA also specifies that the type of the handle be called interface-name_type-name. A typedef of the CORBA-specified name to the ILU_C_OBJECT type is therefore generated for each object type. In the example above, the type of the object instance would be I_O.

Two binding procedures are specified for each object type. A binding procedure is a procedure that takes some name for an object instance, and returns the actual instance. Users of a module typically use a surrogate-side binding procedure, which takes the string binding handle of the object, and the most specific type ID of the object's type (if known). Suppliers of a module typically bind objects with a creation procedure, which takes an instance ID, a server on which to maintain the object, and arbitrary user data, and creates and returns the true instance of the object.

In general, for any object type T, the following ANSI C functions are defined:

[ANSI C]: OPTIONAL(T) T__CreateTrue ( OPTIONAL(RETAIN(char *)) instance-id, OPTIONAL(GLOBAL(ilu_Server)) server, OPTIONAL(PASS(void *)) user-data )

Creates a true instance of type T, exporting it with instance-id instance-id, exporting it via server server-id, associating the value user-data with it. If instance-id is not specified, a server-relative instance-id will be assigned automatically. If server is not specified, the value of ILU_C_DefaultServer will be used, if bound. If server is not specified, and ILU_C_DefaultServer is not specified, a NULL pointer will be returned.

[ANSI C]: OPTIONAL(T) T__CreateFromSBH ( RETAIN(char *) sbh, OPTIONAL(char *) most-specific-type-ID)

Finds or creates an instance of T, using the instance-id and server-id specified in sbh, and the type specified by most-specific-type-ID.(9) If most-specific-type-ID is not specified, the most specific ILU type of T is used as the putative type. (The putative type is the most specific type of which the object instance may be assumed to be, even though the actual type of the instance may actually be a subtype of that type.)

[ANSI C]: extern ilu_Class T__MSType

A value of type ilu_Class which identifies the most specific ILU type of the type T.

In the following example, the ILU definition is:

INTERFACE I;
 
TYPE T = OBJECT
  METHODS
    M ( r : REAL ) : INTEGER
  END;

This definition defines an interface I, an object type T, and a method M. The method M takes a REAL as an argument and returns an INTEGER result. The generated ANSI C header file would include the following statements:

typedef ILU_C_OBJECT I_T;

I_T I_T__CreateTrue (ilu_string, ilu_Server server, void *user_data);
I_T I_T__CreateFromSBH (char *sbh, char *mostSpecificTypeID);

ilu_integer I_T_M (I_T, ILU_C_ENVIRONMENT *, ilu_real);

The functions I_T__CreateTrue and I_T__CreateFromSBH are used to create instances of the class I_T. I_T__CreateTrue is used by servers while I_T__CreateFromSBH is used by clients. The pointer returned in each case is the object instance and must be passed with each method invocation.

In addition to its specified arguments, the method I_T_M takes an instance of the type I_T and a reference to a variable of type ILU_C_ENVIRONMENT *, which is a macro defined to be the appropriate CORBA environment type, and is used to return exception codes. The environment struct pointed to by the environment argument must be instantiated in a client; its address is passed as the second argument to each method. True procedures must expect a pointer to this structure as the second argument. Finally, the ANSI C client calling the method for M might be as follows:

#include "I.h"
 
main (int ac, char **av)
{
  double atof( );
  I_T inst;
  int xx;
  double f;
  ILU_C_ENVIRONMENT ev;
 
  I__Initialize( );
  f = atof (av[1]);
  inst = I_T__CreateFromSBH (av[2], NULL);
  xx = I_T_M (inst, &ev, f);
  if (ILU_C_SUCCESSFUL(&ev))
    printf( "result is %d\n", xx );
  else
    printf( "exception <%s> signalled on call to I_T_M\n",
      ILU_C_EXCEPTION_ID(&ev));
}

Note the call on the interface-specific client initialization procdedure I__Initialize; these are described in a later section.

In this example, the string binding handle is obtained from standard input along with some floating-point value.

The class specific function I_T__CreateFromSBH is called to obtain the object instance. This function was passed the string binding handle, and a NULL pointer. specific function. The returned object instance is then passed as the first argument to the method I_T_M, along with the environment ev, and the single actual ilu_real argument f. I_T_M returns an ilu_integer value which is placed in xx.

The true implementation of the method M might be as follows:

ilu_integer server_I_T_M ( I_T h, ILU_C_ENVIRONMENT s, ilu_real u )
{
  return( (ilu_integer) (u + 1) );
}

In this simple example, the corresponding server, or true, method computes some value to be returned. In this case it adds one to its ilu_real argument u, converts the value to an integer, and returns that value. Note that the server method, if not signalling any exceptions, may ignore the environment parameter.

Inheritance

Through inheritance, an object type may participate in the behaviors of several different types that it inherits from. These types are called ancestors of the object type. In ANSI C, an object type supplies all methods either defined directly on that type, or on any of its ancestor types.

Consider the following example:

INTERFACE I2;
 
EXCEPTION E1;

TYPE T1 = OBJECT
  METHODS
    M1 (a : ilu.CString) : REAL RAISES E1 END
  END;

TYPE T2 = OBJECT
  METHODS
    M2 ( a : INTEGER, Out b : INTEGER )
  END;
 
TYPE T3 = OBJECT SUPERTYPES T1, T2 END
  METHODS
    M3 ( a : INTEGER )
  END;

The class T3 inherits from the class T2. Thus, five ANSI C methods are generated for the interface I2: I2_T1_M1, I2_T2_M2, I2_T3_M1, I2_T3_M2, and I2_T3_M3. A module that implements true instances of T3 would have to define all five true methods.

Object Implementation

This information is provided for those interested in the implementation of the ANSI C object system. It is not guaranteed to remain the same from release to release.

Each object type is represented by a TypeVector, which is a vector of pointers to MethodBlock structs, one for each component type of the object type, ordered in the proper class precedence for that object type. Each MethodBlock struct contains a ilu_Class value, followed by a vector of pointers to the methods directly defined by that ilu_Class. There are two different TypeVectors for each object type, one for the surrogate class of the type, and the other for the true class of the type. The TypeVector for the surrogate class uses the MethodBlocks of its supertypes; the TypeVector for the true class uses its own MethodBlocks for both direct and inherited methods, as true classes in the ANSI C implementation override all of their methods. The TypeVectors, and MethodBlocks for true classes, are not exported; the MethodBlocks for surrogate classes are, as they are used by their subclasses.

For each method directly defined in the type, a generic function is defined in the common code for its interface, which dispatches to the appropriate method. It does this by walking down the TypeVector for the object, till it finds a MethodBlock which contains the appropriate ilu_Class on which this method is directly defined), then calling the method pointer which is indexed in the MethodBlock's vector of method pointers by the index of the method. The generic functions have the correct type signature for the method. They can be referenced with the & operator.

Exceptions

ANSI C has no defined exception mechanism. As already indicated, exceptions are passed in ILU ANSI C by adding an additional status argument to the beginning of each method which contains an exception code, and a union of all the possible exception value types defined in the interface. Method implementations set the exception code, and fill in the appropriate value of the union, to signal an exception.

In the following example, the div method can raise the exception DivideByZero:

INTERFACE calc;
 
TYPE numerator = INTEGER;
 
EXCEPTION DivideByZero : numerator;
 
TYPE self = OBJECT
  METHODS
    Div( v1 : INTEGER, v2 : INTEGER ) : INTEGER RAISES DivideByZero END
  END;

The generated include file, calc.h contains the exception definitions:

#ifndef __calc_h_
#define __calc_h_
/*
** this file was automatically generated for C
** from the interface spec calc.isl.
*/
 
#ifndef __ilu_c_h_
#include "ilu-c.h"
#endif
 
extern ILU_C_ExceptionCode    _calc__Exception_DivideByZero;
#define ex_calc_DivideByZero _calc__Exception_DivideByZero
 
typedef ilu_integer calc_numerator;
typedef calc_numerator calc_DivideByZero;
 
typedef ILU_C_OBJECT calc_self;
 
calc_self calc_self__CreateTrue ( char *id, ilu_Server server,
   void * user_data);
calc_self calc_self__CreateFromSBH ( char * sbh, char * mstid );

ilu_integer calc_self_Div( calc_self, ILU_C_ENVIRONMENT *,
   ilu_integer, ilu_integer );

#endif 

The method implementation for Div in the true module must detect the divide-by-zero condition and raise the exception(10):

long server_calc_self_Div (calc_self h, ILU_C_ENVIRONMENT *s, ilu_integer u, ilu_integer v)
{
  calc_numerator n = 9;

  if ( v == 0 )
    {
      s->returnCode = ex_ilu_ProtocolError;
      s->_major = ILU_C_USER_EXCEPTION;
      s->ptr = (void *) malloc(sizeof(calc_numerator));
      *((calc_numerator *) (s->ptr)) = n;
      s->freeRoutine = (void (*) (void *)) free;
      return( u );
    }
  else
    return( u / v );
}

The exception is sent back to the client, which can detect it thusly:

  ...
  calc_self instance;
  ILU_C_ENVIRONMENT s;
  ilu_integer i, j;
  ilu_integer val;
  ...
  instance = calc_self__CreateFromSBH (sbh, NULL);

  val = calc_self_Div (instance, &s, i, j);

  /* check to see if an exception occured */

  if (! ILU_C_SUCCESSFUL(&s)) {
    /* report exception to user */
    char *p;

    p = ILU_C_EXCEPTION_ID (&s);

    if (p == ex_calc_DivideByZero) {
      calc_numerator *ip;
      ip = (calc_numerator *) ILU_C_EXCEPTION_VALUE (&s);
      fprintf (stderr, "%s signaled:  numerator = %d\n", p, *ip);
      }
    else {
      /* odd exception at this point */
      fprintf (stderr, "Unexpected <%s> on call to Div.\n", p);
      }
    /* free up any transient exception data */
    ILU_C_EXCEPTION_FREE (&s);
    }
  else {
    /* no exception - print the result */
    printf( "result is %d \n", val );
    }
  ...

True Module (Server Module) Construction

This section will outline the construction of a true module exported by an address space. For the example, we will demonstrate the calculator interface described above. We will also use the CORBA 1.2 names for standard types and exceptions, to show that it can be done.

First, some runtime initialization of the server stubs must be done. Call Foo__InitializeServer for every ISL interface Foo containing an object type implemented by the address space. Also call any client initialization procedures needed (see next section). These server and client initialization calls can be made in any order, and each initialization procedure can be called more than once. However, no two calls may be done concurrently (this is an issue only for those using some sort of multi-threading package).

Then we create an instance of calc_self. We then make the string binding handle of the object available by printing it to stdout. Finally the ILU_C_Run procedure is called. This procedure listens for connections and dispatches server methods.

The main program for the server is as follows:

#include "I2.h"
 
CORBA_long
  server_calc_self_Div (calc_self h,
                        CORBA_Environment *s,
                        CORBA_integer u,
                        CORBA_integer v)
{
  calc_numerator n = 9;

  if ( v == 0 )
    {
      s->returnCode = ex_ilu_ProtocolError;
      s->_major = CORBA_USER_EXCEPTION;
      s->ptr = (void *) malloc(sizeof(calc_numerator));
      *((calc_numerator *) (s->ptr)) = n;
      s->freeRoutine = (void (*) (void *)) free;
      return( u );
    }
  else
    return( u / v );
}

main ()
{
  calc_self s;
  char * sbh;
  CORBA_Environment ev;

  calc__InitializeServer( );

  s = calc_self__CreateTrue (NULL, NULL, NULL);
  if (s == NULL)
    {
      fprintf (stderr, "Unable to create instance of calc_self.\n");
      exit(1);
    }
  else
    {
      sbh = CORBA_ORB_object_to_string (ILU_C_ORB, &ev, s);
      if (ev._major == CORBA_NO_EXCEPTION)
        {
          printf ("%s\n", sbh);
          ILU_C_Run (); /* enter main loop; hang processing requests */
        }
      else
        {
          fprintf (stderr,
                   "Attempt to obtain sbh of object %x signalled <%s>.\n",
                   s, CORBA_exception_id(&ev));
          exit(1);
        }
    }
}

Using ILU Modules

Before manipulating surrogate objects, a client module must first call a runtime initialization procedure Foo__Initialize for each ISL interface Foo that declares object types whose surrogates are to be manipulated. Additionally, server modules must also call server initialization procedures (see previous section). These initialization calls may be made in any order, and each procedure may be called more than once. However, no two calls may be done concurrently (this is an issue only for those using some sort of multi-threading package).

A client of an exported module may obtain an object instance either by calling a method which returns the instance, or by calling TYPE__CreateFromSBH() on the string binding handle of an instance. Once the object instance, which is typically a surrogate instance, but may in fact be a true instance, is held by the client, it can be used simply by making method calls on it, as shown above.

Stub Generation

To generate ANSI C stubs from an ISL file, use the program c-stubber. Four files are generated from the `.isl' file:

Typically, clients of a module never have a need for the `interface-name-true.c' file.

% c-stubber foo.isl
header file interface foo to ./foo.h...
code for interface foo to ./foo-common.c...
code for interface foo to ./foo-surrogate.c...
code for server stubs of interface foo to ./foo-true.c...
%

Tailoring Identifier Names

The option -renames renames-filename may be used with c-stubber to specify particular ANSI C names for ISL types.

It is sometimes necessary to have the ANSI C names of an ILU interface match some other naming scheme. A mechanism is provided to allow the programmer to specify the names of ANSI C language artifacts directly, and thus override the automatic ISL to ANSI C name mappings.

To do this, you place a set of synonyms for ISL names in a renames-file, and invoke the c-stubber program with the switch -renames, specifying the name of the renames-file. The lines in the file are of the form

construct ISL-name ANSI C-name
where construct is one of method, exception, type, interface, or constant; ISL-name is the name of the construct, expressed either as the simple name, for interface names, the concatenation interface-name.construct-name for exceptions, types, and constants, or interface-name.type-name.method-name for methods; and ANSI C-name is the name the construct should have in the generated ANSI C code. For example:

# change "foo_r1" to plain "R1"
type foo_r1 r1
# change name of method "m1" to "method1"
method foo_o1_m1 method1

Lines beginning with the `sharp' character `#' are treated as comment lines, and ignored, in the renames-file.

This feature of the c-stubber should be used as little and as carefully as possible, as it can cause confusion for readers of the ISL interface, in trying to follow the ANSI C code. It can also create name conflicts between different modules, unless names are carefully chosen.

Libraries and Linking

For clients of an ILU module, it is only necessary to link with the `interface-name-surrogate.o' and `interface-name-common' files generated from the ANSI C files generated for the interface or interfaces being used, and with the two libraries `ILUHOME/lib/libilu-c.a' and `ILUHOME/lib/libilu.a' (in this order, as `libilu-c.a' uses functions in `libilu.a').

For implementors of servers, the code for the server-side stubs, in the file `interface-name-true.o' compiled from `interface-name-true.c', and in the file `interface-name-common.o' compiled from `interface-name-common.c', should be included along with the other files and libraries.

ILU C API

In addition to the functions defined by the CORBA mapping, the ILU ANSI C mapping provides some other functions, chiefly for type manipulation, object manipulation, and server manipulation. There are also a number of macros provided for compatibility with both versions of CORBA (1.1 and 1.2).

Type Manipulation

[ILU C API]: OPTIONAL(ilu_Class) ILU_C_FindILUClassByTypeName ( RETAIN(ilu_string) type-name )

Locking: L1_sup < otmu, L2, Main unconstrained.

Given the type-name of an ILU object type, of the form "Interface.Typename", returns the ilu_Class value for it. This value can be used to compare types for equality.

[ILU C API]: OPTIONAL(ilu_Class) ILU_C_FindILUClassByTypeID ( RETAIN(ilu_string) type-id)

Locking: L1_sup < otmu; L2, Main unconstrained.

Given the type-id of an ILU object type, of the form "ilu:gfbSCM7tsK9vVYjKfLole1HOBDc", returns the ilu_Class value for it. This value can be used to compare types for equality.

[ILU C API]: GLOBAL(OPTIONAL(ilu_string)) ILU_C_ClassName ( RETAIN(CORBA_Object) )

Locking: unconstrained.

Returns the ILU name for the most specific type of an object instance.

[ILU C API]: GLOBAL(OPTIONAL(ilu_string)) ILU_C_ClassID ( RETAIN(CORBA_Object) )

Locking: unconstrained.

Returns the ILU type ID for the most specific type of an object instance.

[ILU C API]: ilu_Class ILU_C_ClassRecordOfInstance (CORBA_Object)

Locking: unconstrained.

Returns the ilu_Class value for the most specific type of an object instance.

Object Manipulation

[ILU C API]: ilu_string ILU_C_SBHOfObject ( CORBA_Object instance )

Locking: Main invariant holds.

Given an instance, returns a string form which is its name and contact information. The CORBA-specified routine CORBA_ORB_object_to_string() should typically be used instead.

[ILU C API]: OPTIONAL(CORBA_Object) ILU_C_SBHToObject (char * sbh, OPTIONAL(char *) mostSpecificTypeID, OPTIONAL(ilu_Class) putative_type)

Locking: Main invariant holds.

Given the string form of an object instance, along with information about its type, this routine returns an object, creating it if necessary.

[ILU C API]: OPTIONAL(PASS(char*)) ILU_C_PublishObject ( CORBA_Object instance )

Locking: Main invariant holds.

Publishes the OID of the instance in a domain-wide registry. This is an experimental interface, and may change in the future.

[ILU C API]: ilu_boolean ILU_C_WithdrawObject ( CORBA_Object instance, PASS(char *) proof)

Locking: Main invariant holds.

Removes the OID of the instance from the domain-wide registry. proof is the string returned from the call to ILU_C_PublishObject().

[ILU C API]: OPTIONAL(GLOBAL(CORBA_Object)) ILU_C_LookupObject ( RETAIN(char *) oid, ilu_Class putative-class )

Locking: Main invariant holds.

Finds and returns the object specified by oid by consulting the local domain registry of objects. putative-class is the type that the object is expected to be of, though the type of the actual object returned may be a subtype of putative-class.

Server Manipulation

[ILU C API]: void ILU_C_Run (void)

Locking: Main invariant holds.

Called to animate a server or other program. Invokes the event handling loop. Never returns.

[ILU C API]: ilu_Server ILU_C_DefaultServer

Locking: Main invariant holds.

Can be set to choose the default server. Note that the default port must be chosen in lockstep.

[ILU C API]: ilu_Port ILU_C_DefaultPort

Locking: Main invariant holds.

Can be set to determine the default port.

[ILU C API]: ilu_Server ILU_C_InitializeServer (OPTIONAL(RETAIN(char *)) serverID, OPTIONAL(GLOBAL(ilu_ObjectTable)) obj_tab, OPTIONAL(RETAIN(char *)) protocol, OPTIONAL(RETAIN(char *)) transport, ilu_boolean setdefaultport)

Locking: Main invariant holds.

Creates and returns an ilu_Server with ID serverID, object mapping table obj_tab, using protocol protocol over a transport of type transport. If serverID is specified as NULL, a unique string is generated automatically for the server ID. If obj_tab is specified as NULL, the default hash table object table is used.

If either protocol or transport is specified, or if setdefaultport, an ilu_Port will automatically be created and added to the ilu_Server. protocol is a string of the form "sunrpc_". It defaults using Sun RPC. transport is a string of the form "yyy_localhost_xxx", where yyy is one of tcp or udp, and xxx is a decimal number indicating which UNIX port to listen on, or 0 if you wish the system to select the port. The default transport value, if NULL is passed, is "tcp_localhost_0". If setdefaultport is true, the newly created ilu_Port will become the default port of the ilu_Server.

CORBA Compatibility Macros

ILU supports either CORBA 1.1 and 1.2, depending on how it is installed at your site.(11) A number of macros are defined to make programs less dependent on which version they use.

Macro: ILU_C_OBJECT

Expands to either CORBA_Object or Object.

Macro: ILU_C_ENVIRONMENT

Expands to either CORBA_Environment or Environment.

Macro: ILU_C_NO_EXCEPTION

Expands to either StExcep_NO_EXCEPTION or CORBA_NO_EXCEPTION.

Macro: ILU_C_USER_EXCEPTION

Expands to either StExcep_USER_EXCEPTION or CORBA_USER_EXCEPTION.

Macro: ILU_C_SYSTEM_EXCEPTION

Expands to either StExcep_SYSTEM_EXCEPTION or CORBA_SYSTEM_EXCEPTION.

Macro: ILU_C_SUCCESSFUL ( ILU_C_ENVIRONMENT * ev )

Evaluates to true if no exception has been raised.

Macro: ILU_C_SET_SUCCESSFUL ( ILU_C_ENVIRONMENT * ev )

Sets ev to a successful result.

Macro: ILU_C_EXCEPTION_ID ( ILU_C_ENVIRONMENT * ev )

Returns the char * value that is the exception's ID.

Macro: ILU_C_EXCEPTION_VALUE ( ILU_C_ENVIRONMENT * ev )

Expands to either exception_value(ev) or CORBA_exception_value(ev).

Macro: ILU_C_EXCEPTION_FREE ( ILU_C_ENVIRONMENT * ev )

Expands to either exception_free(ev) or CORBA_exception_free(ev).

Go to the previous, next section.