SOM IDL and the SOM Compiler

From EDM2
Jump to: navigation, search
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.

Contents

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

A SOM IDL interface statement for a class may contain an implementation statement, which specifies information about how the class will be implemented (version numbers for the class, overriding of inherited methods, what resolution mechanisms the bindings for a particular method will support, and so forth). If the implementation statement is omitted, default information is assumed.

Because the implementation statement is specific to SOM IDL (and is not part of the CORBA standard), the implementation statement should be preceded by an "#ifdef _ _SOMIDL_ _" directive and followed by an "#endif" directive. (See Example 3 in the SOM IDL Tutorial presented earlier.)

The syntax for the implementation statement is as follows:

  #ifdef __SOMIDL__
  implementation
  {
    implementation*
  };
  #endif

where each "implementation" can be a modifier statement, a passthru statement, or a declarator of an instance variable, terminated by a semicolon. These constructs are described below. An interface statement may not contain multiple implementation statements.

Modifier statements

A modifier statement gives additional implementation information about IDL definitions, such as interfaces, attributes, methods, and types. Modifiers can be unqualified or qualified: An unqualified modifier is associated with the interface it is defined in. An unqualified modifier statement has the following two syntactic forms: modifier modifier = value where "modifier" is either a SOM Compiler-defined identifier or a user-defined identifier, and where "value" is an identifier, a string enclosed in double quotes (" "), or a number.

For example:

  filestem = foo;
  nodata;
  persistent;
  dllname = "E:/som/dlls";

A qualified modifier is associated with a qualifier. The qualified modifier has the syntax:

  qualifier : modifier
  qualifier : modifier = value
  #pragma modifier qualifier : modifier
  #pragma modifier qualifier : modifier = value

where "qualifier" is the identifier of an IDL definition or is user defined. If the "qualifier" is an IDL definition introduced in the current interface, module, or global scope, then the modifier is attached to that definition. Otherwise, if the qualifier is user defined, the modifier is attached to the interface it occurs in. If a user-defined modifier is defined outside of an interface body (by using #pragma modifier), then it is ignored.

For example, consider the following IDL file. (Notice that qualified modifiers can be defined with the "qualifier" and "modifier[=value]" in either order. Also observe that additional modifiers can be included by separating them with commas.)

  #include <somobj.idl>
  #include <somcls.idl>
  typedef long newInt;
  #pragma somemittypes on
  #pragma modifier newInt : nonportable;
  #pragma somemittypes off
  module M {
      typedef long long_t;
      module  N {
          typedef short short_t;
          interface M_I : SOMClass {
              implementation {
                  somInit : override;
              };
          };
          interface I : SOMObject {
              void op ();
              #pragma modifier op : persistent;


              typedef char char_t;
              implementation {
                  releaseorder : op;
                  metaclass = M_I;
                  callstyle = oidl;
                  mymod : a, b;
                  mymod : c, d;
                  e     : mymod;
                  f     : mymod;
                  op : persistent;
              };
         };
      };
   };

From the preceding IDL file, we associate modifiers with the following definitions:

  TypeDef "::newInt"             1  modifier: nonportable
  InterfaceDef "::M::N::M_I"     1  modifier: override = somInit
  InterfaceDef "::M::N::I"       9 modifiers: metaclass = M_I,
                                              releaseorder = op
                                              callstyle = oidl
                                              mymod = a,b,c,d,e,f
                                              a = mymod
                                              b = mymod
                                              c = mymod
                                              d = mymod
                                              e = mymod
                                              f = mymod
  OperationDef "::M::N::I::op"   1 modifier: persistent

Notice, how the modifiers for the user-defined qualifier "mymod":

  mymod : a, b;
  mymod : c, d;
  e     : mymod;
  f     : mymod;

map onto:

  mymod = a,b,c,d,e,f
  a     = mymod
  b     = mymod
  c     = mymod
  d     = mymod
  e     = mymod
  f     = mymod

This enables users to look up the modifiers with "mymod", either by looking for "mymod" or by using each individual value that uses "mymod". These user-defined modifiers are available for Emitter writers (see the Emitter Writer's Guide and Reference) and from the Interface Repository (see Chapter 7, "The Interface Repository Framework").

SOM Compiler unqualified modifiers

Unqualified modifiers (described below) include the SOM Compiler-defined identifiers abstract, baseproxyclass, callstyle, classinit, directinitclasses, dllname, filestem, functionprefix, majorversion, metaclass, memory_management, minorversion, somallocte and somdeallocate.

abstract

Specifies that the class is intended for use as a parent for subclass derivations, but not for creating instances.

baseproxyclass =class

Specifies the base proxy class to be used by DSOM when dynamically creating a proxy class for the current class. The base proxy class must be derived from the class SOMDClientProxy The SOMDClientProxy class will be used if the baseproxyclass modifier is unspecified. (See chapter 6, "distributed SOM," for a discussion on customizing proxy classes.)

callstyle = oidl

Specifies that the method stub procedures generated by SOM's C/C++ bindings will not include the CORBA-specified (Environment *ev) and (context *ctx) parameters.

classinit = procedure

Specifies a user-written procedure that will be executed to complete the initialization of a class object after it is created. The classinit modifier is needed if something should happen exactly once when a class is created. (That is, you want to define an action that will not be inherited when subclasses are created. One example of this is for staticdata variables.) When the classinit modifier is specified in the .idl file for a class, the implementation file generated by the SOM Compiler provides a template for the procedure, which includes a parameter that is a pointer to the class. The class implementor can then fill in the body of this procedure template. (For an example, see the examples following the staticdata modifier under "SOM Compiler qualified modifiers.")

directinitclasses ="ancestor1, ancestor2, ..."
Specifies the ancestor class(es) whose initializers (and destructors) will be directly invoked by this class's initialization (and destruction) routines. If this modifier is not explicitly specified, the default setting is the parents of the class. For further information, see "Initializing and Uninitializing Objects" in Chapter 5, "Implementing Classes in SOM."
dllname = filename
Specifies the name of the library file that will contain the class's implementation. If filename contains special characters (e.g., periods, backslashes), then filename should be surrounded by double quotes (""). The filename specified can be either a full pathname, or an unqualified (or partially qualified) filename. In the latter cases, the LIBPATH environment variable is used to locate the file.
filestem = stem

Specifies how the SOM Compiler will construct file names for the binding files it generates (<stem>.h, <stem>.c, etc.). The default stem is the file stem of the .idl file for the class.

functionprefix = prefix

Directs the SOM Compiler to construct method-procedure names by prefixing method names with "prefix". For example, "functionprefix = xx;" within an implementation statement would result in a procedure name of xxfoo for method foo. The default for this attribute is the empty string. If an interface is defined in a module, then the default function prefix is the fully scooped interface name. Tip: Using a function prefix with the same name as the class makes it easier to remember method-procedure names when debugging.

When an .idl file defines multiple interfaces not contained within a module, use of a function prefix for each interface is essential to avoid name collisions. For example, if one interface introduces a method and another interface in the same .idl file overrides it, then the implementation file for the classes will contain two method procedures of the same name (unless function prefixes are defined for one of the classes), resulting in a name collision at compile time.

majorversion = number
Specifies the major version number of the current class definition. The major version number of a class definition usually changes only when a significant enhancement or incompatible change is made to the class. The "number" must be a positive integer less than 2[32]-1. If a non-zero major version number is specified, SOM will verify that any code that purports to implement the class has the same major version number. The default major version number is zero.
memory_management = corba

Specifies that all methods introduced by the class follow the CORBA specification for parameter memory management, except where a particular method has an explicit modifier indicating otherwise (either "object_owns_result" or "object_owns_parameters"). See the section in Chapter 6 entitled "Memory Management" for a discussion of the CORBA memory-management requirements.

metaclass = class
Specifies the class's metaclass. The specified metaclass (or one automatically derived from it at run time) will be used to create the class object for the class. If a metaclass is specified, its .idl file (if separate) must be included in the include section of the class's .idl file. If no metaclass is specified, the metaclass will be defined automatically.
minorversion = number
Specifies the minor version number of the current class definition. The minor version number of a class definition changes whenever minor enhancements or fixes are made to a class. Class implementers usually maintain backward compatibility across changes in the minor version number. The "number" must be a positive integer less than 2[32]-1. If a non-zero minor version number is specified, SOM will verify that any code that purports to implement the class has the same or a higher minor version number. The default minor version number is zero.
somallocate=procedure
Specifies a user-written procedure that will be executed to allocate memory for class instances when the somAllocate class method is invoked.
somdeallocate=procedure
Specifies a user-written procedure that will be executed to deallocate memory for class instances when the somDeallocate class method is invoked.

The following example illustrates the specification of unqualified interface modifiers:

      implementation   {
        filestem = hello;
        functionprefix = hel;
        majorversion = 1;
        minorversion = 2;
        classinit = helloInit;
        metaclass = M_Hello;
      };

SOM Compiler qualified modifiers

Qualified modifiers are categorized according to the IDL component (class, attribute, method, or type) to which each modifier applies. Listed below are the SOM Compiler-defined identifiers used as qualified modifiers, along with the IDL component to which it applies. Descriptions of all qualified modifiers are then given in alphabetical order. Recall that qualified modifiers are defined using the syntax qualifier: modifier[=value].

For classes
releaseorder
For attributes
indirect, nodata, noget, noset
For methods
caller_owns_parameters, caller_owns_result, const, init, method, migrate, namelookup, nocall, noenv, nonstatic, nooverride, noself, object_owns_parameters, object_owns_result, offset, override, procedure, reintroduce, and select
For variables
staticdata
For types
impctx
caller_owns_parameters ="p1,p2,...,pn"
Specifies the names of the method's parameters whose ownership is retained by (in the case of "in"parameters) or transferred to (for "inout" or "out" parameters) the caller. This modifier is only valid in the interface specification of the method's introducing class. This modifier only makes sense for parameters whose IDL type is a data item that can be freed (string, object, array, pointer, or TypeCode), or a data item containing memory that can be freed (for example, a sequence or any), or a struct or union.
For parameters whose type is an object, ownership applies to the object reference rather than to the object (that is, the caller should invoke release on the parameter, rather than somFree).
caller_owns_result
Specifies that ownership of the return result of the method is transferred to the caller, and that the caller is responsible for freeing the memory. This modifier is only valid in the interface specification of the method's introducing class. This modifier only makes sense when the method's return type is a data type that can be freed (string, object, array, pointer, or TypeCode), or a data item containing memory that can be freed (for example, a sequence or any). For methods that return an object, ownership applies to the object reference rather than to the object (that is, the caller should invoke release on the result, rather than somFree).
const
Indicates that implementations of the related method should not modify their target argument. SOM provides no way to verify or guarantee that implementations do not modify the targets of such methods, and the information provided by this modifier is not currently of importance to any of the Toolkit emitters. However, the information may prove useful in the future. For example, since modifiers are available in the Interface Repository, there may be future uses of this information by DSOM.
impctx
Supports types that cannot be fully defined using IDL. For full information, see "Using the tk_foreign TypeCode" in Chapter 7, "The Interface Repository Framework."
indirect
Directs the SOM Compiler to generate "get" and "set" methods for the attribute that take and return a pointer to the attribute's value, rather than the attribute value itself. For example, if an attribute x of type float is declared to be an indirect attribute, then the "_get_x" method will return a pointer to a float, and the input to the "_set_x" method must be a pointer to a float. (This modifier is provided for OIDL compatibility only.)
init
Indicates that a method is an initializer method. For information concerning the use of this modifier, see "Initializing and Uninitializing Objects: in Chapter 5, "Implementing Classes in SOM"
method or nonstatic or procedure
Indicates the category of method implementation. Refer to the topic "The four kinds of SOM methods" in Chapter 5, "Implementing Classes in SOM," for an explanation of the meanings of these different method modifiers. If none of these modifiers is specified, the default is method. Methods with the procedure modifier cannot be invoked remotely using DSOM.
migrate = ancestor
Indicates that a method originally introduced by this interface has been moved upward to a specified <ancestor> interface. When this is done, the method introduction must be removed from this interface (because the method is now inherited). However, the original releaseorder entry for the method should be retained, and migrate should be used to assure that clients compiled based on the original interface will not require recompilation. The ancestor interface is specified using a C-scoped interface name. For example, "Module_InterfaceName", not "Module::InterfaceName". See the later topic "Name usage in client programs" for an explanation of C-scoped names.
namelookup
See "offset or namelookup."
nocall
Specifies that the related method should not be invoked on an instance of this class even though it is supported by the interface.
nodata
Directs the SOM Compiler not to define an instance variable corresponding to the attribute. For example, a "time" attribute would not require an instance variable to maintain its value, because the value can be obtained from the operating system. The "get" and "set" methods for "nodata" attributes must be defined by the class implementer; stub method procedures for them are automatically generated in the implementation template for the class by the SOM Compiler.
noenv
Indicates that a direct-call procedure does not receive an environment as an argument.
noget
Directs the SOM Compiler not to automatically generate a "get" method procedure for the attribute in the .ih/.xih binding file for the class. Instead, the "get" method must be implemented by the class implementer. A stub method procedure for the "get" method is automatically generated in the implementation template for the class by the SOM Compiler, to be filled in by the implementer.
nonstatic
See "method or nonstatic or procedure."
nooverride
Indicates that the method should not be overridden by subclasses. The SOM Compiler will generate an error if this method is overridden.
noself
Indicates that a direct-call procedure does not receive a target object as an argument.
noset
Directs the SOM Compiler not to automatically generate a "set" method procedure for the attribute in the .ih/.xih binding file for the class. Instead, the "set" method must be implemented by the class implementer. A stub method procedure for the "set" method is automatically generated in the implementation template for the class by the SOM Compiler.

Note: The "set" method procedure that the SOM Compiler generates by default for an attribute in the .h/.xh binding file (when the noset modifier is not used) does a shallow copy of the value that is passed to the attribute. For some attribute types, including strings and pointers, this may not be appropriate. For instance, the "set" method for an attribute of type string should perform a string copy, rather than a shallow copy, if the attribute's value may be needed after the client program has freed the memory occupied by the string. In such situations, the class implementer should specify the noset attribute modifier and implement the attribute's "set" method manually, rather than having SOM implement the "set" method automatically.

object_owns_parameters ="p1, p2, ..., pn"
Specifies the names of the method's parameters whose ownership is transferred to (in the case of "in" parameters) or is retained by (for "inout" or "out" parameters) the object. For "in" parameters, the object can free the parameter at any time after receiving it. (Hence, the caller should not reuse the parameter or pass it as any other object-owned parameter in the same method call.) For "inout" and "out" parameters, the object is responsible for freeing the parameter sometime before the object is destroyed. This modifier is only valid in the interface specification of the method's introducing class. This modifier only makes sense for parameters whose IDL type is a data item that can be freed (string, object, array, pointer, or TypeCode), or a data item containing memory that can be freed (for example, a sequence or any), or a struct or union.
For parameters whose type is an object, ownership applies to the object reference rather than to the object (that is, the object will invoke release on the parameter, rather than somFree). For "in" and "out" parameters whose IDL-to-C/C++ mapping introduces a pointer, ownership applies only to the data item itself, and not to the introduced pointer. (For example, even if an "out string" IDL parameter (which becomes a "string *" C/C++ parameter) is designated as "object-owned," the object assumes ownership of the string, but not of the pointer to the string.)
object_owns_result
Specifies that the object retains ownership of the return result of the method, and that the caller must not free the memory. The object is responsible for freeing the memory sometime before the object is destroyed. This modifier is only valid in the interface specification of the method's introducing class. This modifier only makes sense when the method's return type is a data type that can be freed (string, object, array, pointer, or TypeCode), or a data item containing memory that can be freed (for example, a sequence or any). For methods that return an object, ownership applies to the object reference rather than to the object (that is, the object will be responsible for invoking release on the result, rather than somFree).
offset or namelookup
Indicates whether the SOM Compiler should generate bindings for invoking the method using offset resolution or name lookup. Offset resolution requires that the class of the method's target object be known at compile time. When different methods of the same name are defined by several classes, namelookup is a more appropriate technique for method resolution than is offset resolution. (See Chapter 3, the section entitled "Invoking Methods.") The default modifier is offset.
override
Indicates that the method is one introduced by an ancestor class and that this class will re-implement the method. See also the related modifier, select.
procedure
See "method or nonstatic or procedure."
reintroduce
Indicates that this interface will "hide" a method introduced by some ancestor interface, and will replace it with another implementation. Methods introduced as direct-call procedures or nonstatic methods can be reintroduced. However, static methods (the default implementation category for SOM met hods) cannot be reintroduced.
releaseorder
a, b, c, ...:Specifies the order in which the SOM Compiler will place the class's methods in the data structures it builds to represent the class. Maintaining a consistent release order for a class allows the implementation of a class to change without requiring client programs to be recompiled.

The release order should contain every method name introduced by the class (private and nonprivate), but should not include any inherited methods, even if they are overridden by the class. The "get" and "set" methods defined automatically for each new attribute (named "_get_<attributeName>" and "_set_<attributeName>") should also be included in the release order list. The order of the names on the list is unimportant except that once a name is on the list and the class has client programs, it should not be reordered or removed, even if the method is no longer supported by the class, or the client programs will require recompilation. New methods should be added only to the end of the list. If a method named on the list is to be moved up in the class hierarchy, its name should remain on the current list, but it should also be added to the release order list for the class that will now introduce it.

If not explicitly specified, the release order will be determined by the SOM Compiler, and a warning will be issued for each missing method. If new methods or attributes are subsequently added to the class, the default release order might change; programs using the class would then require recompilation. Thus, it is advisable to explicitly give a release order.

select = parent
Used in conjunction with the override modifier, this, indicates that an inherited static method will use the implementation inherited from the indicated <parent> class. The parent is specified using the C-scoped name. For example, "Module_InterfaceName", not "Module:: InterfaceName". See the later topic "Name usage in client programs" for an explanation of C-scoped names.
staticdata
Indicates that the declared variable is not stored within objects, but, instead, that the ClassData structure for the implementing class will contain a pointer to the staticdata variable. This is similar in concept to C++ static data members. The staticdata variable must also be included in the releaseorder. The class implementor has responsibility for allocating the staticdata variable and for loading the ClassData structure's pointer to the staticdata variable during class initialization. (The pointer is accessible as <className>ClassData.<variableName>.) The implementor's responsibility can be facilitated by writing a special class initialization function and indicating its name using the classinit unqualified modifier. (See also the examples that follow.)
Note
Attributes can be declared as staticdata. This is an important implementation technique that allows classes to introduce attributes whose backing storage is not inherited by subclasses.

The following example illustrates the specification of qualified modifiers:

implementation
{
  releaseorder : op1, op3, op2, op5, op6, x, y, _set_z, _get_z;
  op1 : persistent;
  somDefaultInit : override, init;
  op2: reintroduce, procedure;
  op3: reintroduce, nonstatic;
  op4: override, select = ModuleName_parentInterfaceName;
  op5: migrate = ModuleName_ancestorInterfaceName;
  op6: procedure, noself, noenv;
  long x;
  x: staticdata;
  y: staticdata; // y and z are attributes
  _set_z: object_owns_parameters = "name";
  _get_z: object_owns_result;
  mymod : a, b;
};

As shown above for attribute z, separate modifiers can be declared for an attribute's _set and _get methods, using method modifiers. This capability may be useful for DSOM applications. (See the DSOM sample program "animal" that is distributed with the SOMobjects Toolkit.)

The next example for classes "X" and "Y" illustrates the use of a staticdata modifier, along with its corresponding classinit modifier and the template procedure generated for classinit by the SOM Compiler.

/* IDL for staticdata and classinit example: */

#include <somobj.idl>

interface X : SOMObject {
        attribute long staticAttribute;
        attribute long normalAttribute;
        implementation {
                staticAttribute: staticdata;
                classinit = Xinit;
                releaseorder: staticAttribute,
                                    _get_staticAttribute,
                                    _set_staticAttribute,
                                    _get_normalAttribute,
                                    _set_normalAttribute;
        };
};

interface Y : X { };
/* Template procedure for classInit: */

#ifndef SOM_Module_classinit_Source
#define SOM_Module_classinit_Source
#endif
#define X_Class_Source

#include "classInit.ih"

static long holdStaticAttribute = 1234;
void SOMLINK Xinit(SOMClass *cls)
{
        XClassData.staticAttribute = &holdStaticAttribute;
}

main()
{
        X *x = XNew();
        Y *y = YNew();

        somPrintf("initial staticAttribute = x(%d) = y(%d)\n",
                                _get_staticAttribute(x,0),
                                _get_staticAttribute(y,0));

        _set_staticAttribute(x,0,42);
        _set_staticAttribute(y,0,4321);

        somPrintf("changed staticAttribute = x(%d) = y(%d)\n",
                                _get_staticAttribute(x,0),
                                _get_staticAttribute(y,0));
}

/* Program output:

        initial staticAttribute = x(1234) = y(1234)
        changed staticAttribute = x(4321) = y(4321)
        after setting normalAttribute, x(10) != y(20)
*/

Passthru statements

A passthru statement (used within the body of an implementation statement, described above) allows a class implementer to specify blocks of code (for C/C++ programmers, usually only #include directives) that the SOM compiler will pass into the header files it generates.

Passthru statements are included in SOM IDL primarily for backward compatibility with the SOM OIDL language, and their use by C and C++ programmers should be limited to #include directives. C and C++ programmers should use IDL type and constant declarations rather than passthru statements when possible. (Users of other languages, however, may require passthru statements for type and constant declarations.)

The contents of the passthru lines are ignored by the SOM compiler and can contain anything that needs to placed near the beginning of a header file for a class. Even comments contained in passthru lines are processed without modification. The syntax for specifying passthru lines is one of the following forms:

   passthru language_suffix
       = literal+ ; passthru language_suffix_before
       = literal+ ; passthru language_suffix_after
       = literal+ ;

where "language" specifies the programming language and "suffix" indicates which header files will be affected. The SOM Compiler supports suffixes h, ih, xh, and xih. For both C and C++, "language" is specified as C.

Each "literal" is a string literal (enclosed in double quotes) to be placed verbatim into the specified header file. [Double quotes within the passthru literal should be preceded by a backslash. No other characters escaped with a backslash will be interpreted, and formatting characters (newlines, tab characters, etc.) are passed through without processing.] The last literal for a passthru statement must not end in a backslash (put a space or other character between a final backslash and the closing double quote).

When either of the first two forms is used, passthru lines are placed before the #include statements in the header file. When the third form is used, passthru lines are placed just after the #include statements in the header file.

For example, the following passthru statement

      implementation   {
        passthru C_h = "#include <foo.h>";
      };

results in the directive #include <foo.h> being placed at the beginning of the .h C binding file that the SOM Compiler generates.

For any given target file (as indicated by language_suffix), only one passthru statement may be defined within each implementation section. You may, however, define multiple #include statements in a single passthru. For legibility, each #include should begin on a new line, optionally with a blank line to precede and follow the #include list. For an example, see "Introducing non-IDL data types or classes" later in this section.

Declaring instance variables and staticdata variables

Declarators are used within the body of an implementation statement (described above) to specify the instance variables that are introduced by a class, and the staticdata variables pointed to by the class's ClassData structure. These variables are declared using ANSI C syntax for variable declarations, restricted to valid SOM IDL types (see "Type and constant declarations," above). For example, the following implementation statement declares two instance variables, x and y, and a staticdata variable, z, for class "Hello,":

implementation
{
  short x;
  long y;
  double z;
  z: staticdata;
};

Instance variables are normally intended to be accessed only by the class's methods and not by client programs or subclasses' methods. For data to be accessed by client programs or subclass methods, attributes should be used instead of instance variables. (Note, however, that declaring an attribute has the effect of also declaring an instance variable of the same name, unless the "nodata" attribute modifier is specified.)

Staticdata variables, by contrast, are publicly available and are associated specifically with their introducing class. They are, however, very different in concept from class variables. Class variables are really instance variables introduced by a metaclass, and are therefore present in any class that is an instance of the introducing metaclass (or of any metaclass derived from this metaclass). As a result, class variables present in any given class will also be present in any class derived from this class (that is, class variables are inherited). In contrast, staticdata variables are introduced by a class (not a metaclass) and are (only) accessed from the class's ClassData structure - they are not inherited.

Introducing non-IDL data types or classes

On occasion, you may want a new .idl file to reference some element that the SOM Compiler would not recognize, such as a user-defined class or an instance variable or attribute with a user-defined data type. You can reference such elements if they already exist in .h or .xh files that the SOM Compiler can #include with your new .idl file, as follows:

  • To introduce a non-IDL class, insert an interface statement that is a forward reference to the existing user-defined class. It must precede the interface statement for the new class in the .idl file.
  • To declare an instance variable or attribute that is not a valid IDL type, declare a dummy typedef preceding the interface declaration.
  • In each case above, in the implementation section use a passthru statement to pass an #include statement into the language-specific binding file(s) of the new .idl file (a) for the existing user-defined class or (b) for the real typedef.

In the following example, the generic SOM type somToken is used in the .idl file for the user's types "myRealType" and "myStructType". The passthru statement then causes an appropriate #include statement to be emitted into the C/C++ binding file, so that the file defining types "myRealType" and "myStructType" will be included when the binding files process. In addition, an interface declaration for "myOtherClass" is defined as a forward reference, so that an instance of that class can be used within the definition of "myCurrentClass". The passthru statement also #includes the binding file for "myOtherClass":

typedef somToken myRealType;typedef somToken myStructType;

interface myOtherClass;

interface myCurrentClass : SOMObject {
. . .
        implementation {
                . . .
                myRealType myInstVar;
                attribute myStructType st1;
                passthru C_h =
                        ""
                        "#include <myTypes.h>"
                        "#include <myOtherClass.h>"
                        "";
        };
};

Note: See also the section "Using the tk_foreign TypeCode" in Chapter 7, "The Interface Repository Framework."

Passthru statements

A passthru statement (used within the body of an implementation statement, described above) allows a class implementer to specify blocks of code (for C/C++ programmers, usually only #include directives) that the SOM compiler will pass into the header files it generates.

Passthru statements are included in SOM IDL primarily for backward compatibility with the SOM OIDL language, and their use by C and C++ programmers should be limited to #include directives. C and C++ programmers should use IDL type and constant declarations rather than passthru statements when possible. (Users of other languages, however, may require passthru statements for type and constant declarations.)

The contents of the passthru lines are ignored by the SOM compiler and can contain anything that needs to placed near the beginning of a header file for a class. Even comments contained in passthru lines are processed without modification. The syntax for specifying passthru lines is one of the following forms:

passthru language_suffix = literal+ ;
passthru language_suffix_before = literal+ ;
passthru language_suffix_after = literal+ ;

where "language" specifies the programming language and "suffix" indicates which header files will be affected. The SOM Compiler supports suffixes h, ih, xh, and xih. For both C and C++, "language" is specified as C.

Each "literal" is a string literal (enclosed in double quotes) to be placed verbatim into the specified header file. [Double quotes within the passthru literal should be preceded by a backslash. No other characters escaped with a backslash will be interpreted, and formatting characters (newlines, tab characters, etc.) are passed through without processing.] The last literal for a passthru statement must not end in a backslash (put a space or other character between a final backslash and the closing double quote).

When either of the first two forms is used, passthru lines are placed before the #include statements in the header file. When the third form is used, passthru lines are placed just after the #include statements in the header file.

For example, the following passthru statement

   implementation
   {
     passthru C_h = "#include <foo.h>";
   };

results in the directive #include <foo.h> being placed at the beginning of the .h C binding file that the SOM Compiler generates.

For any given target file (as indicated by language_suffix), only one passthru statement may be defined within each implementation section. You may, however, define multiple #include statements in a single passthru. For legibility, each #include should begin on a new line, optionally with a blank line to precede and follow the #include list. For an example, see "Introducing non-IDL data types or classes" later in this section.

Declaring instance variables and staticdata variables

Declarators are used within the body of an implementation statement (described above) to specify the instance variables that are introduced by a class, and the staticdata variables pointed to by the class's ClassData structure. These variables are declared using ANSI C syntax for variable declarations, restricted to valid SOM IDL types (see "Type and constant declarations," above). For example, the following implementation statement declares two instance variables, x and y, and a staticdata variable, z, for class "Hello," :

implementation
{
  short x;
  long y;
  double z;
  z: staticdata;
};

Instance variables are normally intended to be accessed only by the class's methods and not by client programs or subclasses' methods. For data to be accessed by client programs or subclass methods, attributes should be used instead of instance variables. (Note, however, that declaring an attribute has the effect of also declaring an instance variable of the same name, unless the "nodata" attribute modifier is specified.)

Staticdata variables, by contrast, are publicly available and are associated specifically with their introducing class. They are, however, very different in concept from class variables. Class variables are really instance variables introduced by a metaclass, and are therefore present in any class that is an instance of the introducing metaclass (or of any metaclass derived from this metaclass). As a result, class variables present in any given class will also be present in any class derived from this class (that is, class variables are inherited). In contrast, staticdata variables are introduced by a class (not a metaclass) and are (only) accessed from the class's ClassData structure - they are not inherited.

Introducing non-IDL data types or classes

On occasion, you may want a new .idl file to reference some element that the SOM Compiler would not recognize, such as a user-defined class or an instance variable or attribute with a user-defined data type. You can reference such elements if they already exist in .h or .xh files that the SOM Compiler can #include with your new .idl file, as follows:

  • To introduce a non-IDL class, insert an interface statement that is a forward reference to the existing user-defined class. It must precede the interface statement for the new class in the .idl file.
  • To declare an instance variable or attribute that is not a valid IDL type, declare a dummy typedef preceding the interface declaration.
  • In each case above, in the implementation section use a passthru statement to pass an #include statement into the language-specific binding file(s) of the new .idl file (a) for the existing user-defined class or (b) for the real typedef.

In the following example, the generic SOM type somToken is used in the .idl file for the user's types "myRealType" and "myStructType". The passthru statement then causes an appropriate #include statement to be emitted into the C/C++ binding file, so that the file defining types "myRealType" and "myStructType" will be included when the binding files process. In addition, an interface declaration for "myOtherClass" is defined as a forward reference, so that an instance of that class can be used within the definition of "myCurrentClass". The passthru statement also #includes the binding file for "myOtherClass":

typedef somToken myRealType;
typedef somToken myStructType;

interface myOtherClass;

interface myCurrentClass : SOMObject {
. . .
        implementation {
                . . .
                myRealType myInstVar;
                attribute myStructType st1;
                passthru C_h =
                        ""
                        "#include <myTypes.h>"
                        "#include <myOtherClass.h>"
                        "";
        };
};

Note: See also the section "Using the tk_foreign TypeCode" in Chapter 7, "The Interface Repository Framework."

Comments within a SOM IDL file

SOM IDL supports both C and C++ comment styles. The characters "//" start a line comment, which finishes at the end of the current line. The characters "/*" start a block comment that finishes with the "*/". Block comments do not nest. The two comment styles can be used interchangeably.

Comments in a SOM IDL specification must be strictly associated with particular syntactic elements, so that the SOM Compiler can put them at the appropriate place in the header and implementation files it generates. Therefore, comments may appear only in these locations (in general, following the syntactic unit being commented):

  • At the beginning of the IDL specification
  • After a semicolon
  • Before or after the opening brace of a module, interface statement, implementation statement, structure definition, or union definition
  • After a comma that separates parameter declarations or enumeration members
  • After the last parameter in a prototype (before the closing parenthesis)
  • After the last enumeration name in an enumeration definition (before the closing brace)
  • After the colon following a case label of a union definition
  • After the closing brace of an interface statement

Numerous examples of the use of comments can be found in the Tutorial of Chapter 2.

Because comments appearing in a SOM IDL specification are transferred to the files that the SOM Compiler generates, and because these files are often used as input to a programming language compiler, it is best within the body of comments to avoid using characters that are not generally allowed in comments of most programming languages. For example, the C language does not allow "*/" to occur within a comment, so its use is to be avoided, even when using C++ style comments in the .idl file.

SOM IDL also supports throw-away comments. They may appear anywhere in an IDL specification, because they are ignored by the SOM Compiler and are not transferred to any file it generates. Throw-away comments start with the string "//#" and end at the end of the line. Throw-away comments can be used to "comment out" portions of an IDL specification.

To disable comment processing (that is, to prevent the SOM Compiler from transferring comments from the IDL specification to the binding files it generates), use the -c option of the sc or somc command when running the SOM Compiler (See Section 4.3, "The SOM Compiler"). When comment processing is disabled, comment placement is not restricted and comments can appear anywhere in the IDL specification.

Designating 'private' methods and attributes

To designate methods or attributes within an IDL specification as "private," the declaration of the method or attribute must be surrounded with the preprocessor commands #ifdef__PRIVATE__ (with two leading underscores and two following underscores) and #endif. For example, to declare a method "foo" as a private method, the following declaration would appear within the interface statement:

#ifdef __PRIVATE__
  void foo();
#endif

Any number of methods and attributes can be designated as private, either within a single #ifdef or in separate ones. [Kernighan-Ritchie reference for the C preprocessor: pages 88-92.]

When compiling an .idl file, the SOM Compiler normally recognizes only public (nonprivate) methods and attributes, as that is generally all that is needed. To generate header files for client programs that do need to access private methods and attributes, or for use when implementing a class library containing private methods, the -p option should be included when running the SOM Compiler. The resulting header files will then include bindings for private, as well as public, methods and attributes. Both the implementation bindings (.ih or .xih file) and the usage bindings to be #included in the implementation (.h or .xh file) should be generated under the -p option. The -p option is described in the topic "Running the SOM Compiler" later in this chapter.

The SOMobjects Toolkit also provides a pdl (Public Definition Language) emitter that can be used with the SOM Compiler to generate a copy of an .idl file which has the portions designated as private removed. The next main section of this chapter describes how to invoke the SOM Compiler and the various emitters.

Module declarations to define multiple interfaces in a .idl file

A single .idl file can define multiple interfaces. This allows, for example, a class and its metaclass to be defined in the same file. When a file defines two (or more) interfaces that reference one another, forward declarations can be used to declare the name of an interface before it is defined. This is done as follows:

interface class-name ;

The actual definition of the interface for "class-name" must appear later in the same .idl file.

If multiple interfaces are defined in the same .idl file, and the classes are not a class-metaclass pair, they can be grouped into modules, by using the following syntax:

module module-name { definition+ };

where each "definition" is a type declaration, constant declaration, exception declaration, interface statement, or nested module statement. Modules are used to scope identifiers (see below).

Alternatively, multiple interfaces can be defined in a single .idl file without using a module to group the interfaces. Whether or not a module is used for grouping multiple interfaces, the languages bindings produced from the .idl file will include support for all of the defined interfaces.

Note
When multiple interfaces are defined in a single .idl file and a module statement is not used for grouping these interfaces, it is necessary to use the functionprefix modifier to assure that different names exist for functions that provide different implementations for a method. In general, it is a good idea to always use the functionprefix modifier, but in this case it is essential.

Scooping and name resolution

A .idl file forms a naming scope (or scope). Modules, interface statements, structures, unions, methods, and exceptions form nested scopes. An identifier can only be defined once in a particular scope. Identifiers can be redefined in nested scopes.

Names can be used in an unqualified form within a scope, and the name will be resolved by successively searching the enclosing scopes. Once an unqualified name is defined in an enclosing scope, that name cannot be redefined.

Fully qualified names are of the form:

scoped-name::identifier

For example, method name "meth" defined within interface "Test" of module "M1" would have the fully qualified name:

M1::Test::meth

A qualified name is resolved by first resolving the "scoped-name" to a particular scope S, then locating the definition of "identifier" within that scope. Enclosing scopes of S are not searched.

Qualified names of the form:

::identifier

These names are resolved by locating the definition of "identifier" within the smallest enclosing module.

Every name defined in an IDL specification is given a global name, constructed as follows:

  • Before the SOM Compiler scans an .idl file, the name of the current root and the name of the current scope are empty. As each module is encountered, the string "::" and the module name are appended to the name of the current root. At the end of the module, they are removed.
  • As each interface, struct, union, or exception definition is encountered, the string "::" and the associated name are appended to the name of the current scope. At the end of the definition, they are removed. While parameters of a method declaration are processed, a new unnamed scope is entered so that parameter names can duplicate other identifiers.
  • The global name of an IDL definition is then the concatenation of the current root, the current scope, a "::", and the local name for the definition.

The names of types, constants, and exceptions defined by the parents of a class are accessible in the child class. References to these names must be unambiguous. Ambiguities can be resolved by using a scoped name (prefacing the name with the name of the class that defines it and the characters "::", as in "parent-class::identifier). Scope names can also be used to refer to a constant, type, or exception name defined by a parent class but redefined by the child class.

Name usage in client programs

Within a C or C++ program, the global name for a type, constant, or exception corresponding to an IDL scoped name is derived by converting the string "::" to an underscore ("_") and removing the leading underscore. Such names are referred to as C-scoped names. This means that types, constants, and exceptions defined within the interface statement for a class can be referenced in a C/C++ program by prepending the class name to the name of the type, constant, or exception. For example, consider the types defined in the following IDL specification:

 typedef sequence<long,10> mySeq;
 interface myClass : SOMObject
 {
    enum color {red, white, blue};
    typedef string<100> longString;
    ...
 }

These types could be accessed within a C or C++ program with the following global names:

mySeq,
myClass_color,
myClass_red,
myClass_white,
myClass_blue, and
myClass_longString.

Type, constant, and exception names defined within modules similarly have the module name prepended. When using SOM's C/C++ bindings, the short form of type, constant, and exception names (such as, color, longString) can also be used where unambiguous, except that enumeration names must be referred to using the long form (for example, myClass_red and not simply red).

Because replacing "::" with an underscore to create global names can lead to ambiguity if an IDL identifier contains underscores, it is best to avoid the use of underscores when defining IDL identifiers.

Extensions to CORBA IDL permitted by SOM IDL

The following topics describe several SOM-unique extensions of the standard CORBA syntax that are permitted by SOM IDL for convenience. These constructs can be used in an .idl file without generating a SOM Compiler error.

If you want to verify that an IDL file contains only standard CORBA specifications, the SOM Compiler option -mcorba turns off each of these extensions and produces compiler errors wherever non-CORBA specifications are used. (The SOM Compiler command and options are described in the topic "Running the SOM Compiler" later in this chapter.)

Pointer '*' types

In addition to the base CORBA types, SOM IDL permits the use of pointer types ('*'). As well as increasing the range of base types available to the SOM IDL programmer, using pointer types also permits the construction of more complex data types, including self-referential and mutually recursive structures and unions.

If self-referential structures and unions are required, then, instead of using the CORBA approach for IDL sequences, such as the following:

  struct X {
      ...
      sequence <X> self;
      ...
  };

it is possible to use the more typical C/C++ approach. For example:

  struct X {
      ...
      X *self;
      ...
  };

SOM IDL does not permit an explicit '*' in sequence declarations. If a sequence is required for a pointer type, then it is necessary to typedef the pointer type before use. For example:

sequence <long *> long_star_seq;        // error.

typedef long * long_star;
sequence <long_star> long_star_seq;      // OK.

Unsigned types

SOM IDL permits the syntax "unsigned <type>", where <type> is a previously declared type mapping onto "short" or "long". (Note that CORBA permits only "unsigned short" and "unsigned long".)

Implementation section

SOM IDL permits an implementation section in an IDL interface specification to allow the addition of instance variables, method overrides, metaclass information, passthru information, and "pragma-like" information, called modifiers, for the emitters. See the topic "Implementation statements" earlier in this chapter.

Comment processing

The SOM IDL Compiler by default does not remove comments in the input source; instead, it attaches them to the nearest preceding IDL statement. This facility is useful, since it allows comments to be emitted in header files, C template files, documentation files, and so forth. However, if this capability is desired, this does mean that comments cannot be placed with quite as much freedom as with an ordinary IDL compiler. To turn off comment processing so that you can compile .idl files containing comments placed anywhere, you can use the compiler option -c or use "throw-away" comments throughout the .idl file (that is, comments preceded by //#); as a result, no comments will be included in the output files.

Generated header files

CORBA expects one header file, <file>.h, to be generated from <file>.idl. However, SOM IDL permits use of a class modifier, filestem, that changes this default output file name. (See "Running the SOM Compiler" later in this chapter.)

The SOM Compiler

The SOM Compiler translates the IDL definition of a SOM class into a set of "binding files" appropriate for the language that will implement the class's methods and the language(s) that will use the class. These bindings make it more convenient for programmers to implement and use SOM classes. The SOM Compiler currently produces binding files for the C and C++ languages.

Important Note
C and C++ bindings can not both be generated during the same execution of the SOM compiler.

Generating binding files

The SOM Compiler operates in two phases:

  • A precompile phase, in which a precompiler analyzes an OIDL or IDL class definition, and
  • An emission phase, in which one or more emitter programs produce binding files.

Each binding file is generated by a separate emitter program. Setting the SMEMIT environment variable determines which emitters will be used, as described below. Note: In the discussion below, the <filesystem> is determined by default from the name of the source .idl file with the ".idl" extension removed. Otherwise, a "filestem" modifier can be defined in the .idl file to specify another file name (see "Modifier statements" above).

Note
If changes to definitions in the .idl file later become necessary, the SOM Compiler should be rerun to update the current implementation template file, provided that the c or xc emitter is specified (either with the -s option or the SMEMIT environment variable, as described below). For more information on generating updates, see "Running incremental updates of the implementation template file" later in this chapter.

The emitters for the C language produce the following binding files:

<filestem>.c (produced by the c emitter)
This is a template for a C source program that implements a class's methods. This will become the primary source file for the class. (The other binding files can be generated from the .idl file as needed.) This template implementation file contains "stub" procedures for each method introduced or overridden by the class. (The stub procedures are empty of code except for required initialization and debugging statements.)

After the class implementer has supplied the code for the method procedures, running the c emitter again will update the implementation file to reflect changes made to the class definition (in the .idl file). These updates include adding new stub procedures, adding comments, and changing method prototypes to reflect changes made to the method definitions in the IDL specification. Existing code within method procedures is not disturbed, however.

The .c file contains an #include directive for the .ih file, described below.

The contents of the C source template is controlled by the Emitter Framework file <SOMBASE>/include/ctm.efw. This file can be customized to change the template produced. For detailed information on changing the template file see the Emitter Framework Guide and Reference.

<filestem>.h (produced by the h emitter)
This is the header file to be included by C client programs (programs that use the class). It contains the C usage bindings for the class, including macros for accessing the class's methods and a macro for creating new instances of the class. This header file includes the header files for the class's parent classes and its metaclass, as well as the header file that defines SOM's generic C bindings, som.h.
<filestem>.ih (produced by the ih emitter)
This is the header file to be included in the implementation file (the file that implements the class's methods-the .c file). It contains the implementation bindings for the class, including:
  • a struct defining the class's instance variables,
  • macros for accessing instance variables,
  • macros for invoking parent methods the class overrides,
  • the <className>GetData macro used by the method procedures in the <filestem>.c file (see "Stub procedures for methods" in Section 5.4 of Chapter 5.)
  • a <className>NewClass procedure for constructing the class object at run time, and
  • any IDL types and constants defined in the IDL interface.

The emitters for the C++ language produce the following binding files:

<filestem>.C (for AIX) or <filestem>.cpp (for OS/2) (produced by the xc emitter)

This is a template for a C++ source program that implements a class's methods. This will become the primary source file for the class. (The other binding files can be generated from the .idl file as needed.) This template implementation file contains "stub" procedures for each method introduced or overridden by the class. (The stub procedures are empty of code except for required initialization and debugging statements.)

After the class implementer has supplied the code for the method procedures, running the xc emitter again will update this file to reflect changes made to the class definition (in the .idl file). These updates include adding new stub procedures, adding comments, and changing method prototypes to reflect changes made to the method definitions in the IDL specification. Existing code within method procedures is not disturbed, however.

The C++ implementation file contains an #include directive for the .xih file, described below.

The contents of the C++ source template is controlled by the Emitter Framework file <SOMBASE>/include/ctm.efw. This file can be customized to change the template produced. For detailed information on changing the template file see the Emitter Framework Guide and Reference.

<filestem>.xh (produced by the xh emitter)

This is the header file to be included by C++ client programs that use the class. It contains the usage bindings for the class, including a C++ definition of the class, macros for accessing the class's methods, and the new operator for creating new instances of the class. This header file includes the header files for the class's parent classes and its metaclass, as well as the header file that defines SOM's generic C++ bindings, som.xh.

<filestem>.xih (produced by the xih emitter)

This is the header file to be included in the implementation file (the file that implements the class's methods). It contains the implementation bindings for the class, including:

  • a struct defining the class's instance variables,
  • macros for accessing instance variables,
  • macros for invoking parent methods the class overrides,
  • the <className>GetData macro (see section 5.4),
  • a <className>NewClass procedure for constructing the class object at run time, and
  • any IDL types and constants defined in the IDL interface.

Other files the SOM Compiler generates:

<filestem>.hh (produced by the hh emitter)

This file is a DirectToSOM C++ header file that describes a SOMobjects class in a way appropriate to DTS C++. When running this emitter, you must include the noqualitytypes command-line modifier for the -m optionof the SOM Compiler command sc or somc.

<filestem>pdl (produced by the pdl emitter)

This file is the same as the .idl file from which it is produced except that all items within the .idl file that are marked as "private" have been removed. (an item is marked as private by surrounding it with "#ifdef_PRIVATE_" and "#endif" directives. Thus, the pdl (Public Definition Lnguage) emitter can be used to generate a "public" version of the .idl file.

<filestem>.def (for OS/2) (produced by the def emitter)

This file is used by the linker to package a class as a library. To combine several classes into a single library, you must merge the exports statements from each of their .def files into a single .def file for the entire library. When packaging multiple classes in a single library, you must also write a simple C procedure named SOMInitModule and add it to the export list. This procedure should call the routine <className>NewClass for each class packaged in the library. The SOMInitModule procedure is called by the SOM Class Manager when the library is dynamically loaded.

<filestem>.exp (for AIX) (produced by the exp emitter)

This file is used by the linker to package a class as a library. To combine several classes into a single library, you must merge the exports statements from each of their .exp files into a single .exp file for the entire library. When packaging multiple classes in a single library, you must also write a simple C procedure named SOMInitModule and add it to the export list. This procedure should call the routine <className>NewClass for each class packaged in the library The SOMInitModule procedure is called by the SOM Class Manager when the library is dynamically loaded.

The Interface Repository (produced by the ir emitter)

See Chapter 7 for a discussion on the Interface Repository.

Note: The C/C++ bindings generated by the SOM Compiler have the following limitation: If two classes named "ClassName" and "ClassNameC" are defined, the bindings for these two classes will clash. That is, if a client program uses the C/C++ bindings (includes the .h/.xh header file) for both classes, a name conflict will occur. Thus, class implementers should keep this limitation in mind when naming their classes.

SOM users can extend the SOM Compiler to generate additional files by writing their own emitters. To assist users in extending the SOM Compiler, SOM provides an Emitter Framework a collection of classes and methods useful for writing object-oriented emitters that the SOM Compiler can invoke. For more information, see the Emitter Framework Guide and Reference.

Note re: porting SOM classes: The header files (binding files) that the SOM Compiler generates will only work on the platform (operating system) on which they were generated. Thus, when porting SOM classes from the platform where they were developed to another platform, the header files must be regenerated from the .idl file by the SOM Compiler on that target platform.

Environment variables affecting the SOM Compiler

To execute the SOM Compiler on one or more files that contain IDL specifications for one or more classes, use the sc. or somc as follows:

sc [-options] files      (on AIX or OS/2) 
somc [-options] files    (on Windows) 

where "files" specifies one or more .idl files.

Available "-options" for the command are detailed in the next topic. The operation of the SOM Compiler (whether it produces C binding files or C++ binding files, for example) is also controlled by a set of environment variables that can be set before the sc command is issued. The applicable environment variables are as follows:

SMEMIT
Determines which output files the SOM Compiler produces. Its value consists of a list of items separated by semicolons for OS/2, or by semicolons or colons for AIX. Each item designates an emitter to execute. For example, the statement:
SET SMEMIT=c;h;ih (for OS/2, for C binding files)
export SMEMIT="c;h;ih" (for AIX)
directs the SOM Compiler to produce the C binding files "hello.c", "hello.h", and"hello.ih" from the "hello.idl" input specification. By comparison,
SET SMEMIT=xc;xh;xih (for OS/2)
export SMEMIT="xc;xh;xih" (for AIX)
directs the SOM Compiler to produce C++ binding files "hello.C" (for AIX) or "hello.cpp" (for OS/2), "hello.xh", and "hello.xih" from the "hello.idl" input specification.

By default, all output files are placed in the same directory as the input file. If the SMEMIT environment variable is not set, then a default value of "h;ih" is assumed.

Windows note: The SMEMIT environmental variable can be set by using the SET command before the somc command is issued. For example:

SET SMEMIT="c;h;ih"          (for Windows)

If you are running the SOM Compiler from a DOS box under Windows, make sure to define SMEMIT before Windows is started.

SMINCLUDE
Specifies where the SOM Compiler should look for .idl files #included by the .idl file being compiled. Its value should be one or more directory names separated by a semicolon when using OS/2, or separated by a semicolon or colon when using AIX. Directory names can be specified with absolute or relative pathnames. For example:
SET SMINCLUDE=.;..\MYSCDIR;C:\TOOLKT20\C\INCLUDE; (for OS/2 or Windows)

export SMINCLUDE=.:myscdir:/u/som/include (for AIX) 

The default value of the SMINCLUDE environment variable is the "include" subdirectory of the directory into which SOM has been installed.

SMTMP
Specifies the directory that the SOM Compiler should use to hold intermediate output files. This directory should not coincide with the directory of the input or output files. For AIX, the default setting of SMTMP is /tmp; for OS/2, the default setting of SMTMP is the root directory of the current drive.
OS/2 or Windows example:
SET SMTMP=..\MYSCDIR\GARBAGE
tells the SOM Compiler to place the temporary files in the GARBAGE directory.

Or, on OS/2 only:

SET SMTMP=%TMP%

tells the SOM Compiler to use the same directory for temporary files as given by the setting of the TMP environment variable (the defult location for temporary system files). (On Windows, you cannot set one variable to another.)

AIX example:

export SMTMP=$TMP
export SMTMP=../myscdir/garbage
SMKNOWNEXTS
Specifies additional emitters to which the SOM Compiler should add a header. For example, if you were to write a new emitter for Pascal, called "emitpas", then by default the SOM Compiler would not add any header comments to it. However, by setting SMKNOWNEXTS=pas, as shown:
set SMKNOWNEXTS=pas (for OS/2 or Windows)

export SMKNOWNEXTS=pas (for AIX)

the SOM Compiler will add a header to files generated with the "emitpas" emitter. The "header" added is a SOM Compiler-generated message plus any comments, such as copyright statements, that appear at the head of your .idl input file. For details on writing your own emitter, see the Emitter Framework Guide and Reference.

SOMIR
Specifies the name (or list of names) of the Interface Repository file. The ir emitter, if run, creates the Interface Repository, or checks it for consistency if it already exists. If the -u option is specified when invoking the SOM Compiler, the ir emitter also updates an existing Interface Repository.
SMADDSTAR
When defined, causes all interface references to have a "*" added to them for the C bindings. The command-line options -maddstar and -mnoaddstar supercede and override the SMADDSTAR setting, however.

Note: Environment variables that affect the SOM Compiler can be set for any -m options of the SOM Compiler command. See the -m option in the following topic, "Running the SOM Compiler." Also, the -E option in the following topic can be used to set an environment variable.

Running the SOM Compiler

The syntax of the command for running the SOM Compiler takes the forms:

sc [-options] files
somc[-options] files

The "files" specified in the sc or somc command denote one or more files containing the IDL class definitions to be compiled. If no extension is specified, .idl is assumed. By default, the <filestem> of the .idl file determines the filestem of each emitted file. Otherwise, a "filestem" modifier can be defined in the .idl file to specify another name (see "Modifier statements" discussed earlier).

Selected "-options" can be specified individually, as a string of option characters, or as a combination of both. Any option that takes an argument either must be specified individually or must appear as the final option in a string of option characters. Available options and their purposes are as follows:

-C n
Sets the maximum allowable size for a simple comment in the .idl file (default: 32767). This is only needed for very large single comments.
-D name[=def]
Defines name as in a #define directive. The default def is 1. This option is the same as the -D option for the C compiler. Note: This option can be used to define __PRIVATE__ so that the SOM Compiler will also compile any methods and attributes that have been defined as private using the directive #ifdef__PRIVATE__; however, the -p option does the same thing more easily. When a class contains private methods or attributes, both the implementation bindings and the usage bindings to be #included in the implementation should be generated using either the -p or -D_PRIVATE_ option.
-E variable=value
Sets an environment variable. (See the previous topic for a discussion of the available environment variables: SMADDSTAR, SMEMIT, SMINCLUDE, SMTMP, SMKNOWNEXTS, and SOMIR.)
-I dir
When looking for #included files, looks first in dir, then in the standard directories (same as the C compiler -I option).
-S n
Sets the total allowable amount of unique string space used in the IDL specification for names and passthru lines (default: 32767). This is only needed for very large .idl files.
-U name
Removes any initial definition (via a #define preprocessor directive) of symbol name.
-V
Displays version information about the SOM Compiler.
-c
Turns off comment processing. This allows comments to appear anywhere within an IDL specification (rather than in restricted places), and it causes comments not to be transferred to the output files that the SOM Compiler produces.
-d directory
Specifies a directory where all output files should be placed. If the -d option is not used, all output files are placed in the same directory as the input file.
-h or -?
Produces a listing of this option list. (This option is typically used in an sc or somc command that does not include a .idl file name)
-i filename
Specifies the name of the class definition file. Use this option to override the built-in assumption that the input file will have a .idl extension. Any filename supplied with the -i option is used exactly as it is specified.
-m name[=value]
Adds a global modifier. (See the following Note on the -m options, which explains how to convert any "-m name" modifier to an environment variable.)

Note: All command-line -m modifier options can be specified in the environment by changing them to UPPERCASE and prepending " SM" to them. For example, if you want to always set the options "-mnotc" and "-maddstar", set corresponding environment variables as follows:

On OS/2:

set SMNOTC=1
set SMADDSTAR=1

On AIX:

export SMNOTC=1
export SMADDSTAR=1

The currently supported global modifiers for the -m name[=value] option are as follows:

addprefixes
Adds 'functionprefixes' to the method procedure prototypes during an incremental update of the implementation template file. This option applies only when rerunning the c or xc emitter on an IDL file that previously did not specify a functionprefix. A class implementor who later decides to use prefixes should add a line in the 'implementation' section of the .idl file containing the specification:
functionprefix = prefix

(as described earlier in the topic "Modifier statements") and then rerun the c or xc emitter using the -maddprefixes option. The method procedure prototypes in the implementation file will then be updated so that each method name includes the assigned prefix. (This option does not support changes to existing prefix names, nor does it apply for OIDL files.)

addstar
This option causes all interface references to have a '*' added to them for the C bindings. See the earlier section entitled "Object types" for further details.
comment=comment string
where comment string can be either of the designations: "/*" or "//". This option indicates that comments marked in the designated manner in the .idl file are to be completely ignored by the SOM Compiler and will not be included in the output files. Note: Comments on lines beginning with "//#" are always ignored by the SOM Compiler.
corba
This option directs the SOM Compiler to compile the input definition according to strict CORBA-defined IDL syntax. This means, for example, that comments may appear anywhere and that pointers are not allowed. When the -mcorba option is used, parts of a .idl file surrounded by #ifdef__SOMIDL__ and #endif directives are ignored. This option can be used to determine whether all nonstandard constructs (those specific to SOM IDL) are properly protected by #ifdef__SOMIDL__ and #endif directives.
csc
This option forces the OIDL compiler to be run. This is required only if you want to compile an OIDL file that does not have an extension of .csc or .sc.
emitappend
This option causes emitted files to be appended at the end of existing files of the same name.
noaccessors
This option turns off the automatic creation of OperationDef entries in the Interface Repository for attribute accessors (that is, for an attribute's _set and _get methods).
noaddstar
This option ensures that interface references will not have a "*" added to them for the C bindings. This is the default setting; it is the opposite of the -m compiler option addstar.
noint
This option directs the SOM Compiler not to warn about the portability problems of using int's in the source.
nolock
This option causes the Interface Repository Emitter emitir (see Chapter 7, "Interface Repository Framework") to leave the IR unlocked when updates are made to it. This can improve performance on networked file systems. By not locking the IR, however, there is the risk of multiple processes attempting to write to the same IR, with unpredictable results. This option should only be used when you know that only one process is updating an IR at once.
nopp
This option directs the SOM Compiler not to run the SOM preprocessor on the .idl input file.
noqualifytypes
This option prevents the use od C-scoped names in emitter output, and is used in conjunction with the .hh emitter.
notc
This option directs the SOM Compiler not to create TypeCode information when emitting IDL files that contain some undeclared types. This option is only used when compiling converted .csc files (that is, OIDL files originally) that have not had typing information added.
nouseshort
This option directs the SOM Compiler not to generate short forms for type names in the .h and .xh public header files. This can be useful to save disk space.
pbl
This option tells the SOM Compiler that, in declarations containing a linkage specifier, the "*" will appear before the linkage specifier. This is required when using any C++ compiler (Watcom is a known example) that cannot handle declarations in the default format where the "*" follows the linkage specifier. A default example is the declaration:
typedef void (SOMLINK * somTD_SOMObject_somFree)
             (SOMObject *somSelf);

Under the -mpbl option of the SOM Compiler command, the same example would be declared as:

typedef void (* SOMLINK somTD_SOMObject_somFree)
             (SOMObject *somSelf);
pp=preprocessor
This option directs the SOM Compiler to use the specified preprocessor as the SOM preprocessor, rather than the default "somcpp". Any standard C/C++ preprocessor can be used as a preprocessor for IDL specifications.
tcconsts
This option directs the SOM Compiler to generate TypeCode constants in the h and .xh public header files. Please refer to the Interface Repository (described in Chapter 7) for more details.
-p
Causes the "private" sections of the IDL file to be included in the compilation (that is, sections preceded by #ifdef __PRIVATE__ that contain private methods and attributes). Note: If -p is used, it must be applied for both the implementation bindings (.ih or .xih file) and the usage bindings (.h or .xh file) to be #included in the implementation.
-r
Checks that all names specified in the release order statement are valid method names (default: FALSE).
-s string
Substitutes string in place of the contents of the SMEMIT environment variable for the duration of the current sc command. This determines which emitters will be run and, hence, which output files will be produced. (If a list of values is given, on OS/2 only the list must be enclosed in double quotes.)
The -s option is a convenient way to override the SMEMIT environment variable. In OS/2 for example, the command:
> SC -s"h;c" EXAMPLE

is equivalent to the following sequence of commands:

> SET OLDSMEMIT=%SMEMIT%
> SET SMEMIT=H;C
> SC EXAMPLE
> SET SMEMIT=%OLDSMEMIT%

Similarly, in AIX the command:

> sc -sh";"c example

is equivalent to the following sequence of commands:

> export OLDSMEMIT=$SMEMIT
> export SMEMIT=h";"c
> sc example
> export SMEMIT=$OLDSMEMIT
-u
Updates the Interface Repository (default: no update). With this option, the Interface Repository will be updated even if the ir emitter is not explicitly requested in the SMEMIT environment variable or the -s option.
-v
Uses verbose mode to display informational messages (default: FALSE). This option is primarily intended for debugging purposes and for writers of emitters.
-w
Suppresses warning messages (default: FALSE).

The following sample commands illustrate various options for the sc command or similarly with somc):

sc -sc hello.idl
Generates file "hello.c".
sc -hV
Generates a help message and displays the version of the SOM Compiler currently available.
sc -vsh";"ih hello.idl
Generates "hello.h" and "hello.ih" with informational messages.
sc -sxc -doutdir hello.idl
Generates "hello.xc" in directory "outdir".

The 'pdl' Facility

As discussed earlier in this chapter, the SOM Compiler provides a pdl (Public Definition Language) emitter. This emitter generates a file that is the same as the .idl file from which it is produced, except that it removes all items within the .idl file that are marked as "private." An item is marked as private by surrounding it with "#ifdef_ _PRIVATE_ _" and "#endif" directives. Thus, the pdl emitter can be used to generate a "public" version of a .idl file. (Generally, client programs will need only the "public" methods and attributes.)

The SOMobjects Toolkit also provides a separate program, pdl, which performs the same function as the pdl emitter, but can be invoked independently of the SOM Compiler. In addition, the pdl program can remove any kind of items in the .idl file that are preceded by a user-specified "#ifdef" directive and followed by an "#endif" directive: The pdl program is invoked as follows:

pdl [ -c | d | f | h | s |/] files

where "files" specifies one or more .idl files whose specified "#ifdef" sections are to be removed. Filenames must be completely specified (with the .idl extension). If no "#ifdef" directive is specified (by including a -/<string> option), then the "#ifdef __PRIVATE__" sections will be removed by default.

The pdl command supports the following options. (Selected options can be specified individually, as a string of option characters, or as a combination of both. Any option that takes an argument either must be specified individually or must appear as the final option in a string of option characters.)

-c
cmd Specifies that, for each .idl file, the pdl program is to run the specified system command. This command may contain a single occurrence of the string "%s", which will be replaced with the source file name before the command is executed. For example the option -c"sc -sh %s" has the same effect as issuing the sc command with the -sh option.
-d
dir Specifies a directory in which the output files are to be placed. (The output files are given the same name as the input files.) If no directory is specified, the output files are named <fileStem>.pdl (where fileStem is the file stem of the input file) and are placed in the current working directory.
-h
Requests this description of the pdl command syntax and options.
-f
Specifies that output files are to replace existing files with the same name, even if the existing files are read-only. By default, files are replaced only if they have write access.
-s
smemit Specifies the SMEMIT variable with which the pdl program is to invoke the SOM Compiler.
-/
<string> Specifies the "#ifdef" pattern for which the pdl program will strip out .idl statements. The default is "#ifdef __PRIVATE__".

For example, to install public versions of the .idl files in the directory "pubinclude", type:

pdl -d pubinclude *.idl