Jump to content

SOM IDL and the SOM Compiler: Difference between revisions

From EDM2
No edit summary
Line 1: Line 1:
{{Template:SOMPG}}
{{IBM-Reprint}}
This chapter first discusses how to define SOM classes and then describes the SOM Compiler. To allow a class of objects to be implemented in one programming language and used in another (that is, to allow a SOM class to be language neutral), the interface to objects of this class must be specified separately from the objects' implementation.
This chapter first discusses how to define SOM classes and then describes the SOM Compiler. To allow a class of objects to be implemented in one programming language and used in another (that is, to allow a SOM class to be language neutral), the interface to objects of this class must be specified separately from the objects' implementation.



Revision as of 16:22, 8 August 2020

System Object Model Programming Guide
  1. About This Book
  2. Introduction to the SOMobjects Developer Toolkit
  3. Tutorial for Implementing SOM Classes
  4. Using SOM Classes in Client Programs
  5. SOM IDL and the SOM Compiler
  6. Implementing Classes in SOM
  7. Distributed SOM (DSOM)
  8. The SOM Interface Repository Framework
  9. The Metaclass Framework
  10. The Event Management Framework
  11. SOMobjects Error Codes
  12. SOM IDL Language Grammar
  13. Implementing Sockets Subclasses
  14. Glossary

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation This chapter first discusses how to define SOM classes and then describes the SOM Compiler. To allow a class of objects to be implemented in one programming language and used in another (that is, to allow a SOM class to be language neutral), the interface to objects of this class must be specified separately from the objects' implementation.

To summarize: As a first step, a file known as the .idl file is used to declare classes and their methods, using SOM's language#neutral Interface Definition Language (IDL). Next, the SOM Compiler is run on the .idl file to produce a template implementation file that contains stub method procedures for the new and overridden methods; this preliminary code corresponds to the computer language that will implement the class. Then, the class implementer fills in the stub procedures with code that implements the methods (or redefines overridden methods) and sets instance data. (This implementation process is the subject of Chapter 5, "Implementing Classes in SOM.") At this point, the implementation file can be compiled and linked with a client program that uses it (as described in Chapter 3, "Using SOM Classes in Client Programs").

Syntax for SOM IDL and the SOM Compiler are presented in this chapter, along with helpful information for using them correctly.

Interface vs Implementation

The interface to a class of objects contains the information that a client must know to use an object-namely, the names of its attributes and the signatures of its methods. The interface is described in a formal language independent of the programming language used to implement the object's methods. In SOM, the formal language used to define object interfaces is the Interface Definition Language (IDL), standardized by CORBA.

The implementation of a class of objects (that is, the procedures that implement methods) is written in the implementer's preferred programming language. This language can be object-oriented (for instance, C++) or procedural (for instance, C).

A completely implemented class definition, then, consists of two main files:

  • An IDL specification of the interface to instances of the class (the interface definition file (or .idl file) and
  • Method procedures written in the implementer's language of choice (the implementation file).

The interface definition file has an .idl extension, as noted. The implementation file, however, has an extension specific to the language in which it is written. For example, implementations written in C have a .c extension, and implementations written in C++ have a .C (for AIX) or .cpp (for OS/2) extension.

To assist users in implementing SOM classes, the SOMobjects Toolkit provides a SOM Compiler. The SOM Compiler takes as input an object interface definition file (the .idl file) and produces a set of binding files that make it convenient to implement and use a SOM class whose instances are objects that support the defined interface. The binding files and their purposes are as follows:

  • An implementation template that serves as a guide for how the implementation file for the class should look. The class implementer fills in this template file with language-specific code to implement the methods that are available on the class' instances.
  • Header files to be included (a) in the class's implementation file and (b) in client programs that use the class.

These binding files produced by the SOM Compiler bridge the gap between SOM and the object model used in object-oriented languages (such as C++), and they allow SOM to be used with non-object-oriented languages (such as C). The SOM Compiler currently produces binding files for the C and C++ programming languages. SOM can also be used with other programming languages; the bindings simply offer a more convenient programmer's interface to SOM. Vendors of other languages may also offer SOM bindings; check with your language vendor for possible SOM support.

The subsequent sections of this chapter provide full syntax for SOM IDL and the SOM Compiler.

SOM Interface Definition Language

This section describes the syntax of SOM's Interface Definition Language (SOM IDL). SOM IDL complies with CORBA's standard for IDL; it also adds constructs specific to SOM. (For more information on the CORBA standard for IDL, see The Common Object Request Broker: Architecture and Specification, published by Object Management Group and x/Open.) The full grammar for SOM IDL is given in Appendix C. Instructions for converting existing OIDL-syntax files to IDL are given in Appendix B. The current section describes the syntax and semantics of SOM IDL using the following conventions:

Constants (words to be used literally, such as keywords) appear in bold. User-supplied elements appear in italics.

{ } Groups related items together as a single item. 
[] Encloses an optional item. 
* Indicates zero or more repetitions of the preceding item. 
+ Indicates one or more repetitions of the preceding item. 
| Separates alternatives. 
_ Within a set of alternatives, an underscore indicates the default, if defined. 

IDL is a formal language used to describe object interfaces. Because, in SOM, objects are implemented as instances of classes, an IDL object interface definition specifies for a class of objects what methods (operations) are available, their return types, and their parameter types. For this reason, we often speak of an IDL specification for a class (as opposed to simply an object interface). Constructs specific to SOM discussed below further strengthen this connection between SOM classes and the IDL language.

IDL generally follows the same lexical rules as C and C++, with some exceptions. In particular:

  • IDL uses the ISO Latin-1 (8859.1) character set (as per the CORBA standard).
  • White space is ignored except as token delimiters.
  • C and C++ comment styles are supported.
  • IDL supports standard C/C++ preprocessing, including macro substitution, conditional compilation, and source file inclusion.
  • Identifiers (user-defined names for methods, attributes, instance variables, and so on) are composed of alphanumeric and underscore characters, (with the first character alphabetic) and can be of arbitrary length, up to an operating-system limit of about 250 characters.
  • Identifiers must be spelled consistently with respect to case throughout a specification.
  • Identifiers that differ only in case yield a compilation error.
  • There is a single name space for identifiers (thus, using the same identifier for a constant and a class name within the same naming scope, for example, yields a compilation error).
  • Integer, floating point, character, and string literals are defined just as in C and C++.

The terms listed in the Keywords table are reserved keywords and may not be used otherwise. Keywords must be spelled using upper- and lower-case characters exactly as shown in the table. For example, "void" is correct, but "Void" yields a compilation error.

A typical IDL specification for a single class, residing in a single .idl file, has the following form. (Also see the later section, "Module declarations to define multiple interfaces in an .idl file.") The order is unimportant, except that names must be declared (or forward referenced) before they are referenced. The subsequent topics of this section describe the requirements for these specifications:

Include directives (optional)

Type declarations (optional)

Constant declarations (optional)

Exception declarations (optional)

Interface declaration (optional)

Module declaration (optional)


Keywords for SOM IDL
┌─────────────────────────┬─────────────────────────┬─────────────────────────┐
│any                      │FALSE                    │readonly                 │
├─────────────────────────┼─────────────────────────┼─────────────────────────┤
│attribute                │float                    │sequence                 │
├─────────────────────────┼─────────────────────────┼─────────────────────────┤
│boolean                  │implementation           │short                    │
├─────────────────────────┼─────────────────────────┼─────────────────────────┤
│case                     │in                       │string                   │
├─────────────────────────┼─────────────────────────┼─────────────────────────┤
│char                     │inout                    │struct                   │
├─────────────────────────┼─────────────────────────┼─────────────────────────┤
│class                    │interface                │switch                   │
├─────────────────────────┼─────────────────────────┼─────────────────────────┤
│const                    │long                     │TRUE                     │
├─────────────────────────┼─────────────────────────┼─────────────────────────┤
│context                  │module                   │TypeCode                 │
├─────────────────────────┼─────────────────────────┼─────────────────────────┤
│default                  │octet                    │typedef                  │
├─────────────────────────┼─────────────────────────┼─────────────────────────┤
│double                   │oneway                   │unsigned                 │
├─────────────────────────┼─────────────────────────┼─────────────────────────┤
│enum                     │out                      │union                    │
├─────────────────────────┼─────────────────────────┼─────────────────────────┤
│exception                │raises                   │void                     │
└─────────────────────────┴─────────────────────────┴─────────────────────────┘

Include directives

The IDL specification for a class normally contains #include statements that tell the SOM Compiler where to find the interface definitions (the .idl files) for:

  • Each of the class's parent (direct base) classes, and
  • The class's metaclass (if specified).

The #include statements must appear in the above order. For example, if class "C" has parents "foo" and "bar" and metaclass "meta", then file "C.idl" must begin with the following #include statements:

  #include <foo.idl>
  #include <bar.idl>
  #include <meta.idl>

As in C and C++, if a filename is enclosed in angle brackets (< >), the search for the file will begin in system-specific locations. If the filename appears in double quotation marks (" "), the search for the file will begin in the current working directory, then move to the system-specific locations.

Type and constant declarations

IDL specifications may include type declarations and constant declarations as in C and C++, with the restrictions/extensions described below. [Note: For any reader not familiar with C, a recommended reference is The C Programming Language (2nd edition, 1988, Prentice Hall) by Brian W. Kernighan and Dennis M. Ritchie. See pages 36-40 for a discussion of type and constant declarations.]

IDL supports the following basic types (these basic types are also defined for C and C++ client and implementation programs, using the SOM bindings):

Integral types

IDL supports only the integral types short, long, unsigned short, and unsigned long, which represent the following value ranges:

short -2[15] .. 2[15]-1 
long -2[31] .. 2[31]-1 
unsigned short 0 .. 2[16]-1 
unsigned long 0 .. 2[32]-1 

Floating point types

IDL supports the float and double floating-point types. The float type represents the IEEE single-precision floating-point numbers; double represents the IEEE double-precision floating-point numbers.

Character type

IDL supports a char type, which represents an 8-bit quantity. The ISO Latin-1 (8859.1) character set defines the meaning and representation of graphic characters. The meaning and representation of null and formatting characters is the numerical value of the character as defined in the ASCII (ISO 646) standard. Unlike C/C++, type char cannot be qualified as signed or unsigned. (The octet type, below, can be used in place of unsigned char.)

Boolean type

IDL supports a boolean type for data items that can take only the values TRUE and FALSE.

Octet type

IDL supports an octet type, an 8-bit quantity guaranteed not to undergo conversion when transmitted by the communication system. The octet type can be used in place of the unsigned char type.

Any type

IDL supports an any type, which permits the specification of values of any IDL type. In the SOM C and C++ bindings, the any type is mapped onto the following struct:

  typedef struct any {
      TypeCode _type;
      void *_value;
  } any;


The "_value" member for an any type is a pointer to the actual value. The "_type" member is a pointer to an instance of a TypeCode that represents the type of the value. The TypeCode provides functions for obtaining information about an IDL type. Chapter 7, "The Interface Repository Framework," describes TypeCodes and their associated functions.

Constructed types

In addition to the above basic types, IDL also supports three constructed types: struct, union, and enum. The structure and enumeration types are specified in IDL the same as they are in C and C++ [Kernighan-Ritchie references: struct, p. 128; union, p. 147; enum, p. 39], with the following restrictions:

Unlike C/C++, recursive type specifications are allowed only through the use of the sequence template type (see below).

Unlike C/C++, structures, discriminated unions, and enumerations in IDL must be tagged. For example, "struct { int a; ... }" is an invalid type specification. The tag introduces a new type name.

In IDL, constructed type definitions need not be part of a typedef statement; furthermore, if they are part of a typedef statement, the tag of the struct must differ from the type name being defined by the typedef. For example, the following are valid IDL struct and enum definitions:

  struct myStruct {
     long x;
     double y;
  };                               /* defines type name myStruct*/
  enum colors { red, white, blue };  /* defines type name colors */


By contrast, the following definitions are not valid:

  typedef struct myStruct {        /*  NOT VALID  */
     long x;
     double y;
  } myStruct;                      /* myStruct has been redefined */
  typedef enum colors { red, white, blue } colors;  /* NOT VALID */


The valid IDL struct and enum definitions shown above are translated by the SOM Compiler into the following definitions in the C and C++ bindings, assuming they were declared within the scope of interface "Hello":

  typedef struct Hello_myStruct {  /* C/C++ bindings for IDL struct */
     long x;
     double y;
  } Hello_myStruct;
  typedef unsigned long Hello_colors; /* C/C++ bindings for IDL enum */
  #define Hello_red 1UL
  #define Hello_white 2UL
  #define Hello_blue 3UL


When an enumeration is defined within an interface statement for a class, then within C/C++ programs, the enumeration names must be referenced by prefixing the class name. For example, if the colors enum, above, were defined within the interface statement for class Hello, then the enumeration names would be referenced as Hello_red, Hello_white, and Hello_blue. Notice the first identifier in an enumeration is assigned the value 1.

All types and constants generated by the SOM Compiler are fully qualified. That is, prepended to them is the fully qualified name of the interface or module in which they appear. For example, consider the following fragment of IDL:

  module M {
      typedef long long_t;
      module  N {
          typedef long long_t;
          interface I {
              typedef long long_t;
          };
      };
  };


That specification would generate the following three types:

  typedef long  M_long_t;
  typedef long  M_N_long_t;
  typedef long  M_N_I_long_t;


For programmer convenience, the SOM Compiler also generates shorter bindings, without the interface qualification. Consider the next IDL fragment:

  module M {
      typedef long long_t;
      module  N {
          typedef short short_t;
          interface I {
              typedef char char_t;
          };
      };
  };


In the C/C++ bindings of the preceding fragment, you can refer to "M_long_t" as "long_t", to "M_N_short_t" as "short_t", and to "M_N_I_char_t" as "char_t".

However, these shorter forms are available only when their interpretation is not ambiguous. Thus, in the first example the shorthand for "M_N_I_long_t" would not be allowed, since it clashes with "M_long_t" and "M_N_long_t". If these shorter forms are not required, they can be ignored by setting #define SOM_DONT_USE_SHORT_NAMES before including the public header files, or by using the SOM Compiler option -mnouseshort so that they are not generated in the header files.

In the SOM documentation and samples, both long and short forms are illustrated, for both type names and method calls. It is the responsibility of each user to adopt a style according to personal preference. It should be noted, however, that CORBA specifies that only the long forms must be present.

Union type

IDL also supports a union type, which is a cross between the C union and switch statements. The syntax of a union type declaration is as follows: union identifier switch ( switch-type )

   { case+ } 

The "identifier" following the union keyword defines a new legal type. (Union types may also be named using a typedef declaration.) The "switch-type" specifies an integral, character, boolean, or enumeration type, or the name of a previously defined integral, boolean, character, or enumeration type. Each "case" of the union is specified with the following syntax:

case-label+ type-spec declarator ;

where "type-spec" is any valid type specification; "declarator" is an identifier, an array declarator (such as, foo[3][5]), or a pointer declarator (such as, *foo); and each "case-label" has one of the following forms:

case const-expr: default:

The "const-expr" is a constant expression that must match or be automatically castable to the "switch-type". A default case can appear no more than once.

Unions are mapped onto C/C++ structs. For example, the following IDL declaration:

  union Foo switch (long) {
    case 1: long x;
    case 2: float y;
    default: char z;
  };


is mapped onto the following C struct:

  typedef Hello_struct {
    long _d;
    union {
      long x;
      float y;
      char z;
    } _u;
  } Hello_foo;


The discriminator is referred to as "_d", and the union in the struct is referred to as "_u". Hence, elements of the union are referenced just as in C:

  Foo v;
  /* get a pointer to 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;
  }


Note: This example is from The Common Object Request Broker: Architecture and Specification, revision 1.1, page 90.

Template types (sequences and strings)

IDL defines two template types not found in C and C++: sequences and strings. A sequence is a one-dimensional array with two characteristics: a maximum size (specified at compile time) and a length (determined at run time). Sequences permit passing unbounded arrays between objects. Sequences are specified as follows:

sequence < simple-type [, positive-integer-const] >

where "simple-type" specifies any valid IDL type, and the optional "positive-integer-const" is a constant expression that specifies the maximum size of the sequence (as a positive integer).

Note: The "simple-type" cannot have a '*' directly in the sequence statement. Instead, a typedef for the pointer type must be used. For example, instead of:

  typedef sequence<long *> seq_longptr; // Error: '*' not allowed.


use:

  typedef long * longptr;
  typedef sequence<longptr> seq_longptr;  // Ok.


In SOM's C and C++ bindings, sequences are mapped onto structs with the following members:

unsigned long _maximum; unsigned long _length; simple-type *_buffer;

where "simple-type" is the specified type of the sequence. For example, the IDL declaration

  typedef sequence<long, 10> vec10;


results in the following C struct:

  #ifndef _IDL_SEQUENCE_long_defined
  #define _IDL_SEQUENCE_long_defined
  typedef struct {
      unsigned long _maximum;
      unsigned long _length;
      long *_buffer;
  } _IDL_SEQUENCE_long;
  #endif /* _IDL_SEQUENCE_long_defined */
  typedef _IDL_SEQUENCE_long vec10;


and an instance of this type is declared as follows:

  vec10 v = {10L, 0L, (long *)NULL};


The "_maximum" member designates the actual size of storage allocated for the sequence, and the "_length" member designates the number of values contained in the "_buffer" member. For bounded sequences, it is an error to set the "_length" or "_maximum" member to a value larger than the specified bound of the sequence.

Before a sequence is passed as the value of an "in" or "inout" method parameter, the "_buffer" member must point to an array of elements of the appropriate type, and the "_length" member must contain the number of elements to be passed. (If the parameter is "inout" and the sequence is unbounded, the "_maximum" member must also be set to the actual size of the array. Upon return, "_length" will contain the number of values copied into "_buffer", which must be less than "_maximum".) When a sequence is passed as an "out" method parameter or received as the return value, the method procedure allocates storage for "_buffer" as needed, the "_length" member contains the number of elements returned, and the "_maximum" member contains the number of elements allocated. (The client is responsible for subsequently freeing the memory pointed to by "_buffer".)

C and C++ programs using SOM's language bindings can refer to sequence types as:

_IDL_SEQUENCE_type

where "type" is the effective type of the sequence members. For example, the IDL type sequence<long,10> is referred to in a C/C++ program by the type name _IDL_SEQUENCE_long. If longint is defined via a typedef to be type long, then the IDL type sequence<longint,10> is also referred to by the type name _IDL_SEQUENCE_long.

If the typedef is for a pointer type, then the effective type is the name of the pointer type. For example, the following statements define a C/C++ type _IDL_SEQUENCE_longptr and not _IDL_SEQUENCE_long:

  typedef long * longptr;
  typedef sequence<longptr> seq_longptr;


A string is similar to a sequence of type char. It can contain all possible 8-bit quantities except NULL. Strings are specified as follows:

string [ < positive-integer-const > ]

where the optional "positive-integer-const" is a constant expression that specifies the maximum size of the string (as a positive integer, which does not include the extra byte to hold a NULL as required in C/C++). In SOM's C and C++ bindings, strings are mapped onto zero-byte terminated character arrays. The length of the string is encoded by the position of the zero-byte. For example, the following IDL declaration:

  typedef string<10> foo;


is converted to the following C/C++ typedef:

  typedef char *foo;


A variable of this type is then declared as follows:

  foo s = (char *) NULL;


C and C++ programs using SOM's language bindings can refer to string types by the type name string.

When an unbounded string is passed as the value of an "inout" method parameter, the returned value is constrained to be no longer than the input value. Hence, using unbounded strings as "inout" parameters is not advised.

Arrays

Multidimensional, fixed-size arrays can be declared in IDL as follows:

identifier {[ positive-integer-const ] }+ 

where the "positive-integer-const" is a constant expression that specifies the array size, in each dimension, as a positive integer. The array size is fixed at compile time.

Pointers

Although the CORBA standard for IDL does not include them, SOM IDL offers pointer types. Declarators of a pointer type are specified as in C and C++:

type *declarator 

where "type" is a valid IDL type specification and "declarator" is an identifier or an array declarator.

Object types

The name of the interface to a class of objects can be used as a type. For example, if an IDL specification includes an interface declaration (described below) for a class (of objects) "C1", then "C1" can be used as a type name within that IDL specification. When used as a type, an interface name indicates a pointer to an object that supports that interface. An interface name can be used as the type of a method argument, as a method return type, or as the type of a member of a constructed type (a struct, union, or enum). In all cases, the use of an interface name implicitly indicates a pointer to an object that supports that interface.

As explained in Using SOM Classes in Client Programs, SOM's C usage bindings for SOM classes also follow this convention. However, within SOM's C++ bindings, the pointer is made explicit, and the use of an interface name as a type refers to a class instance itself, rather than a pointer to a class instance. For example, to declare a variable "myobj" that is a pointer to an instance of class "Foo" in an IDL specification and in a C program, the following declaration is required:

  Foo myobj;


However, in a C++ program, the following declaration is required:

  Foo *myobj;


If a C programmer uses the SOM Compiler option -maddstar, then the bindings generated for C will also require an explicit '*' in declarations. Thus,

Foo myobj; in IDL requires

Foo *myobj; in both C and C++ programs

This style of bindings for C is permitted for two reasons:

  • It more closely resembles the bindings for C++, thus making it easier to change to the C++ bindings at a later date; and
  • It is required for compatibility with existing SOM OIDL code.

Note: The same C and C++ binding emitters should not be run in the same SOM Compiler command. For example,

  sc "-sh;xh" cls.idl    // Not valid.


If you wish to generate both C and C++ bindings, you should issue the commands separately:

  sc -sh cls.idl
  sc -sxh cls.idl


Exception declarations

IDL specifications may include exception declarations, which define data structures to be returned when an exception occurs during the execution of a method. (IDL exceptions are implemented by simply passing back error information after a method call, as opposed to the "catch/throw" model where an exception is implemented by a long jump or signal.) Associated with each type of exception is a name and, optionally, a struct-like data structure for holding error information. Exceptions are declared as follows:

exception identifier { member* }; 

The "identifier" is the name of the exception, and each "member" has the following form:

type-spec declarators ; 

where "type-spec" is a valid IDL type specification and "declarators" is a list of identifiers, array declarators, or pointer declarators, delimited by commas. The members of an exception structure should contain information to help the caller understand the nature of the error. The exception declaration can be treated like a struct definition; that is, whatever you can access in an IDL struct, you can access in an exception declaration. Alternatively, the structure can be empty, whereby the exception is just identified by its name.

If an exception is returned as the outcome of a method, the exception "identifier" indicates which exception occurred. The values of the members of the exception provide additional information specific to the exception. The topic "Method declarations" below describes how to indicate that a particular method may raise a particular exception, and Using SOM Classes in Client Programs, describes how exceptions are handled, in the section entitled "Exceptions and error handling."

Following is an example declaration of a "BAD_FLAG" exception:

  exception BAD_FLAG { long ErrCode; char Reason[80]; };


The SOM Compiler will map the above exception declaration to the following C language constructs:

  #define ex_BAD_FLAG "::BAD_FLAG"
  typedef struct BAD_FLAG {
      long  ErrCode;
      char  Reason[80];
  } BAD_FLAG;


Thus, the ex_BAD_FLAG symbol can be used as a shorthand for naming the exception.

An exception declaration within an interface "Hello", such as this:

  interface Hello {
      exception LOCAL_EXCEPTION { long ErrCode; };
  };


would map onto:

  #define ex_Hello_LOCAL_EXCEPTION "::Hello::LOCAL_EXCEPTION"
  typedef struct Hello_LOCAL_EXCEPTION {
      long  ErrCode;
  } Hello_LOCAL_EXCEPTION;
  #define ex_LOCAL_EXCEPTION ex_Hello_LOCAL_EXCEPTION


In addition to user-defined exceptions, there are several predefined exceptions for system run-time errors. The standard exceptions as prescribed by CORBA are shown in the table "Standard Exceptions Defined by CORBA". These exceptions correspond to standard run-time errors that may occur during the execution of any method (regardless of the list of exceptions listed in its IDL specification).

Each of the standard exceptions has the same structure: an error code (to designate the subcategory of the exception) and a completion status code. For example, the NO_MEMORY standard exception has the following definition:

  enum completion_status {YES, NO, MAYBE};
  exception NO_MEMORY { unsigned long minor;
                        completion_status completed; };


The "completion_status" value indicates whether the method was never initiated (NO), completed its execution prior to the exception (YES), or the completion status is indeterminate (MAYBE).

Since all the standard exceptions have the same structure, somcorba.h (included by som.h) defines a generic StExcep typedef which can be used instead of the specific typedefs:

  typedef struct StExcep {
       unsigned long minor;
       completion_status completed;
  } StExcep;


The standard exceptions shown in the table "Standard Exceptions Defined by CORBA". are defined in an IDL module called StExcep, in the file called stexcep.idl, and the C definitions can be found in stexcep.h.


Standard Exceptions Defined by CORBA

module StExcep {
  #define ex_body { unsigned long minor; completion_status completed; }

  enum completion_status { YES, NO, MAYBE };

  enum exception_type {NO_EXCEPTION, USER_EXCEPTION, SYSTEM_EXCEPTION};

  exception UNKNOWN              ex_body;   // the unknown exception
  exception BAD_PARAM            ex_body;   // an invalid parameter was passed
  exception NO_MEMORY            ex_body;   // dynamic memory allocation failure
  exception IMP_LIMIT            ex_body;   // violated implementation limit
  exception COMM_FAILURE         ex_body;   // communication failure
  exception INV_OBJREF           ex_body;   // invalid object reference
  exception NO_PERMISSION        ex_body;   // no permission for attempted op.
  exception INTERNAL             ex_body;   // ORB internal error
  exception MARSHAL              ex_body;   // error marshalling param/result
  exception INITIALIZE           ex_body;   // ORB initialization failure
  exception NO_IMPLEMENT         ex_body;   // op. implementation unavailable
  exception BAD_TYPECODE         ex_body;   // bad typecode
  exception BAD_OPERATION        ex_body;   // invalid operation
  exception NO_RESOURCES         ex_body;   // insufficient resources for
                                 request
  exception NO_RESPONSE          ex_body;   // response to req. not yet
                                 available
  exception PERSIST_STORE        ex_body;   // persistent storage failure
  exception BAD_INV_ORDER        ex_body;   // routine invocations out
                                 of order
  exception TRANSIENT            ex_body;   // transient failure - reissue
                                 request
  exception FREE_MEM             ex_body;   // cannot free memory
  exception INV_IDENT            ex_body;   // invalid identifier syntax
  exception INV_FLAG             ex_body;   // invalid flag was specified
  exception INTF_REPOS           ex_body;   // error accessing interface
                                 repository
  exception CONTEXT              ex_body;   // error processing context object
  exception OBJ_ADAPTER          ex_body;   // failure detected by object adapter
  exception DATA_CONVERSION      ex_body;   // data conversion error
};

Interface declarations

The IDL specification for a class of objects must contain a declaration of the interface these objects will support. Because, in SOM, objects are implemented using classes, the interface name is always used as a class name as well. Therefore, an interface declaration can be understood to specify a class name, and its parent (direct base) class names. This is the approach used in the following description of an interface declaration. In addition to the class name and its parents names, an interface indicates new methods (operations), and any constants, type definitions, and exception structures that the interface exports. An interface declaration has the following syntax:

interface class-name [: parent-class1, parent-class2, ...]
{ 
constant declarations           (optional)
type declarations               (optional)
exception declarations           (optional)
attribute declarations           (optional)
method declarations             (optional)
implementation statement         (optional)
};

Many class implementers distinguish a "class-name" by using an initial capital letter, but that is optional. The "parent-class" (or base-class) names specify the interfaces from which the interface of "class-name" instances is derived. Parent-class names are required only for the immediate parent(s). Each parent class must have its own IDL specification (which must be #included in the subclass's .idl file). A parent class cannot be named more than once in the interface statement header.

Note: In general, an "interface class-name" header must precede any subsequent implementation that references "class-name." For more discussion of multiple interface statements, refer to the later topic "Module declarations to define multiple interfaces in an .idl file."

The following topics describe the various declarations/statements that can be specified within the body of an interface declaration. The order in which these declarations are specified is usually optional, and declarations of different kinds can be intermixed. Although all of the declarations/statements are listed above as "optional," in some cases using one of them may mandate another. For example, if a method raises an exception, the exception structure must be defined beforehand. In general, types, constants, and exceptions, as well as interface declarations, must be defined before they are referenced, as in C/C++.

Constant, type, and exception declarations

The form of a constant, type, or exception declaration within the body of an interface declaration is the same as described previously in this chapter. Constants and types defined within an interface for a class are transferred by the SOM Compiler to the binding files it generates for that class, whereas constants and types defined outside of an interface are not.

Global types (such as, those defined outside of an interface and module) can be emitted by surrounding them with the following #pragmas:

   #pragma somemittypes on
        typedef sequence <long,10> vec10;
        exception BAD_FLAG { long ErrCode; char Reason[80]; };
        typedef long long_t;
   #pragma somemittypes off

Types, constants, and exceptions defined in a parent class are also accessible to the child class. References to them, however, must be unambiguous. Potential ambiguities can be resolved by prefacing a name with the name of the class that defines it, separated by the characters "::" as illustrated below:

  MyParentClass::myType


The child class can redefine any of the type, constant, and exception names that have been inherited, although this is not advised. The derived class cannot, however, redefine attributes or methods. It can only replace the implementation of methods through overriding (as in example 3 of the Tutorial). To refer to a constant, type, or exception "name" defined by a parent class and redefined by "class-name," use the "parent-name::name" syntax as before.

Note: A name reference such as MyParentClass::myType required in IDL syntax is equivalent to MyParentClass_myType in C/C++. For a full discussion of name recognition in SOM, see "Scoping and name resolution" later in this chapter.

Attribute declarations

Declaring an attribute as part of an interface is equivalent to declaring two accessor methods: one to retrieve the value of the attribute (a "get" method, named "_get_<attributeName>") and one to set the value of the attribute (a "set" method, named "_set_<attributeName>").

Attributes are declared as follows:

[ readonly ] attribute   type-spec declarators ; 

where "type-spec" specifies any valid IDL type and "declarators" is a list of identifiers or pointer declarators, delimited by commas. (An array declarator cannot be used directly when declaring an attribute, but the type of an attribute can be a user-defined type that is an array.) The optional readonly keyword specifies that the value of the attribute can be accessed but not modified by client programs. (In other words, a readonly attribute has no "set" method.) Below are examples of attribute declarations, which are specified within the body of an interface statement for a class:

  interface Goodbye: Hello, SOMObject
  {
    void  sayBye();
    attribute short xpos;
    attribute char c1, c2;
    readonly attribute float xyz;
  };


The preceding attribute declarations are equivalent to defining the following methods:

  short _get_xpos();
  void  _set_xpos(in short xpos);
  char  _get_c1();
  void  _set_c1(in char c1);
  char  _get_c2();
  void  _set_c2(in char c2);
  float _get_xyz();


Note: Although the preceding attribute declarations are equivalent to the explicit method declarations above, these method declarations are not legal IDL, because the method names begin with an '_'. All IDL identifiers must begin with an alphabetic character, not including '_'.

Attributes are inherited from ancestor classes (indirect base classes). An inherited attribute name cannot be redefined to be a different type.

Method (operation) declarations

Method (operation) declarations define the interface of each method introduced by the class. A method declaration is similar to a C/C++ function definition: [oneway] type-spec identifier ( parameter-list) [raises-expr] [context-expr] ; where "identifier" is the name of the method and "type-spec" is any valid IDL type (or the keyword void, indicating that the method returns no value). Unlike C and C++ procedures, methods that do not return a result must specify void as their return type. The remaining syntax of a method declaration is elaborated in the following subtopics.

Note: Although IDL does not allow methods to receive and return values whose type is a pointer to a function, it does allow methods to receive and return method names (as string values). Thus, rather than defining methods that pass pointers to functions (and that subsequently invoke those functions), programmers should instead define methods that pass method names (and subsequently invoke those methods using one of the SOM-supplied method-dispatching or method-resolution methods or functions, such as somDispatch).

Oneway keyword

The optional oneway keyword specifies that when a client invokes the method, the invocation semantics are "best-effort", which does not guarantee delivery of the call. "Best-effort" implies that the method will be invoked at most once. A oneway method should not have any output parameters and should have a return type of void. A oneway method also should not include a "raises expression" (see below), although it may raise a standard exception.

If the oneway keyword is not specified, then the method has "at-most-once" invocation semantics if an exception is raised, and it has "exactly-once" semantics if the method succeeds. This means that a method that raises an exception has been executed zero or one times, and a method that succeeds has been executed exactly once.

Note: Currently the "oneway" keyword, although accepted, has no effect on the C/C++ bindings that are generated.

Parameter list

The "parameter-list" contains zero or more parameter declarations for the method, delimited by commas. (The target object for the method is not explicitly specified as a method parameter in IDL, nor are the Environment or Context parameters.) If there are no explicit parameters, the syntax "( )" must be used, rather than "(void)". A parameter declaration has the following syntax:

{ in Æ out Æ inout } type-spec declarator

where "type-spec" is any valid IDL type and "declarator" is an identifier, array declarator, or pointer declarator.

In, out, inout parameters: The required inÆoutÆinout directional attribute indicates whether the parameter is to be passed from client to server (in), from server to client (out), or in both directions (inout). A method must not modify an in parameter. If a method raises an exception, the values of the return result and the values of the out and inout parameters (if any) are undefined. When an unbounded string or sequence is passed as an inout parameter, the returned value must be no longer than the input value.

The following are examples of valid method declarations in SOM IDL:

  short meth1(in char c, out float f);
  oneway void meth2(in char c);
  float meth3();


Classes derived from SOMObject can declare methods that take a pointer to a block of memory containing a variable number of arguments, using a final parameter of type va_list. The va_list must use the parameter name "ap", as in the following example:

  void MyMethod(in short numArgs, in va_list ap);


For in parameters of type array, C and C++ clients must pass the address of the first element of the array. For in parameters of type struct, union, sequence or any, C/C++ clients must pass the address of a variable of that type, rather than the variable itself.

For all IDL types except arrays, if a parameter of a method is out or inout, then C/C++ clients must pass the address of a variable of that type (or the value of a pointer to that variable) rather than the variable itself. (For example, to invoke method "meth1" above, a pointer to a variable of type float must be passed in place of parameter "f".) For arrays, C/C++ clients must pass the address of the first element of the array.

If the return type of a method is a struct, union, sequence, or any type, then for C/C++ clients, the method returns the value of the C/C++ struct representing the IDL struct, union, sequence, or any. If the return type is string, then the method returns a pointer to the first character of the string. If the return type is array, then the method returns a pointer to the first element of the array.

The pointers implicit in the parameter types and return types for IDL method declarations are made explicit in SOM's C and C++ bindings. Thus, the stub procedure that the SOM Compiler generates for method "meth1", above, has the following signature:

  SOM_Scope short  SOMLINK meth1(char c, float *f)


For C and C++ clients, if a method has an out parameter of type string sequence, or any, then the method must allocate the storage for the string, for the "_buffer" member of the struct that represents the sequence, or for the "_value" member of the struct that represents the any. It is then the responsibility of the client program to free the storage when it is no longer needed. Similarly, if the return type of a method is string, sequence, array, or any, then storage must be allocated by the method, and it will be the responsibility of the client program to subsequently free it.

Note: The foregoing description also applies for the _get_ <attributeName> method associated with an attribute of type string, sequence, any, or array. Hence, the attribute should be specified with a "noget" modifier to override automatic implementation of the attribute's "get" method. Then, needed memory can be allocated by the developer's "get" method implementation and subsequently deallocated by the caller. (The "noget" modifier is described under the topic "Modifier statements" later in this section.)

Raises expression

The optional raises expression ("raises-expr") in a method declaration indicates which exceptions the method may raise. (IDL exceptions are implemented by simply passing back error information after a method call, as opposed to the "catch/throw" model where an exception is implemented by a long jump or signal.) A raises expression is specified as follows:

raises ( identifier1, identifier2,... ) 

where each "identifier" is the name of a previously defined exception. In addition to the exceptions listed in the raises expression, a method may also signal any of the standard exceptions. Standard exceptions, however, should not appear in a raises expression. If no raises expression is given, then a method can raise only the standard exceptions. (See the earlier topic "Exception declarations" for information on defining exceptions and for the list of standard exceptions. See Chapter 3, the section entitled "Exceptions and error handling," for information on using exceptions.)

Context expression

The optional context expression ("context-expr") in a method declaration indicates which elements of the client's context the method may consult. A context expression is specified as follows:

context ( identifier1, identifier2, ... ) 

where each "identifier" is a string literal made up of alphanumeric characters, periods, underscores, and asterisks. (The first character must be alphabetic, and an asterisk can only appear as the last character, where it serves as a wildcard matching any characters. If convenient, identifiers may consist of period-separated valid identifier names, but that form is optional.)

The Context is a special object that is specified by the CORBA standard. It contains a properly list - a set of property-name/string-value pairs that the client can use to store information about its environment that methods may find useful. It is used in much the same way as environment variables. It is passed as an additional (third) parameter to CORBA-compliant methods that are defined as "context-sensitive" in IDL, along with the CORBA-defined Environment structure.

The context expression of a mehod declaration in IDL specifies which property names the method uses. If these properties are present in the Context object supplied by the client, they will be passed to the object implementation, which can access them via the get_values method of the Context object. However, the argument that is passed to the method having a context expression is a Context object, not the names of the properties. The client program must either create a Context object and use the set_values or set_one_value method of the Context class to set the context properties, or use the get_default_context method. The client program then passes the Context object in the method invocation. Note that the CORBA standard also allows properties in addition to those in the context expression to be passed in the Context object.

In Chapter 3, "Using SOM Classes in Client Programs," the topic "Invoking Methods" describes the placement of a context parameter in a method call. See also Chapter 6 of The Common Object Request Broker: Architecture and Specification for a discussion of how clients associate values with context identifiers. A description of the Context class and its methods is contained in the SOMobjects Developer Toolkit: Programmers Reference Manual.

Implementation statements

Modifier statements

SOM Compiler unqualified modifiers

SOM Compiler qualified modifiers

Passthru statements

Declaring instance variables and staticdata variables

Introducing non-IDL data types or classes

Comments within a SOM IDL file

Designating 'private' methods and attributes

Module declarations to define multiple interfaces in a .idl file

Scooping and name resolution

Name usage in client programs

Extensions to CORBA IDL permitted by SOM IDL

Pointer '*' types

Unsigned types

Implementation section

Comment processing

Generated header files

The SOM Compiler

       Generating binding files
       Environment variables affecting the SOM Compiler
       Running the SOM Compiler 

The 'pdl' Facility