Jump to content

SOM IDL and the SOM Compiler: Difference between revisions

From EDM2
Line 889: Line 889:
</PRE>
</PRE>


====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":
<PRE>
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>"
                        "";
        };
};
</PRE>
Note: See also the section "Using the tk_foreign TypeCode" in Chapter 7, "The Interface Repository Framework."


====Passthru statements====
====Passthru statements====

Revision as of 05:01, 15 August 2020

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

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation

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

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

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

Interface vs Implementation

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

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

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

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

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

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

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

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

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

SOM Interface Definition Language

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

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

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

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

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

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

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

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

Include directives (optional)

Type declarations (optional)

Constant declarations (optional)

Exception declarations (optional)

Interface declaration (optional)

Module declaration (optional)

Keywords for SOM IDL
any FALSE readonly
attribute float sequence
boolean implementation short
case in string
char inout struct
class interface switch
const long TRUE
context module TypeCode
default octet typedef
double oneway unsigned
enum out union
exception raises void

Include directives

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

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

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

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

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

Type and constant declarations

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

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

Integral types

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

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

Floating point types

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

Character type

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

Boolean type

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

Octet type

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

Any type

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

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

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

Constructed types

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

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

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

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

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

By contrast, the following definitions are not valid:

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

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

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

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

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

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

That specification would generate the following three types:

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

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

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

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

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

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

Union type

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

   { case+ }

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

case-label+   type-spec   declarator ;

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

case const-expr:
default:

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

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

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

is mapped onto the following C struct:

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

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

  Foo v;
  /* get a pointer to Foo in v: */
  switch(v->_d) {
    case 1: printf("x = %ld\n", v->_u.x); break;
    case 2: printf("y = %f\n", v->_u.y); break;
    default: printf("z = %c\n", v->_u.z); break;
  }

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

Template types (sequences and strings)

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

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

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

Note
The "simple-type" cannot have a '*' directly in the sequence statement. Instead, a typedef for the pointer type must be used. For example, instead of:
  typedef sequence<long *> seq_longptr; // Error: '*' not allowed.

use:

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

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

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

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

  typedef sequence<long, 10> vec10;

results in the following C struct:

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

and an instance of this type is declared as follows:

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

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

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

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

_IDL_SEQUENCE_type

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

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

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

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

string [ < positive-integer-const > ]

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

  typedef string<10> foo;

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

  typedef char *foo;

A variable of this type is then declared as follows:

  foo s = (char *) NULL;

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

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

Arrays

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

identifier {[ positive-integer-const ] }+

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

Pointers

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

type *declarator

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

Object types

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

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

  Foo myobj;

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

  Foo *myobj;

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

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

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

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

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

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

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

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

Exception declarations

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

exception identifier { member* };

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

type-spec declarators ;

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

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

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

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

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

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

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

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

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

would map onto:

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

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

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

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

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

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

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

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

Standard Exceptions Defined by CORBA

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

  enum completion_status { YES, NO, MAYBE };

  enum exception_type {NO_EXCEPTION, USER_EXCEPTION, SYSTEM_EXCEPTION};

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

Interface declarations

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

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

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

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

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

Constant, type, and exception declarations

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

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

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

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

  MyParentClass::myType

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

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

Attribute declarations

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

Attributes are declared as follows:

[ readonly ] attribute   type-spec declarators ;

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

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

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

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

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

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

Method (operation) declarations

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

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

Oneway keyword

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

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

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

Parameter list

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

{ in Æ out Æ inout } type-spec declarator

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

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

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

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

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

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

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

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

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

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

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

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

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

Raises expression

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

raises ( identifier1, identifier2,... )

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

Context expression

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

context ( identifier1, identifier2, ... )

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

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

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

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

Implementation statements

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

Designating 'private' methods and attributes

Module declarations to define multiple interfaces in a .idl file

Scooping and name resolution

Name usage in client programs

Extensions to CORBA IDL permitted by SOM IDL

Pointer '*' types

Unsigned types

Implementation section

Comment processing

Generated header files

The SOM Compiler

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

The 'pdl' Facility