[Top] [Prev] [Next] [Bottom]

C Language Mapping

14


CORBA is independent of the programming language used to construct clients or implementations. In order to use the ORB, it is necessary for programmers to know how to access ORB functionality from their programming languages. This chapter defines the mapping of OMG IDL constructs to the C programming language.

14.1 Requirements for a Language Mapping

All language mappings have approximately the same structure. They must define the means of expressing in the language:

A complete language mapping will allow a programmer to have access to all ORB functionality in a way that is convenient for the particular programming language. To support source portability, all ORB implementations must support the same mapping for a particular language.

14.1.1 Basic Data Types

A language mapping must define the means of expressing all of the data types defined in Section 3.8.1, "Basic Types," on page 3-20. The ORB defines the range of values supported, but the language mapping defines how a programmer sees those values. For example, the C mapping might define TRUE as 1 and FALSE as 0, whereas the LISP mapping might define TRUE as T and FALSE as NIL. The mapping must specify the means to construct and operate on these data types in the programming language.

14.1.2 Constructed Data Types

A language mapping must define the means of expressing the constructed data types defined in Section 3.8.2, "Constructed Types," on page 3-22. The ORB defines aggregates of basic data types that are supported, but the language mapping defines how a programmer sees those aggregates. For example, the C mapping might define an OMG IDL struct as a C struct, whereas the LISP mapping might define an OMG IDL struct as a list. The mapping must specify the means to construct and operate on these data types in the programming language.

14.1.3 Constants

OMG IDL definitions may contain named constant values that are useful as parameters for certain operations. The language mapping should provide the means to access these constants by name.

14.1.4 Objects

There are two parts of defining the mapping of ORB objects to a particular language. The first specifies how an object is represented in the program and passed as a parameter to operations. The second is how an object is invoked. The representation of an object reference in a particular language is generally opaque, that is, some language-specific data type is used to represent the object reference, but the program does not interpret the values of that type. The language-specific representation is independent of the ORB representation of an object reference, so that programs are not ORB-dependent. In an object-oriented programming language, it may be convenient to represent an ORB object as a programming language object. Any correspondence between the programming language object types and the OMG IDL types including inheritance, operation names, etc., is up to the language mapping.

There are only three uses that a program can make of an object reference: it may specify it as a parameter to an operation (including receiving it as an output parameter), it can invoke an operation on it, or it can perform an ORB operation (including object adapter operations) on it.

14.1.5 Invocation of Operations

An operation invocation requires the specification of the object to be invoked, the operation to be performed, and the parameters to be supplied. There are a variety of possible mappings, depending to a large extent on the procedure mechanism in the particular language. Some possible choices for language mapping of invocation include: interface-specific stub routines, a single general-purpose routine, a set of calls to construct a parameter list and initiate the operation, or mapping ORB operations to operations on objects defined in an object-oriented programming language.

The mapping must define how parameters are associated with the call, and how the operation name is specified. It is also necessary to specify the effect of the call on the flow of control in the program, including when an operation completes normally and when an exception is raised.

The most natural mapping would be to model a call on an ORB object as the corresponding call in the particular language. However, this may not always be possible for languages where the type system or call mechanism is not powerful enough to handle ORB objects. In this case, multiple calls may be required. For example, in C, it is necessary to have a separate interface for dynamic construction of calls, since C does not permit discovery of new types at runtime. In LISP, however, it may be possible to make a language mapping that is the same for objects whether or not they were known at compile time.

In addition to defining how an operation is expressed, it is necessary to specify the storage allocation policy for parameters, for example, what happens to storage of input parameters, and how and where output parameters are allocated. It is also necessary to describe how a return value is handled, for operations that have one.

14.1.6 Exceptions

There are two aspects to the mapping of exceptions into a particular language. First is the means for handling an exception when it occurs, including deciding which exception occurred. If the programming language has a model of exceptions that can accommodate ORB exceptions, that would likely be the most convenient choice; if it does not, some other means must be used, for example, passing additional parameters to the operations that receive the exception status.

It is commonly the case that the programmer associates specific code to handle each kind of exception. It is desirable to make that association as convenient as possible.

Second, when an exception has been raised, it must be possible to access the parameters of the exception. If the language exception mechanism allows for parameters, that mechanism could be used. Otherwise, some other means of obtaining the exception values must be provided.

14.1.7 Attributes

The ORB models attributes as a pair of operations, one to set and one to get the attribute value. The language mapping defines the means of expressing these operations. One reason for distinguishing attributes from pairs of operations is to allow the language mapping to define the most natural way for accessing them. Some possible choices include defining two operations for each attribute, defining two operations that can set or get, respectively, any attribute, defining operations that can set or get groups of attributes, and so forth.

14.1.8 ORB Interfaces

Most of a language mapping is concerned with how the programmer-defined objects and data are accessed. Programmers who use the ORB must also access some interfaces implemented directly by the ORB, for example, to convert an object reference to a string. A language mapping must also specify how these interfaces appear in the particular programming language.

Various approaches may be taken, including defining a set of library routines, allowing additional ORB-related operations on objects, or defining interfaces that are similar to the language mapping for ordinary objects.

The last approach is called defining pseudo-objects. A pseudo-object has an interface that can (with a few exceptions) be defined in IDL, but is not necessarily implemented as an ORB object. Using stubs a client of a pseudo-object writes calls to it in the same way as if it were an ordinary object. Pseudo-object operations cannot be invoked with the Dynamic Invocation Interface. However, the ORB may recognize such calls as special and handle them directly. One advantage of pseudo-objects is that the interface can be expressed in IDL independent of the particular language mapping, and the programmer can understand how to write calls by knowing the language mapping for the invocations of ordinary objects.

It is not necessary for a language mapping to use the pseudo-object approach. However, this document defines interfaces in subsequent chapters using OMG IDL wherever possible. A language mapping must define how these interfaces are accessed, either by defining them as pseudo-objects and supporting a mapping similar to ordinary objects, by defining language-specific interfaces for them, or in some other way.

14.2 Scoped Names

The C programmer must always use the global name for a type, constant, exception, or operation. The C global name corresponding to an OMG IDL global name is derived by converting occurrences of "::" to "_" (an underscore) and eliminating the leading underscore.

Consider the following example:

typedef string<256> filename_t;

interface example0 {
enum color {red, green, blue};
union bar switch (enum foo {room, bell}) { ... };
· · ·
};

Code to use this interface would look as follows:

#include "example0.h" /* C */

filename_t FN;

example0_color C = example0_red;

example0_bar myUnion;


switch (myUnion._d) {

case example0_bar_room: · · ·

case example0_bar_bell: · · ·

};

Note that the use of underscores to replace the "::" separators can lead to ambiguity if the OMG IDL specification contains identifiers with underscores in them. Consider the following example:

typedef long foo_bar;

interface foo {
typedef short bar; /* A legal OMG IDL statement, but ambiguous in C */
· · ·
};

Due to such ambiguities, it is advisable to avoid the indiscriminate use of underscores in identifiers.

14.3 Mapping for Interfaces

All interfaces must be defined at global scope (no nested interfaces). The mapping for an interface declaration is as follows:

interface example1 {

long op1(in long arg1);
};

The preceding example generates the following C declarations1:

typedef CORBA_Object example1; /* C */

extern CORBA_long example1_op1(

example1 o,

CORBA_long arg1,

CORBA_Environment *ev

);

All object references (typed interface references to an object) are of the well-known, opaque type CORBA_Object. The representation of CORBA_Object is a pointer. To permit the programmer to decorate a program with typed references, a type with the name of the interface is defined to be a CORBA_Object. The literal

CORBA_OBJECT_NIL is legal wherever a CORBA_Object may be used; it is guaranteed to pass the is_nil operation defined in Section 7.2.3, "Nil Object References," on page 7-4.

OMG IDL permits specifications in which arguments, return results, or components of constructed types may be interface references. Consider the following example:

#include "example1.idl"

interface example2 {
example1 op2();
};

This is equivalent to the following C declaration:

#include "example1.h" /* C */

typedef CORBA_Object example2;

extern example1 example2_op2(example2 o, CORBA_Environment *ev);

A C fragment for invoking such an operation is as follows:

#include "example2.h" /* C */

example1 ex1;

example2 ex2;

CORBA_Environment ev;

/* code for binding ex2 */

ex1 = example2_op2(ex2, &ev);

14.4 Inheritance and Operation Names

OMG IDL permits the specification of interfaces that inherit operations from other interfaces. Consider the following example:

interface example3 : example1 {

void op3(in long arg3, out long arg4);
};

This is equivalent to the following C declarations:

typedef CORBA_Object example3; /* C */

extern CORBA_long example3_op1(

example3 o,

CORBA_long arg1,

CORBA_Environment *ev

);

extern void example3_op3(

example3 o,

CORBA_long arg3,

CORBA_long *arg4,

CORBA_Environment *ev

);

As a result, an object written in C can access op1 as if it was directly declared in example3. Of course, the programmer could also invoke example1_op1 on an Object of type example3; the virtual nature of operations in interface definitions will cause invocations of either function to cause the same method to be invoked.

14.5 Mapping for Attributes

The mapping for attributes is best explained through example. Consider the following specification:

interface foo {

struct position_t {
float x, y;
};
attribute float radius;
readonly attribute position_t position;
};

This is exactly equivalent to the following illegal OMG IDL specification:

interface foo {

struct position_t {
float x, y;
};
float _get_radius();
void _set_radius(in float r);
position_t _get_position();
};

This latter specification is illegal, since OMG IDL identifiers are not permitted to start with the underscore (_) character.

The language mapping for attributes then becomes the language mapping for these equivalent operations. More specifically, the function signatures generated for the above operations are as follows:

typedef struct foo_position_t { /* C */

CORBA_float x, y;

} foo_position_t;

extern CORBA_float foo__get_radius(foo o, CORBA_Environment *ev);

extern void foo__set_radius(

foo o,

CORBA_float r,

CORBA_Environment *ev

);

extern foo_position_t foo__get_position(foo o, CORBA_Environment *ev);

Note that two underscore characters (__) separate the name of the interface from the words "get" or "set" in the names of the functions.

If the "set" accessor function fails to set the attribute value, the method should return one of the standard exceptions defined in Section 3.15, "Standard Exceptions," on page 3-33.

14.6 Mapping for Constants

Constant identifiers can be referenced at any point in the user's code where a literal of that type is legal. In C, these constants are #defined.

The fact that constants are #defined may lead to ambiguities in code. All names which are mandated by the mappings for any of the structured types below start with an underscore.

14.7 Mapping for Basic Data Types

The basic data types have the mappings shown in Table 19 on page 14-8. Implementations are responsible for providing typedefs for CORBA_short, CORBA_long, and so forth. consistent with OMG IDL requirements for the corresponding data types.
Table 19: Data Type Mappings

OMG IDL

C

short

CORBA_short

long

CORBA_long

unsigned short

CORBA_unsigned_short

unsigned long

CORBA_unsigned_long

float

CORBA_float

double

CORBA_double

char

CORBA_char

boolean

CORBA_boolean

octet

CORBA_octet

enum

CORBA_enum

any

typedef struct CORBA_any { CORBA_TypeCode _type; void *_value; }

CORBA_any;

The C mapping of the OMG IDL boolean types is unsigned char with only the values 1 (TRUE) and 0 (FALSE) defined; other values produce undefined behavior. CORBA_boolean is provided for symmetry with the other basic data type mappings.

The C mapping of OMG IDL enum types is an unsigned integer type capable of representing 232 enumerations. Each enumerator in an enum is #defined with an appropriate unsigned integer value conforming to the ordering constraints described in "Enumerations" on page 3-24.

TypeCodes are described in Section 6.7, "TypeCodes," on page 6-33. The _value member for an any is a pointer to the actual value of the datum.

The any type supports the notion of ownership of its _value member. By setting a release flag in the any when a value is installed, programmers can control ownership of the memory pointed to by _value. The location of this release flag is implementation-dependent, so the following two ORB-supplied functions allow for the setting and checking of the any release flag:

void CORBA_any_set_release(CORBA_any*, CORBA_boolean); /* C */
CORBA_boolean CORBA_any_get_release(CORBA_any*);

CORBA_any_set_release can be used to set the state of the release flag. If the flag is set to TRUE, the any effectively "owns" the storage pointed to by _value; if FALSE, the programmer is responsible for the storage. If, for example, an any is returned from an operation with its release flag set to FALSE, calling CORBA_free() on the returned any* will not deallocate the memory pointed to by _value. Before calling CORBA_free() on the _value member of an any directly, the programmer should check the release flag using CORBA_any_get_release. If it returns FALSE, the programmer should not invoke CORBA_free() on the _value member; doing so produces undefined behavior. Also, passing a null pointer to either CORBA_any_set_release or CORBA_any_get_release produces undefined behavior.

If CORBA_any_set_release is never called for a given instance of any, the default value of the release flag for that instance is FALSE.

14.8 Mapping Considerations for Constructed Types

The mapping for OMG IDL structured types (structs, unions, arrays, and sequences) can vary slightly depending on whether the data structure is fixed-length or variable-length. A type is variable-length if it is one of the following types:

14.9 Mapping for Structure Types

OMG IDL structures map directly onto C structs. Note that all OMG IDL types that map to C structs may potentially include padding.

14.10 Mapping for Union Types

OMG IDL discriminated unions are mapped onto C structs. Consider the following OMG IDL declaration:

union Foo switch (long) {

case 1: long x;
case 2: float y;
default: char z;
};

This is equivalent to the following struct in C:

typedef struct { /* C */

CORBA_long _d;

union {

CORBA_long x;

CORBA_float y;

CORBA_char z;

} _u;

} Foo;

The discriminator in the struct is always referred to as _d; the union in the struct is always referred to as _u.

Reference to union elements is as in normal C:

Foo *v; /* C */

/* make a call that returns a pointer to a Foo in v */

switch(v->_d) {

case 1: printf("x = %ld\n", v->_u.x); break;

case 2: printf("y = %f\n", v->_u.y); break;

default: printf("z = %c\n", v->_u.z); break;

}

An ORB implementation need not use a C union to hold the OMG IDL union elements; a C struct may be used instead. In either case, the programmer accesses the union elements via the _u member.

14.11 Mapping for Sequence Types

The OMG IDL data type sequence permits passing of unbounded arrays between objects. Consider the following OMG IDL declaration:

typedef sequence<long,10> vec10;

In C, this is converted to:

typedef struct { /* C */

CORBA_unsigned_long _maximum;

CORBA_unsigned_long _length;

CORBA_long *_buffer;

} vec10;

An instance of this type is declared as follows:

vec10 x = {10L, 0L, (CORBA_long *)NULL); /* C */

Prior to passing &x as an in parameter, the programmer must set the _buffer member to point to a CORBA_long array of 10 elements, and must set the _length member to the actual number of elements to transmit.

Prior to passing the address of a vec10* as an out parameter (or receiving a vec10* as the function return), the programmer does nothing. The client stub will allocate storage for the returned sequence; for bounded sequences, it also allocates a buffer of the specified size, while for unbounded sequences, it also allocates a buffer big enough to hold what was returned by the object. Upon successful return from the invocation, the _maximum member will contain the size of the allocated array, the _buffer member will point at allocated storage, and the _length member will contain the number of values that were returned in the _buffer member. The client is responsible for freeing the allocated sequence using CORBA_free().

Prior to passing &x as an inout parameter, the programmer must set the _buffer member to point to a CORBA_long array of 10 elements. The _length member must be set to the actual number of elements to transmit. Upon successful return from the invocation, the _length member will contain the number of values that were copied into the buffer pointed to by the _buffer member. If more data must be returned than the original buffer can hold, the callee can deallocate the original _buffer member using CORBA_free() (honoring the release flag) and assign _buffer to point to new storage.

For bounded sequences, it is an error to set the _length or _maximum member to a value larger than the specified bound.

Sequence types support the notion of ownership of their _buffer members. By setting a release flag in the sequence when a buffer is installed, programmers can control ownership of the memory pointed to by _buffer. The location of this release flag is implementation-dependent, so the following two ORB-supplied functions allow for the setting and checking of the sequence release flag:

void CORBA_sequence_set_release(void*, CORBA_boolean); /* C */
CORBA_boolean CORBA_sequence_get_release(void*);

CORBA_sequence_set_release can be used to set the state of the release flag. If the flag is set to TRUE, the sequence effectively "owns" the storage pointed to by _buffer; if FALSE, the programmer is responsible for the storage. If, for example, a sequence is returned from an operation with its release flag set to FALSE, calling CORBA_free() on the returned sequence pointer will not deallocate the memory pointed to by _buffer. Before calling CORBA_free() on the _buffer member of a sequence directly, the programmer should check the release flag using CORBA_sequence_get_release. If it returns FALSE, the programmer should not invoke CORBA_free() on the _buffer member; doing so produces undefined behavior. Also, passing a null pointer or a pointer to something other than a sequence type to either CORBA_sequence_set_release or CORBA_sequence_get_release produces undefined behavior.

CORBA_sequence_set_release should only be used by the creator of a sequence. If it is not called for a given sequence instance, then the default value of the release flag for that instance is FALSE.

Two sequence types are the same type if their sequence element type and size arguments are identical. For example,

const long SIZE = 25;

typedef long seqtype;
typedef sequence<long, SIZE> s1;
typedef sequence<long, 25> s2;
typedef sequence<seqtype, SIZE> s3;
typedef sequence<seqtype, 25> s4;

declares s1, s2, s3, and s4 to be of the same type.

The OMG IDL type

sequence<type,size>

maps to

#ifndef _CORBA_sequence_type_defined /* C */

#define _CORBA_sequence_type_defined

typedef struct {

CORBA_unsigned_long _maximum;

CORBA_unsigned_long _length;

type *_buffer;

} CORBA_sequence_type;

#endif /* _CORBA_sequence_type_defined */

The ifdef's are needed to prevent duplicate definition where the same type is used more than once. The type name used in the C mapping is the type name of the effective type, e.g. in

typedef CORBA_long FRED; /* C */

typedef sequence<FRED,10> FredSeq;

the sequence is mapped onto struct { ... } CORBA_sequence_long;

If the type in

sequence<type,size>

consists of more than one identifier (e.g. unsigned long), then the generated type name consists of the string "CORBA_sequence_" concatenated to the string consisting of the concatenation of each identifier separated by underscores (e.g. "unsigned_long").

If the type is a string, the string "string" is used to generate the type name. If the type is a sequence, the string "sequence" is used to generate the type name, recursively. For example

sequence<sequence<long> >

generates a type of

CORBA_sequence_sequence_long

These generated type names may be used to declare instances of a sequence type.

In addition to providing a type-specific allocation function for each sequence, an ORB implementation must provide a buffer allocation function for each sequence type. These functions allocate vectors of type T for sequence<T>. They are defined at global scope and are named similarly to sequences:

T *CORBA_sequence_T_allocbuf(CORBA_unsigned_long len); /* C */

Here, "T" refers to the type name. For the type

sequence<sequence<long> >
for example, the sequence buffer allocation function is named

T *CORBA_sequence_sequence_long_allocbuf(CORBA_unsigned_long len);

Buffers allocated using these allocation functions are freed using CORBA_free().

14.12 Mapping for Strings

OMG IDL strings are mapped to 0-byte terminated character arrays; i.e. the length of the string is encoded in the character array itself through the placement of the 0-byte. Note that the storage for C strings is one byte longer than the stated OMG IDL bound. Consider the following OMG IDL declarations:

typedef string<10> sten;

typedef string sinf;

In C, this is converted to:

typedef CORBA_char *sten; /* C */

typedef CORBA_char *sinf;

Instances of these types are declared as follows:

sten s1 = NULL; /* C */

sinf s2 = NULL;

Two string types are the same type if their size arguments are identical. For example,

const long SIZE = 25; /* C */

typedef string<SIZE> sx;

typedef string<25> sy;

declares sx and sy to be of the same type.

Prior to passing s1 or s2 as an in parameter, the programmer must assign the address of a character buffer containing a 0-byte terminated string to the variable. The caller cannot pass a null pointer as the string argument.

Prior to passing &s1 or &s2 as an out parameter (or receiving an sten or sinf as the return result), the programmer does nothing. The client stub will allocate storage for the returned buffer; for bounded strings, it allocates a buffer of the specified size, while for unbounded strings, it allocates a buffer big enough to hold the returned string. Upon successful return from the invocation, the character pointer will contain the address of the allocated buffer. The client is responsible for freeing the allocated storage using CORBA_free().

Prior to passing &s1 or &s2 as an inout parameter, the programmer must assign the address of a character buffer containing a 0-byte terminated array to the variable. If the returned string is larger than the original buffer, the client stub will call CORBA_free() on the original string and allocate a new buffer for the new string. The client should therefore never pass an inout string parameter that was not allocated using CORBA_string_alloc. The client is responsible for freeing the allocated storage using CORBA_free(), regardless of whether or not a reallocation was necessary.

Strings are dynamically allocated using the following ORB-supplied function:


char *CORBA_string_alloc(CORBA_unsigned_long len);

This function allocates len+1 bytes, enough to hold the string and its terminating NUL character.

Strings allocated in this manner are freed using CORBA_free().

14.13 Mapping for Arrays

OMG IDL arrays map directly to C arrays. All array indices run from 0 to <size - 1>.

For each named array type in OMG IDL, the mapping provides a C typedef for pointer to the array's slice. A slice of an array is another array with all the dimensions of the original except the first. For example, given the following OMG IDL definition:

typedef long LongArray[4][5];
The C mapping provides the following definitions:

typedef CORBA_long LongArray[4][5];
typedef CORBA_long LongArray_slice[5];

The generated name of the slice typedef is created by appending "_slice" to the original array name.

If the return result, or an out parameter for an array holding a variable-length type, of an operation is an array, the array storage is dynamically allocated by the stub; a pointer to the array slice of the dynamically allocated array is returned as the value of the client stub function. When the data is no longer needed, it is the programmer's responsibility to return the dynamically allocated storage by calling CORBA_free().

For an array T of a variable-length type is dynamically allocated using the following ORB-supplied function:

T_slice *T__alloc(); /* C */

This function is identical to the allocation functions described in Section 14.8, "Mapping Considerations for Constructed Types," on page 14-9, except that the return type is pointer to array slice, not pointer to array.

14.14 Mapping for Exception Types

Each defined exception type is defined as a struct tag and a typedef with the C global name for the exception. An identifier for the exception, in string literal form, is also #defined, as is a type-specific allocation function. For example:

exception foo {

long dummy;
};

yields the following C declarations:

typedef struct foo { /* C */

CORBA_long dummy;
/* ...may contain additional
* implementation-specific members...
*/

} foo;

#define ex_foo <unique identifier for exception>
foo *foo__alloc();

The identifier for the exception uniquely identifies this exception type. For example, it could be the Interface Repository identifier for the exception (see Section 6.5.19, "ExceptionDef," on page 6-23).

The allocation function dynamically allocates an instance of the exception and returns a pointer to it. Each exception type has its own dynamic allocation function. Exceptions allocated using a dynamic allocation function are freed using CORBA_free().

14.15 Implicit Arguments to Operations

From the point of view of the C programmer, all operations declared in an interface have additional leading parameters preceding the operation-specific parameters:

1. The first parameter to each operation is a CORBA_Object input param eter; this parameter designates the object to process the request.

2. The last parameter to each operation is a (CORBA_Environment *) output parameter; this parameter permits the return of exception information.

3. If an operation in an OMG IDL specification has a context specification, then a CORBA_Context input parameter precedes the (CORBA_Environment *) parameter and follows any operation-specific arguments.

As described above, the CORBA_Object type is an opaque type. The CORBA_Environment type is partially opaque; Section 14.20 provides a description of the non-opaque portion of the exception structure and an example of how to handle exceptions in client code. The CORBA_Context type is opaque; see the Dynamic Invocation Interface chapter for more information on how to create and manipulate context objects.

14.16 Interpretation of Functions with Empty Argument Lists

A function declared with an empty argument list is defined to take no operation-specific arguments.

14.17 Argument Passing Considerations

For all OMG IDL types (except arrays), if the OMG IDL signature specifies that an argument is an out or inout parameter, then the caller must always pass the address of a variable of that type (or the value of a pointer to that type); the callee must dereference the parameter to get to the type. For arrays, the caller must pass the address of the first element of the array.

For in parameters, the value of the parameter must be passed for all of the basic types, enumeration types, and object references. For all arrays, the address of the first element of the array must be passed. For all other structured types, the address of a variable of that type must be passed, regardless of whether they are fixed- or variable-length. For strings, a char* must be passed.

For inout parameters, the address of a variable of the correct type must be passed for all of the basic types, enumeration types, object references, and structured types. For strings, the address of a char* must be passed. For all arrays, the address of the first element of the array must be passed.

Consider the following OMG IDL specification:

interface foo {

typedef long Vector[25];
void bar(out Vector x, out long y);
};

Client code for invoking the bar operation would look like:

foo object; /* C */

foo_Vector_slice x;

CORBA_long y;

CORBA_Environment ev;

/* code to bind object to instance of foo */

foo_bar(object, &x, &y, &ev);

For out parameters of type variable-length struct, variable-length union, string, sequence, an array holding a variable-length type, or any, the ORB will allocate storage for the output value using the appropriate type-specific allocation function. The client may use and retain that storage indefinitely, and must indicate when the value is no longer needed by calling the procedure CORBA_free, whose signature is:

extern void CORBA_free (void *storage); /* C */

The parameter to CORBA_free() is the pointer used to return the out parameter. CORBA_free() releases the ORB-allocated storage occupied by the out parameter, including storage indirectly referenced, such as in the case of a sequence of strings or array of object reference. If a client does not call CORBA_free() before reusing the pointers that reference the out parameters, that storage might be wasted. Passing a null pointer to CORBA_free() is allowed; CORBA_free() simply ignores it and returns without error.

14.18 Return Result Passing Considerations

When an operation is defined to return a non-void return result, the following rules hold:

1. If the return result is one of the types float, double, long, short, unsigned long, unsigned short, char, boolean, octet, Object, or an enumeration, then the value is returned as the operation result.

2. If the return result is one of the fixed-length types struct or union, then the value of the C struct representing that type is returned as the operation result. If the return result is one of the variable-length types struct, union, sequence, or any, then a pointer to a C struct representing that type is returned as the operation result.

3. If the return result is of type string, then a pointer to the first character of the string is returned as the operation result.

4. If the return result is of type array, then a pointer to the slice of the array is returned as the operation result.

Consider the following interface:

interface X {

struct y {
long a;
float b;
};
long op1();
y op2();
}

The following C declarations ensue from processing the specification:

typedef CORBA_Object X; /* C */

typedef struct X_y {

CORBA_long a;

CORBA_float b;

} X_y;

extern CORBA_long X_op1(X object, CORBA_Environment *ev);

extern X_y X_op2(X object, CORBA_Environment *ev);

For operation results of type variable-length struct, variable-length union, string, sequence, array, or any, the ORB will allocate storage for the return value using the appropriate type-specific allocation function. The client may use and retain that storage indefinitely, and must indicate when the value is no longer needed by calling the procedure CORBA_free() described in Section 14.17, "Argument Passing Considerations.

14.19 Summary of Argument/Result Passing

Table 20 on page 14-19 summarizes what a client passes as an argument to a stub and receives as a result.
Table 20: Basic Argument and Result Passing

Data Type

In

Inout

Out

Return

short

short

short*

short*

short

long

long

long*

long*

long

unsigned short

unsigned_short

unsigned_short*

unsigned_short*

unsigned_short

unsigned long

unsigned_long

unsigned_long*

unsigned_long*

unsigned_long

float

float

float*

float*

float

double

double

double*

double*

double

boolean

boolean

boolean*

boolean*

boolean

char

char

char*

char*

char

octet

octet

octet*

octet*

octet

enum

enum

enum*

enum*

enum

object reference ptr1

objref_ptr

objref_ptr*

objref_ptr*

objref_ptr

struct, fixed

struct*

struct*

struct*

struct

struct, variable

struct*

struct*

struct**

struct*

union, fixed

union*

union*

union*

union

union, variable

union*

union*

union**

union*

string

char*

char**

char**

char*

sequence

sequence*

sequence*

sequence**

sequence*

array, fixed

array

array

array

array slice*2

array, variable

array

array

array slice**2

array slice*2

any

any*

any*

any**

any*

1 Including pseudo-object references.

2 A slice is an array with all the dimensions of the original except the first one.

3

A client is responsible for providing storage for all arguments passed as in arguments.
Table 21: Client Argument Storage Responsibilities

Type

Inout Param

Out Param

Return Result

short

1

1

1

long

1

1

1

unsigned short

1

1

1

unsigned long

1

1

1

float

1

1

1

double

1

1

1

boolean

1

1

1

char

1

1

1

octet

1

1

1

enum

1

1

1

object reference ptr

2

2

2

struct, fixed

1

1

1

struct, variable

1

3

3

union, fixed

1

1

1

union, variable

1

3

3

string

4

3

3

sequence

5

3

3

array, fixed

1

1

6

array, variable

1

6

6

any

5

3

3

Table 22: Argument Passing Cases

Case1

1

Caller allocates all necessary storage, except that which may be encapsulated and managed within the parameter itself. For inout parameters, the caller provides the initial value, and the callee may change that value. For out parameters, the caller allocates the storage but need not initialize it, and the callee sets the value. Function returns are by value.

2

Caller allocates storage for the object reference. For inout parameters, the caller provides an initial value; if the callee wants to reassign the inout parameter, it will first call CORBA_Object_release on the original input value. To continue to use an object reference passed in as an inout, the caller must first duplicate the reference. The client is responsible for the release of all out and return object references. Release of all object references embedded in other out and return structures is performed automatically as a result of calling CORBA_free.

3

For out parameters, the caller allocates a pointer and passes it by reference to the callee. The callee sets the pointer to point to a valid instance of the parameter's type. For returns, the callee returns a similar pointer. The callee is not allowed to return a null pointer in either case. In both cases, the caller is responsible for releasing the returned storage. Following the completion of a request, the caller is not allowed to modify any values in the returned storage-to do so, the caller must first copy the returned instance into a new instance, then modify the new instance.

4

For inout strings, the caller provides storage for both the input string and the char* pointing to it. The callee may deallocate the input string and reassign the char* to point to new storage to hold the output value. The size of the out string is therefore not limited by the size of the in string. The caller is responsible for freeing the storage for the out.The callee is not allowed to return a null pointer for an inout, out, or return value.

5

For inout sequences and anys, assignment or modification of the sequence or any may cause deallocation of owned storage before any reallocation occurs, depending upon the state of the boolean release in the sequence or any.

6

For out parameters, the caller allocates a pointer to an array slice, which has all the same dimensions of the original array except the first, and passes the pointer by reference to the callee. The callee sets the pointer to point to a valid instance of the array. For returns, the callee returns a similar pointer. The callee is not allowed to return a null pointer in either case. In both cases, the caller is responsible for releasing the returned storage. Following the completion of a request, the caller is not allowed to modify any values in the returned storage-to do so, the caller must first copy the returned array instance into a new array instance, then modify the new instance.

1 As listed in Table 21 on page 14-19

14.20 Handling Exceptions

The CORBA_Environment type is partially opaque; the C declaration contains at least the following:

typedef struct CORBA_Environment { /* C */

CORBA_exception_type _major;

...

} CORBA_Environment;

Upon return from an invocation, the _major field indicates whether the invocation terminated successfully; _major can have one of the values CORBA_NO_EXCEPTION, CORBA_USER_EXCEPTION, or CORBA_SYSTEM_EXCEPTION; if the value is one of the latter two, then any exception parameters signalled by the object can be accessed.

Three functions are defined on an CORBA_Environment structure for accessing exception information; their signatures are:

extern CORBA_char *CORBA_exception_id(CORBA_Environment *ev); /* C */

extern void *CORBA_exception_value(CORBA_Environment *ev);

extern void CORBA_exception_free(CORBA_Environment *ev);

CORBA_exception_id() returns a pointer to the character string identifying the exception. If invoked on an CORBA_Environment which identifies a non-exception, (_major==CORBA_NO_EXCEPTION) a NULL is returned.

CORBA_exception_value() returns a pointer to the structure corresponding to this exception. If invoked on an CORBA_Environment which identifies a non-exception or an exception for which there is no associated information, a NULL is returned.

CORBA_exception_free() returns any storage which was allocated in the construction of the CORBA_Environment. It is permissible to invoke CORBA_exception_free() regardless of the value of the _major field.

Consider the following example:

interface exampleX {

exception BadCall {
string<80> reason;
};
void op() raises(BadCall);
};

This interface defines a single operation which returns no results and can raise a BadCall exception. The following user code shows how to invoke the operation and recover from an exception:

#include "exampleX.h" /* C */

CORBA_Environment ev;

exampleX obj;

exampleX_BadCall *bc;

/*

* some code to initialize obj to a reference to an object

* supporting the exampleX interface

*/

exampleX_op(obj, &ev);

switch(ev._major) {

case CORBA_NO_EXCEPTION: /* successful outcome*/

/* process out and inout arguments */

break;

case CORBA_USER_EXCEPTION: /* a user-defined exception */

if (strcmp(ex_exampleX_BadCall,CORBA_exception_id(&ev))

== 0) {

bc = (exampleX_BadCall *)CORBA_exception_value(&ev);

fprintf(stderr, "exampleX_op() failed - reason: %s\n",

bc->reason);

}

else { /* should never get here ... */

fprintf( stderr,

"unknown user-defined exception -%s\n",

CORBA_exception_id(&ev));

}

break;

default: /* standard exception */

/*

* CORBA_exception_id() can be used to determine

* which particular standard exception was

* raised; the minor member of the struct

* associated with the exception (as yielded by

* CORBA_exception_value()) may provide additional

* system-specific information about the exception

*/

break;

}

/* free any storage associated with exception */

CORBA_exception_free(&ev);

14.21 Method Routine Signatures

The signatures of the methods used to implement an object depend not only on the language binding, but also on the choice of object adapter. Different object adapters may provide additional parameters to access object adapter-specific features.

Most object adapters are likely to provide method signatures that are similar in most respects to those of the client stubs. In particular, the mapping for the operation parameters expressed in OMG IDL should be the same as for the client side.

See Section 14.25, "BOA: Mapping for Object Implementations," on page 14-27 for the description of method signatures for implementations using the Basic Object Adapter.

14.22 Include Files

Multiple interfaces may be defined in a single source file. By convention, each interface is stored in a separate source file. All OMG IDL compilers will, by default, generate a header file named Foo.h from Foo.idl. This file should be #included by clients and implementations of the interfaces defined in Foo.idl.

Inclusion of Foo.h is sufficient to define all global names associated with the interfaces in Foo.idl and any interfaces from which they are derived.

14.23 Pseudo-objects

In the C language mapping, there are several interfaces that are defined as pseudo-objects; Table 14 on page A-2 lists the pseudo-objects. A client makes calls on a pseudo-object in the same way as an ordinary ORB object. However, the ORB may implement the pseudo-object directly, and there are restrictions on what a client may do with a pseudo-object.

The ORB itself is a pseudo-object with the following partial definition (see Chapter 7, "ORB Interface" for the complete definition):

interface ORB {

string object_to_string (in Object obj);
Object string_to_object (in string str);
};

This means that a C programmer may convert an object reference into its string form by calling:

CORBA_Environment ev; /* C */

CORBA_char *str = CORBA_ORB_object_to_string(orbobj, &ev, obj);

just as if the ORB were an ordinary object. The C library contains the routine CORBA_ORB_object_to_string, and it does not do a real invocation. The orbobj is an object reference that specifies which ORB is of interest, since it is possible to choose which ORB should be used to convert an object reference to a string (see Chapter 7, "ORB Interface" for details on this specific operation).

Although operations on pseudo-objects are invoked in the usual way defined by the C language mapping, there are restrictions on them. In general, a pseudo-object cannot be specified as a parameter to an operation on an ordinary object. Pseudo-objects are also not accessible using the dynamic invocation interface, and do not have definitions in the interface repository.

Operations on pseudo-objects may take parameters that are not permitted in operations on ordinary objects. For example, the set_exception operation on the Basic Object Adapter pseudo-object takes a C (void *) to specify the exception parameters (see Section 14.25.2, "Method Signatures," on page 14-27 for details). Generally, these parameters will be language-mapping specific.

Because the programmer uses pseudo-objects in the same way as ordinary objects, some ORB implementations may choose to implement some pseudo-objects as ordinary objects. For example, assuming it could be efficient enough, a context object might be implemented as an ordinary object.

14.24 Mapping of the Dynamic Skeleton Interface to C

For general information about mapping of the Dyanmic Skeleton Interface to programming languages, refer to Section 5.3, "Dynamic Skeleton Interface: Language Mapping," on page 5-3.

This section contains

14.24.1 Mapping of ServerRequest to C

In the C mapping, a ServerRequest is a pseudo object in the CORBA module that supports the following operations:

CORBA_Identifier CORBA_ServerRequest_op_name (
CORBA_ServerRequest req,
CORBA_Environment *env
);
This function returns the name of the operation being performed, as shown in the operation's OMG IDL specification.

CORBA_Context CORBA_ServerRequest_ctx (
CORBA_ServerRequest req,
CORBA_Environment *env
);
This function may be used to determine any context values passed as part of the operation. Context will only be available to the extent defined in the operation's OMG IDL definition; for example, attribute operations have none.

void CORBA_ServerRequest_params (
CORBA_ServerRequest req,
CORBA_NVList parameters,
CORBA_Environment *env
);
This function is used to retrieve parameters from the ServerRequest, and to find the addresses used to pass pointers to result values to the ORB. It must always be called by each DIR, even when there are no parameters.

The caller passes ownership of the parameters NVList to the ORB. Before this routine is called, that NVList should be initialized with the TypeCodes for each of the parameters to the operation being implemented: in, out, and inout parameters inclusive. When the call returns, the parameters NVList is still usable by the DIR, and all in and inout parameters will have been unmarshaled. Pointers to those parameter values will at that point also be accessible through the parameters NVList.

The implementation routine will then process the call, producing any result values. If the DIR does not need to report an exception, it will replace pointers to inout values in parameters with the values to be returned, and assign pointers to out values in that NVList appropriately as well. When the DIR returns, all the parameter memory is freed as appropriate, and the NVList itself is freed by the ORB.

void CORBA_ServerRequest_result (
CORBA_ServerRequest req,
CORBA_Any value,
CORBA_Environment *env
);
This function is used to report any result value for an operation; if the operation has no result, it must not be called. It also must not be called before the parameters have been retrieved, or if an exception is being reported.

void CORBA_ServerRequest_exception (
CORBA_ServerRequest req,
CORBA_exception_type major,
CORBA_Any value,
CORBA_Environment *env
);
This function is used to report exceptions, both user and system, to the client who made the original invocation. The parameters are as follows:

major indicates whether the exception is a user exception or system exception

value this is the value of the exception, including an exceptionTypeCode.

14.24.2 Mapping of BOA's Dynamic Implementation Routine to C

In C, a DIR is a function with this signature:

typedef void (*DynamicImplementationRoutine) ( /* C */
CORBA_Object target,
CORBA_ServerRequest request,
CORBA_Environment *env
);
Such a function will be invoked by the BOA when an invocation is received on an object reference whose implementation has registered a dynamic skeleton.

target is the name object reference to which the invocation is directed.

request is the ServerRequest used to access explicit parameters and report results (and exceptions).

env may be passed to CORBA_BOA_get_principal if desired.

Unlike other BOA object implementations, the CORBA_BOA_set_exception API is not used. Instead, CORBA_ServerRequest_exception is used; this provides the TypeCode for the exception to the ORB, so it does not need to consult the Interface Repository (or rely on compiled stubs) to marshal the exception value.

14.25 BOA: Mapping for Object Implementations

This section describes the details of the OMG IDL-to-C language mapping that apply specifically to the Basic Object Adapter, such as how the implementation methods are connected to the skeleton.

14.25.1 Operation-specific Details

This chapter defines most of the details of naming of parameter types and parameter passing conventions. Generally, for those parameters that are operation-specific, the method implementing the operation appears to receive the same values that would be passed to the stubs.

14.25.2 Method Signatures

With the BOA, implementation methods have signatures that are identical to the stubs. If the following interface is defined in OMG IDL:

interface example4 { // IDL

long op5(in long arg6);
};

a method for the op5 routine must have the following function signature:

CORBA_long example4_op5( /* C */

example4 object,

CORBA_Environment *ev,

CORBA_long arg6

);

The object parameter is the object reference that was invoked. The method can identify which object was intended by using the get_id BOA operation. The ev parameter is used for authentication on the get_principal BOA operation, and is used for indicating exceptions.

The method terminates successfully by executing a return statement returning the declared operation value. Prior to returning the result of a successful invocation, the method code must assign legal values to all out and inout parameters.

The method terminates with an error by executing the set_exception BOA operation prior to executing a return statement. The set_exception operation has the following C language definition:

void CORBA_BOA_set_exception ( /* C */

CORBA_Object boa,

CORBA_Environment *ev,

CORBA_exception_type major,

CORBA_char *exceptname,

void *param

);

The ev parameter is the environment parameter passed into the method. The caller must supply a value for the major parameter. The value of the major parameter constrains the other parameters in the call as follows:

When raising an exception, the method code is not required to assign legal values to any out or inout parameters. Due to restrictions in C, it must return a legal function value.

14.25.3 Binding Methods to Skeletons

It is not specified as part of the language mapping how the skeletons are connected to the methods. Different means will be used in different environments. For example, the skeletons may make references to the methods that are resolved by the linker or there may be a system-dependent call done at program startup to specify the location of the methods.

14.25.4 BOA and ORB Operations

The operations on the BOA defined earlier in this chapter and the operations on the ORB defined in the ORB Interface chapter are used as if they had the OMG IDL definitions described in the document, and then mapped in the usual way with the C language mapping.

For example, the string_to_object ORB operation has the following signature:

CORBA_Object CORBA_ORB_string_to_object ( /* C */

CORBA_Object orb,

CORBA_Environment *ev,

CORBA_char *objectstring

);

The create BOA operation has the following signature:

CORBA_Object CORBA_BOA_create ( /* C */

CORBA_Object boa,

CORBA_Environment *ev,

CORBA_ReferenceData *id,

CORBA_InterfaceDef intf,

CORBA_ImplementationDef impl

);

Although in each example, we are using an "object" that is special (an ORB, an object adapter, or an object reference), the method name is generated as interface_operation in the same way as ordinary objects. Also, the signature contains an CORBA_Environment parameter for error indications.

In the first two cases, the signature calls for an object reference to represent the particular ORB or object adapter being manipulated. Programs may obtain these objects in a variety of ways, for example, in a global variable before program startup if there is only one ORB or BOA that makes sense, or by obtaining them from a name service if more than one is available. In the third case, the object reference being operated on is specified as the first parameter.

Following the same procedure, the C language binding for the remainder of the ORB, BOA, and object reference operations may be determined.

14.26 ORB and OA/BOA Initialization Operations

ORB Initialization

The following PIDL specifies initialization operations for an ORB; this PIDL is part of the CORBA module (not the ORB interface) and is described in Section 7.4, "ORB Initialization," on page 7-6.

// PIDL

module CORBA {

typedef string ORBid;

typedef sequence <string> arg_list;

ORB ORB_init (inout arg_list argv, in ORBid orb_identifier);

};

The mapping of the preceding PIDL operations to C is as follows:

/* C language mapping */

typedef CORBA_string CORBA_ORBid;

extern CORBA_ORB CORBA_ORB_init (int *argc,

char **argv,

CORBA_ORBid orb_identifier,

CORBA_Environment *env);

The C mapping for ORB_init deviates from the PIDL in its handling of the arg_list parameter. This is intended to provide a meaningful PIDL definition of the initialisation interface which has a natural C (and C++) binding. To this end, the arg_list structure is replaced with argv and argc parameters.

The argv parameter is defined as an unbound array of strings (char **) and the number of strings in the array is passed in the int* parameter.

If a NULL ORBid is used then argv arguments can be used to determine which ORB should be returned. This is achieved by searching the argv parameters for one tagged ORBid, e.g. -ORBid "ORBid_example."

For C, the order of consumption of argv parameters may be significant to an application. In order to ensure that applications are not required to handle argv parameters they do not recognize the ORB initialization function must be called before the remainder of the parameters are consumed. Therefore, after the ORB_init call the argv and argc parameters will have been modified to remove the ORB understood arguments. It is important to note that the ORB_init call can only reorder or remove references to parameters from the argv list, this restriction is made in order to avoid potential memory management problems caused by trying to free parts of the argv list or extending the argv list of parameters. This is why argv is passed as a char** and not a char***.

OA/BOA Initialization

The following PIDL specifies the operations (in the ORB interface) that allow applications to get pseudo object references; it is described in detail in Section 7.5, "OA and BOA Initialization," on page 7-8.

// PIDL

module CORBA {

interface ORB

{

typedef sequence <string> arg_list;

typedef string OAid;

// Template for OA initialization operations

// <OA> <OA>_init (inout arg_list argv,

// in OAid oa_identifier);

BOA BOA_init (inout arg_list argv,

in OAid boa_identifier);

};

}

The mapping of the OAinit (BOA_init) operation (in the ORB interface) to the C programming language is as follows:

/* C language mapping */

typedef CORBA_string CORBA_OAid;

/* Template C binding for <OA>_init */

/*

CORBA_<OA> CORBA_ORB_<OA>_init (CORBA_ORB orb,

int *argc,

char **argv,

CORBA_ORB_OAid boa_identifier,

CORBA_Environment *env);

*/

CORBA_BOA CORBA_ORB_BOA_init (CORBA_ORB orb,

int *argc,

char **argv,

CORBA_ORB_OAid boa_identifier,

CORBA_Environment *env);

The arglist structure from the PIDL definition is replaced in the C mapping with argv and argc parameters. The argv parameters is an unbound array of strings (char**) and the number of strings in the array is passed in the argc (int*).

If a NULL OAid is used then argv arguments can be used to determine which OA should be returned. This is achieved by searching the argv parameters for one tagged OAid, e.g. -OAid "OAid_example".

For C, the order of consumption of argv parameters may be significant to an application. In order to ensure that applications are not required to handle argv parameters they do not recognize the OA initialisation function must be called before the remainder of the parameters are consumed by the application. Therefore, after the <OA>_init call, the argv and argc parameters will have modified to remove the OA understood arguments. It is important to note that the OA_init call can only reorder or remove references to parameters from the argv list, this restriction is made in order to avoid potential memory management problems caused by trying to free parts of the argv list or extending the argv list of parameters. This is why argv is passed as a char** and not a char***.

14.27 Operations for Obtaining Initial Object References

The following PIDL specifies the operations (in the ORB interface) that allow applications to get pseudo object references for the Interface Repository and Object Services. It is described in detail in Section 7.6, "Obtaining Initial Object References," on page 7-10.

// PIDL interface for getting initial object references

module CORBA {

interface ORB {

typedef string ObjectId;

typedef sequence <ObjectId> ObjectIdList;

exception InvalidName {};

ObjectIdList list_initial_services ();

Object resolve_initial_references (in ObjectId identifier)

raises (InvalidName);

}

}

The mapping of the preceding PIDL to C is as follows:

/* C Mapping */

typedef CORBA_string CORBA_ORB_ObjectId;

typedef CORBA_sequence_CORBA_ORB_ObjectId

CORBA_ORB_ObjectIdList;

typedef struct CORBA_ORB_InvalidName CORBA_ORB_InvalidName;

CORBA_ORB_ObjectIdList CORBA_ORB_list_initial_services (

CORBA_ORB orb,

CORBA_Environment *env);

CORBA_Object CORBA_ORB_resolve_initial_references (

CORBA_ORB orb,

CORBA_ORB_ObjectId identifier,

CORBA_Environment *env);



[Top] [Prev] [Next] [Bottom]

1 Section 14.15, "Implicit Arguments to Operations," on page 14-16 describes the additional arguments added to an operation in the C mapping.

2 Transmissible pseudo-objects are listed as "general arguments" in Table 14 on page A-2 .

3 For brevity, the "CORBA_" prefix is omitted from type names in the tables shown here.

pubs@omg.org
Copyright © 1995, Object Management Group. All rights reserved.