Jump to content

The SOM Interface Repository Framework: Difference between revisions

From EDM2
No edit summary
Line 140: Line 140:
:An instance of this class exists for each exception defined in an IDL source file. ExceptionDefs may be found inside of (contained by) any Interface Repository Container except an OperationDef.  
:An instance of this class exists for each exception defined in an IDL source file. ExceptionDefs may be found inside of (contained by) any Interface Repository Container except an OperationDef.  


Repository One instance of this class exists for the entire SOM Interface Repository, to hold IDL elements that are global in scope. The instance of this class does not, however, reside within the IR itself.  
;Repository  
:One instance of this class exists for the entire SOM Interface Repository, to hold IDL elements that are global in scope. The instance of this class does not, however, reside within the IR itself.  


===Methods introduced by Interface Repository classes===
===Methods introduced by Interface Repository classes===

Revision as of 00:11, 10 May 2021

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 section covers the following subjects:

  • Introduction
  • Using the SOM Compiler to Build an Interface Repository
  • Managing Interface Repository files
  • Programming with the Interface Repository Objects

Introduction

The SOM Interface Repository (IR) is a database that the SOM Compiler optionally creates and maintains from the information supplied in IDL source files. The Interface Repository contains persistent objects that correspond to the major elements in IDL descriptions. The SOM Interface Repository Framework is a set of classes that provide methods whereby executing programs can access these objects to discover everything known about the programming interfaces of SOM classes.

The programming interfaces used to interact with Interface Repository objects, as well as the format and contents of the information they return, are architected and defined as part of the Object Management Group's CORBA standard. The classes composing the SOM Interface Repository Framework implement the programming interface to the CORBA Interface Repository. Accordingly, the SOM Interface Repository Framework supports all of the interfaces described in The Common Object Request Broker: Architecture and Specification (OMG Document Number 91.12.1, Revision 1.1, chapter 7).

As an extension to the CORBA standard, the SOM Interface Repository Framework permits storage in the Interface Repository of arbitrary information in the form of SOM IDL modifiers. That is, within the SOM-unique implementation section of an IDL source file or through the use of the #pragma modifier statement, user-defined modifiers can be associated with any element of an IDL specification. (See the section entitled "SOM Interface Definition Language" in Chapter 4, "SOM IDL and the SOM Compiler.") When the SOM Compile creates the Interface Repository from an IDL specification, these potentially arbitrary modifiers are stored in the IR and can then be accessed via the methods provided by the Interface Repository Framework.

This chapter describes, first, how to build and manage interface repositories, and second, the programming interfaces embodied in the SOM Interface Repository Framework.

Using the SOM Compiler to Build an Interface Repository

The SOMobjects Toolkit includes an Interface Repository emitter that is invoked whenever the SOM Compiler is run using an sc command with the -u option (which "updates" the interface repository). The IR emitter can be used to create or update an Interface Repository file. The IR emitter expects that an environment variable, SOMIR, was first set to designate a file name for the Interface Repository. For example, to compile an IDL source file named "newcls.idl" and create an Interface Repository named "newcls.ir", use a command sequence similar to the following:

For OS/2:

set SOMIR=c:\myfiles\newcls.ir
sc -u newcls

For AIX:

export SOMIR=~/newcls.ir
sc -u newcls

If the SOMIR environment variable is not set, the Interface Repository emitter creates a file named "som.ir" in the current directory.

The sc or somc command runs the Interface Reposito emitter plus any other emitters indicated by the environment variable SMEMIT (described in the topic "Running the SOM Compiler" in Chapter 4, "Implementing SOM Classes"). To run the Interface Repository emitter by itself, issue the sc command with the -s option (which overrides SMEMIT) set to "ir". For example:

sc -u -sir newcls        (ON OS/2 or AIX)
somc -u -sir newcls      (On Windows)

or equivalently,

sc -usir newcls          (On OS/2 or AIX)
somc -usir newcls        (On Windows)

The Interface Repository emitter uses the SOMIR environment variable to locate the designated IR file. If the file does not exist, the IR emitter creates it. If the named interface repository already exists, the IR emitter checks all of the "type" information in the IDL source file being compiled for internal consistency, and then changes the contents of the interface repository file to agree with with the new IDL definition. For this reason, the use of the -u compiler flag requires that all of the types mentioned in the IDL source file must be fully defined within the scope of the compilation. Warning messages from the SOM Compiler about undefined types result in actual error messages when using the -u flag.

The additional type checking and file updating activity implied by the -u flag increases the time it takes to run the SOM Compiler. Thus, when developing an IDL class description from scratch, where iterative changes are to be expected, it may be preferable not to use the -u compiler option until the class definition has stabilized.

Managing Interface Repository files

Just as the number of interface definitions contained in a single IDL source file is optional, similarly, the number of IDL files compiled into one interface repository file is also at the programmer's discretion. Commonly, however, all interfaces needed for a single project or class framework are kept in one interface repository.

The SOM IR file "som.ir"

The SOMobjects Toolkit includes an Interface Repository file ("som.ir") that contains objects describing all of the types, classes, and methods provided by the various frameworks of the SOMobjects Toolkit. Since all new classes will ultimately be derived from these predefined SOM classes, some of this information also needs to be included in a programmer's own interface repository files.

For example, suppose a new class, called "MyClass", is derived from SOMObject. When the SOM Compiler builds an Interface Repository for "MyClass", that IR will also include all of the information associated with the SOMObject class. This happens because the SOMObject class definition is inherited by each new class; thus, all of the SOMObject methods and typedefs are implicitly contained in the new class as well.

Eventually, the process of deriving new classes from existing ones would lead to a great deal of duplication of information in separate interface repository files. This would be inefficient, wasteful of space, and extremely difficult to manage. For example, to make an evolutionary change to some class interface, a programmer would need to know about and subsequently update all of the interface repository files where information about that interface occurred.

One way to avoid this dilemma would be to keep all interface definitions in a single interface repository (such as "som.ir"). This is not recommended, however. A single interface repository would soon grow to be unwieldy in size and become a source of frequent access contention. Everyone involved in developing class definitions would need update access to this one file, and simultaneous uses might result in longer compile times.

Managing IRs via the SOMIR environment variable

The SOMobjects Toolkit offers a more flexible approach to managing interface repositories. The SOMIR environment variable can reference an ordered list of separate IR files, which process from left to right. Taken as a whole, however, this gives the appearance of a single, logical interface repository. A programmer accessing the contents of "the interface repository" through the SOM Interface Repository framework would not be aware of the division of information across separate files. It would seem as though all of the objects resided in a single interface repository file.

A typical way to utilize this capability is as follows:

  • The first (leftmost) Interface Repository in the SOMIR list would be "som.ir". This file contains the basic interfaces and types needed in all SOM classes.
  • The second file in the list might contain interface definitions that are used globally across a particular enterprise.
  • A third interface repository file would contain definitions that are unique to a particular department, and so on.
  • The final interface repository in the list should be set aside to hold the interfaces needed for the project currently under development.

Developers working on different projects would each set their SOMIR environment variables to hold slightly different lists. For the most part, the leftmost portions of these lists would be the same, but the rightmost interface repositories would differ. When any given developer is ready to share his/her interface definitions with other people outside of the immediate work group, that person's interface repository can be promoted to inclusion in the master list.

With this arrangement of IR files, the more stable repositories are found at the left end of the list. For example, a developer should never need to make any significant changes to "som.ir", because these interfaces are defined by IBM and would only change with a new release of the SOMobjects Toolkit.

The Interface Repository Framework only permits updates in the rightmost file of the SOMIR interface repository list. That is, when the SOM Compiler -u flag is used to update the Interface Repository, only the final file on the IR list will be affected. The information in all preceding interface repository files is treated as "read only". Therefore, to change the definition of an interface in one of the more global interface repository files, a developer must overtly construct a special SOMIR list that omits all subsequent (that is, further to the right) interface repository files, or else petition the owner of that interface to make the change.

It is important the the rightmost filename in the SOMIR interface repository list not appear elsewhere in the list. For Example, the following setting for SOMIR:

    %SOMBASE5\ETC\SOM.IR;SOM.IR;C:\IR\COMPANY.IR;SOM.IR

would cause problems when attempting to update the SOM.IR file, because SOM.IR appears twice in the list.

Here is an example that illustrates the use of multiple IR files with the SOMIR environment variable. In this example, the SOMBASE environment variable represents the directory in which the SOMobjects Toolkit files have been installed. Only the "myown.ir" interface repository file will be updated with the interfaces found in files "myclass1.idl", "myclass2.idl", and "myclass3.idl".

For OS/2:

set BASE_IRLIST=%SOMBASE%\IR\SOM.IR;C:\IR\COMPANY.IR;C:\IR\DEPT10.IR
set SOMIR=%BASE_IRLIST%;D:\MYOWN.IR
set SMINCLUDE=.;%SOMBASE%\INCLUDE;C:\COMPANY\INCLUDE;C:\DEPT10\INCLUDE
sc -usir myclass1
sc -usir myclass2
sc -usir myclass3

For AIX:

export BASE_IRLIST=$SOMBASE/ir/som.ir:/usr/local/ir/comany.ir:\
    /usr/local/ir/dept10.ir
export SOMIR=$BASE_IRLIST:~/myown.ir
export SMINCLUDE=.:$SOMBASE/INCLUDE:/usr/local/company/include:\
    /usr/local/dept10/include
sc -usir myclass1
sc -usir myclass2
sc -usir myclass3

Placing 'private' information in the Interface Repository

When the SOM Compiler updates the Interface Repository in response to the -u flag, it uses all of the information available from the IDL source file. However, if the __PRIVATE__ preprocessor variable is used to designate certain portions of the IDL file as private, the preprocessor actually removes that information before the SOM Compiler sees it. Consequently, private information will not appear in the Interface Repository unless the -p compiler option is also used in conjunction with -u. For example:

sc -up myclass1          (On AIX or OS/2)
somc -up myclass1        (On Windows)

This command will place all of the information in the "myclass1.idl" file, including the private portions, in the Interface Repository.

If you are using tools that understand SOM and rely on the Interface Repository to describe the types and instance data in your classes, you may need to include the private sections from your IDL source files when building the Interface Repository.

Programming with the Interface Repository Objects

The SOM Interface Repository Framework provides an object-oriented programming interface to the IDL information processed by the SOM Compiler. Unlike many frameworks that require you to inherit their behavior in order to use it, the Interface Repository Framework is useful in its own right as a set of predefined objects that you can access to obtain information. Of course, if you need to subclass a class to modify its behavior, you can certainly do so; but typically this is not necessary.

The SOM Interface Repository contains the fully-analyzed (compiled) contents of all information in an IDL source file. This information takes the the form of persistent objects that can be accessed from a running program. There are ten classes of objects in the Interface Repository that correspond directly to the major elements in IDL source files; in addition, one instance of another class exists outside of the IR itself, as follows:

Contained All objects in the Interface Repository are instances of classes derived from this class and exhibit the common behavior defined in this interface.

Container Some objects in the Interface Repository hold (or contain) other objects. (For example, a module [ModuleDef] can contain an interface [InterfaceDef].) All Interface Repository objects that hold other objects are instances of classes derived from this class and exhibit the common behavior defined by this class.

ModuleDef
An instance of this class exists for each module defined in an IDL source file. ModuleDefs are Containers, and they can hold ConstantDefs, TypeDefs, ExceptionDefs, InterfaceDefs, and other ModuleDefs.
InterfaceDef
An instance of this class exists for each interface named in an IDL source file. (One InterfaceDef corresponds to one SOM class.) InterfaceDefs are Containers, and they can hold ConstantDefs, TypeDefs, ExceptionDefs, AttributeDefs, and OperationDefs.
AttributeDef
An instance of this class exists for each attribute defined in an IDL source file. AttributeDefs are found only inside of (contained by) InterfaceDefs.
OperationDef
An instance of this class exists for each operation (method, _set_method, and _get_method) defined in an IDL source file. OperationDefs are Containers that can hold ParameterDefs. OperationDefs are found only inside of (contained by) InterfaceDefs.
ParameterDef
An instance of this class exists for each parameter of each operation (method) defined in an IDL source file. ParameterDefs are found only inside of (contained by) OperationDefs.
TypeDef
An instance of this class exists for each typedef, struct, union, or enum defined in an IDL source file. TypeDefs may be found inside of (contained by) any Interface Repository Container except an OperationDef.
ConstantDef
An instance of this class exists for each constant defined in an IDL source file. ConstantDefs may be found inside (contained by) of any Interface Repository Container except an OperationDef.
ExceptionDef
An instance of this class exists for each exception defined in an IDL source file. ExceptionDefs may be found inside of (contained by) any Interface Repository Container except an OperationDef.
Repository
One instance of this class exists for the entire SOM Interface Repository, to hold IDL elements that are global in scope. The instance of this class does not, however, reside within the IR itself.

Methods introduced by Interface Repository classes

The Interface Repository classes introduce nine new methods, which are briefly described below. Many of the classes simply override methods to customize them for the corresponding IDL element; this is particularly true for classes representing IDL elements that are only contained within another syntactic element. Full descriptions of each method are found in the SOMobjects Developer Toolkit: Programmers Reference Manual.

  • Contained class methods (all IR objects are instances of this class and exhibit this behavior):

describe Returns a structure of type Description containing all information defined in the IDL specification of the syntactic element corresponding to the target Contained object. For example, for a target InterfaceDef object, the describe method returns information about the IDL interface declaration. The Description structure contains a "name" field with an identifier that categorizes the description (such as, "InterfaceDescription") and a "value" field holding an "any" structure that points to another structure containing the IDL information for that particular element (in this example, the interface's IDL specifications).

within Returns a sequence designating the object(s) of the IR within which the target Contained object is contained. For example, for a target TypeDef object, it might be contained within any other IR object(s) except an OperationDef object.

  • Container class methods (some IR objects contain other objects and exhibit this behavior):

contents Returns a sequence of pointers to the object(s) of the IR that the target Container object contains. (For example, for a target InterfaceDef object, the contents method returns a pointer to each IR object that corresponds to a part of the IDL interface declaration.) The method provides options for excluding inherited objects or for limiting the search to only a specified kind of object (such as AttributeDefs).

describe_contents Combines the describe and contents methods; returns a sequence of ContainerDescription structures, one for each object contained by the target Container object. Each structure has a pointer to the related object, as well as "name" and "value" fields resulting from the describe method.

lookup_name Returns a sequence of pointers to objects of a given name contained within a specified Container object, or within (sub)objects contained in the specified Container object.

  • ModuleDef class methods:
 Override describe and within. 
  • InterfaceDef class methods:

describe_interface Returns a description of all methods and attributes of a given interface definition object that are held in the Interface Repository.

Also overrides describe and within.

  • AttributeDef class method:
 Overrides describe. 
  • OperationDef class method:
 Overrides describe. 
  • ParameterDef class method:
 Overrides describe. 
  • TypeDef class method:
 Overrides describe. 
  • ConstantDef class method:
 Overrides describe. 
  • ExceptionDef class method:
 Overrides describe. 
  • Repository class methods:

lookup_id Returns the Contained object that has a specified RepositoryId.

lookup_modifier Returns the string value held by a SOM or user-defined modifier, given the name and type of the modifier, and the name of the object that contains the modifier.

release_cache Releases, from the internal object cache, the storage used by all currently unreferenced Interface Repository objects.

Accessing objects in the Interface Repository

As mentioned above, one instance of the Repository class exists for the entire SOM Interface Repository. This object does not, itself, reside in the Interface Repository (hence it does not exhibit any of the behavior defined by the Contained class). It is, however, a Container, and it holds all ConstantDefs, TypeDefs, ExceptionDefs, InterfaceDefs, and ModuleDefs that are global in scope (that is, not contained inside of any other Containers).

When any method provided by the Repository class is used to locate other objects in the Interface Repository, those objects are automatically instantiated and activated. Consequently, when the program is finished using an object from the Interface Repository, the client code should release the object using the somFree method.

All objects contained in the Interface Repository have both a "name" and a "Repository ID" associated with them. The name is not guaranteed to be unique, but it does uniquely identify an object within the context of the object that contains it. The Repository ID of each object is guaranteed to uniquely identify that object, regardless of its context.

For example, two TypeDef objects may have the same name, provided they occur in separate name scopes (ModuleDefs or InterfaceDefs). In this case, asking the Interface Repository to locate the TypeDef object based on its name would result in both TypeDef objects being returned. On the other hand, if the name is looked up from a particular ModuleDef or InterfaceDef object, only the TypeDef object within the scope of that ModuleDef or InterfaceDef would be returned. By contrast, once the Repository ID of an object is known, that object can always be directly obtained from the Repository object via its Repository ID.

C or C++ programmers can obtain an instance of the Repository class using the RepositoryNew macro. Programmers using other languages (and C/C++ programmers without static linkage to the Repository class) should invoke the method somGetInterfaceRepository on the SOMClassMgrObject . For example,

For C or C++ (static linkage):

#include <repostry.h>
Repository repo;

repo = RepositoryNew();

From other languages (and for dynamic linkage in C/C++):

1.Use the somEnvironmentNew function to obtain a pointer to the SOMClassMgrObject, as described in Chapter 3, "Using SOM Classes."

2.Use the somResolve or somResolveByName function to obtain a pointer to the somGetInterfaceRepository method procedure.

3.Invoke the method procedure on the SOMClassMgrObject, with no additional arguments, to obtain a pointer to the Repository object.

After obtaining a pointer to the Repository object, use the methods it inherits from Container or its own lookup_id method to instantiate objects in the Interface Repository. As an example, the contents method shown in the C fragment below activates every object with global scope in the Interface Repository and returns a sequence containing a pointer to every global object:

#include <containd.h>      /* Behavior common to all
IR objects */
Environment *ev;
int i;
sequence(Contained) everyGlobalObject;

ev = SOM_CreateLocalEnvironment(); /* Get an environment to use */
printf ("Every global object in the Interface Repository:\n");

everyGlobalObject = Container_contents (repo, ev, "all", TRUE);

for (i=0; i < everyGlobalObject._length; i++) {
    Contained aContained;

    aContained = (Contained) everyGlobalObject._buffer[i];
    printf ("Name: %s, Id: %s\n",
        Contained__get_name (aContained, ev),
        Contained__get_id (aContained, ev));
    SOMObject_somFree (aContained);
}

Taking this example one step further, here is a complete program that accesses every object in the entire Interface Repository. It, too, uses the contents method, but this time recursively calls the contents method until every object in every container has been found:

#include <stdio.h>
#include <containd.h>
#include <repostry.h>

void showContainer (Container c, int *next);

main ()
{
    int count = 0;
    Repository repo;

    repo = RepositoryNew ();
    printf ("Every object in the Interface Repository:\n\n");
    showContainer ((Container) repo, &count);
    SOMObject_somFree (repo);
    printf ("%d objects found\n", count);
    exit (0);
}

void showContainer (Container c, int *next)
{
    Environment *ev;
    int i;
    sequence(Contained) everyObject;

    ev = SOM_CreateLocalEnvironment (); /* Get an environment */
    everyObject = Container_contents (c, ev, "all", TRUE);

    for (i=0; i<everyObject._length; i++) {
        Contained aContained;

        (*next)++;
        aContained = (Contained) everyObject._buffer[i];
        printf ("%6d. Type: %-12s id: %s\n", *next,
            SOMObject_somGetClassName (aContained),
            Contained__get_id (aContained, ev));
        if (SOMObject_somIsA (aContained, _Container))
            showContainer ((Container) aContained, next);
        SOMObject_somFree (aContained);
    }
}

Once an object has been retrieved, the methods and attributes appropriate for that particular object can then be used to access the information contained in the object. The methods supported by each class of object in the Interface Repository, as well as the classes themselves, are documented in the SOMobjects Developer Toolkit: Programmers Reference Manual.

A word about memory management

Several conventions are built into the SOM Interface Repository with regard to memory management. You will need to understand these conventions to know when it is safe and appropriate to free memory references and also when it is your responsibility to do so.

All methods that access attributes (such as, the _get_<attribute> methods) always return either simple values or direct references to data within the target object. This is necessary because these methods are heavily used and must be fast and efficient. Consequently, you should never free any of the memory references obtained through attributes. This memory will be released automatically when the object that contains it is freed.

For all methods that give out object references (there are five: within, contents, lookup_name, lookup_id, and describe_contents), when finished with the object, you are expected to release the object reference by invoking the somFree method. (This is illustrated in the sample program that accesses all Interface Repository objects.) Do not release the object reference until you have either copied or finished using all of the information obtained from the object.

The describe methods (describe, describe_contents, and describe_interface) return structures and sequences that contain information. The actual structures returned by these methods are passed by value (and hence should only be freed if you have allocated the memory used to receive them). However, you may be required to free some of the information contained in the returned structures when you are finished. Consult the specific method in the SOMobjects Developer Toolkit: Programmers Reference Manual for more details about what to free.

During execution of the describe and lookup methods, sometimes intermediate objects are activated automatically. These objects are kept in an internal cache of objects that are in use, but for which no explicit object references have been returned as results. Consequently, there is no way to identify or free these objects individually. However, whenever your program is finished using all of the information obtained thus far from the Interface Repository, invoking the release_cache method causes the Interface Repository to purge its internal cache of these implicitly referenced objects. This cache will replenish itself automatically if the need to do so subsequently arises.

Using TypeCode pseudo-objects

Much of the detailed information contained in Interface Repository objects is represented in the form of TypeCodes. TypeCodes are complex data structures whose actual representation is hidden. A TypeCode is an architected way of describing in complete detail everything that is known about a particular data type in the IDL language, regardless of whether it is a (built-in) basic type or a (user-defined) aggregate type.

Conceptually, every TypeCode contains a "kind" field (which classifies it), and one or more parameters that carry descriptive information appropriate for that particular category of TypeCode. For example, if the data type is long, its TypeCode would contain a "kind" field with the value tk_long. No additional parameters are needed to completely describe this particular data type, since long is a basic type in the IDL language.

By contrast, if the TypeCode describes an IDL struct, its "kind" field would contain the value tk_struct, and it would possess the following parameters: a string giving the name of the struct, and two additional parameters for each member of the struct: a string giving the member name and another (inner) TypeCode representing the member's type. This example illustrates the fact that TypeCodes can be nested and arbitrarily complex, as appropriate to express the type of data they describe. Thus, a structure that has N members will have a TypeCode of tk_struct with 2N+1 parameters (a name and TypeCode parameter for each member, plus a name for the struct itself).

A tk_union TypeCode representing a union with N members has 3N+2 parameters: the type name of the union, the switch TypeCode, and a label value, member name and associated TypeCode for each member. (The label values all have the same type as the switch, except that the default member, if present, has a label value of zero octet.)

A tk_enum TypeCode (which represents an enum) has N+1 parameters: the name of the enum followed by a string for each enumeration identifier. A tk_string TypeCode has a single parameter: the maximum string length, as an integer. (A maximum length of zero signifies an unbounded string.)

A tk_sequence TypeCode has two parameters: a TypeCode for the sequence elements, and the maximum size, as an integer. (Again, zero signifies unbounded.)

A tk_array TypeCode has two parameters: a TypeCode for the array elements, and the array length, as an integer. (Arrays must be bounded.)

The tk_objref TypeCode represents an object reference; its parameter is a repository ID that identifies its interface.

A complete table showing the parameters of all possible TypeCodes is given in the SOMobjects Base Toolkit Programmers Reference Manual; see the TypeCode_kind function of the Interface Repository Framework.

TypeCodes are not actually "objects" in the formal sense. TypeCodes are referred to in the CORBA standard as pseudo-objects and described as "opaque". This means that, in reality, TypeCodes are special data structures whose precise definition is not fully exposed. Their implementation can vary from one platform to another, but all implementations must exhibit a minimal set of architected behavior. SOM TypeCodes support the architected behavior and have additional capability as well (for example, they can be copied and freed).

Although TypeCodes are not objects, the programming interfaces that support them adhere to the same conventions used for IDL method invocations in SOM. That is, the first argument is always a TypeCode pseudo-object, and the second argument is a pointer to an Environment structure. Similarly, the names of the TypeCode functions are constructed like SOM's C-language method-invocation macros (all functions that operate on TypeCodes are named TypeCode_<function-name>). Because of this ostensible similarity to an IDL class, the TypeCode programming interfaces can be conveniently defined in IDL as shown below.

A complete table showing the parameters of all possible TypeCodes is given in the SOMobjects Developer Toolkit Programmers Reference Manual; see the TypeCode_kind function of the Interface Repository Framework.

interface TypeCode {

enum TCKind {
    tk_null, tk_void,
    tk_short, tk_long, tk_ushort, tk_ulong,
    tk_float, tk_double, tk_boolean, tk_char,
    tk_octet, tk_any, tk_TypeCode, tk_Principal, tk_objref,
    tk_struct, tk_union, tk_enum, tk_string,
    tk_sequence, tk_array,

    // The remaining enumerators are SOM-unique extensions
    // to the CORBA standard.
    //
    tk_pointer, tk_self, tk_foreign
};

exception Bounds {};
// This exception is returned if an attempt is made
// by the parameter() operation (described below) to
// access more parameters than exist in the receiving
// TypeCode.

boolean equal (in TypeCode tc);
// Compares the argument with the receiver and returns TRUE
// if both TypeCodes are equivalent.  This is NOT a test for
// identity.

TCKind kind ();
// Returns the type of the receiver as a TCKind.

long param_count ();
// Returns the number of parameters that make up the
// receiving TypeCode.

any parameter (in long index) raises (Bounds);
// Returns the indexed parameter from the receiving TypeCode.
// Parameters are indexed from 0 to param_count()-1.
//
//  The remaining operations are SOM-unique extensions.
//

short alignment ();
// This operation returns the alignment required for an instance
// of the type described by the receiving TypeCode.

TypeCode copy (in TypeCode tc);
// This operation returns a copy of the receiving TypeCode.

void free (in TypeCode tc);
// This operation frees the memory associated with the
// receiving TypeCode. Subsequently, no further use can be
// made of the receiver, which, in effect, ceases to exist.

void print (in TypeCode tc);
// This operation writes a readable representation of the
// receiving TypeCode to stdout.  Useful for examining
// TypeCodes when debugging.

void setAlignment (in short align);
// This operation overrides the required alignment for an
// instance of the type described by the receiving TypeCode.

long size (in TypeCode tc);
// This operation returns the size of an instance of the
// type represented by the receiving TypeCode.
};

A detailed description of the programming interfaces for TypeCodes is given in the SOMobjects Developer Toolkit: Programmers Reference Manual.

Providing 'alignment' information

In addition to the parameters in the TypeCodes that describe each type, a SOM-unique extension to the TypeCode functionality allows each TypeCode to carry alignment information as a "hidden" parameter. Use the TypeCode_alignment function to access the alignment value. The alignment value is a short integer that should evenly divide any memory address where an instance of the type will occur.

If no alignment information is provided in your IDL source files, all TypeCodes carry default alignment information. The default alignment for a type is the natural boundary for the type, based on the natural boundary for the basic types of which it may be composed. This information can vary from one hardware platform to another. The TypeCode will contain the default alignment information appropriate to the platform where it was defined.

To provide alignment information for the types and instances of types in your IDL source file, use the "align=N" modifier, where N is your specified alignment. Use standard modifier syntax of the SOM Compiler to attach the alignment information to a particular element in the IDL source file. In the following example, align=1 (that is, unaligned or no alignment) is attached to the struct "abc" and to one particular instance of struct "def" (the instance data item "y").

interface i {
    struct abc {
        long a;
        char b;
        long c;
    };
    struct def {
        char l;
        long m;
    };

    void foo ();

    implementation {

    //# instance data
        abc x;
        def y;
        def z;

    //# alignment modifiers
        abc: align=1;
        y: align=1;
    };
};


Be aware that assigning the required alignment information to a type does not guarantee that instances of that type will actually be aligned as indicated. To ensure that, you must find a way to instruct your compiler to provide the desired alignment. In practice, this can be difficult except in simple cases. Most compilers can be instructed to treat all data as aligned (that is, default alignment) or as unaligned, by using a compile-time option or #pragma. The more important consideration is to make certain that the TypeCodes going into the Interface Repository actually reflect the alignment that your compiler provides. This way, when programs (such as the DSOM Framework) need to interpret the layout of data during their execution, they will be able to accurately map your data structures. This happens automatically when using the normal default alignment.

If you wish to use unaligned instance data when implementing a class, place an "unattached" align=1 modifier in the implementation section. An unattached align=N modifier is presumed to pertain to the class's instance data structure, and will by implication be attached to all of the instance data items.

When designing your own public types, be aware that the best practice of all (and the one that offers the best opportunity for language neutrality) is to lay out your types carefully so that it will make no difference whether they are compiled as aligned or unaligned!

Using the 'tk_foreign' TypeCode

TypeCodes can be used to partially describe types that cannot be described in IDL (for example, a FILE type in C, or a specific class type in C++). The SOM-unique extension tk_foreign is used for this purpose. A tk_foreign TypeCode contains three parameters:

1.The name of the type, 2.An implementation context string, and 3.A length.

The implementation context string can be used to carry an arbitrarily long description that identifies the context where the foreign type can be used and understood. If the length of the type is also known, it can be provided with the length parameter. If the length is not known or is not constant, it should be specified as zero. If the length is not specified, it will default to the size of a pointer. A tk_foreign TypeCode can also have alignment information specified, just like any other TypeCode.

Using the following steps causes the SOM Compiler to create a foreign TypeCode in the Interface Repository:

1.Define the foreign type as a typedef SOMFOREIGN in the IDL source file.

2.Use the #pragma modifier statement to supply the additional information for the TypeCode as modifiers. The implementation context information is supplied using the "impctx" modifier.

3.Compile the IDL file using the -u option to place the information in the Interface Repository.

For example:

typedef SOMFOREIGN Point;

  1. pragma modifier Point: impctx="C++ Point class",length=12,align=4;


If a foreign type is used to define instance data, structs, unions, attributes, or methods in an IDL source file, it is your responsibility to ensure that the implementation and/or usage bindings contain an appropriate definition of the type that will satisfy your compiler. You can use the passthru statement in your IDL file to supply this definition. However, it is not recommended that you expose foreign data in attributes, methods, or any of the public types, if this can be avoided, because there is no guarantee that appropriate usage binding information can be provided for all languages. If you know that all users of the class will be using the same implementation language that your class uses, you may be able to disregard this recommendation.

TypeCode constants

TypeCodes are actually available in two forms: In addition to the TypeCode information provided by the methods of the Interface Repository, TypeCode constants can be generated by the SOM Compiler in your C or C++ usage bindings upon request. A TypeCode constant contains the same information found in the corresponding IR TypeCode, but has the advantage that it can be used as a literal in a C or C++ program anywhere a normal TypeCode would be acceptable.

TypeCode constants have the form TC_< typename>, where <typename> is the name of a type (that is, a typedef, union, struct, or enum) that you have defined in an IDL source file. In addition, all IDL basic types and certain types dictated by the OMG CORBA standard come with pre-defined TypeCode constants (such as TC_long, TC_short, TC_char, and so forth). A full list of the pre-defined TypeCode constants can be found in the file "somtcnst.h". You must explicitly include this file in your source program to use the pre-defined TypeCode constants.

Since the generation of TypeCode constants can increase the time required by the SOM Compiler to process your IDL files, you must explicitly request the production of TypeCode constants if you need them. To do so, use the "tcconsts" modifier with the -m option of the sc or somc command. For example, the command

sc -sh -mtcconsts myclass.idl (On AIX or Windows) somc -sh -mtcconsts myclass.idl (On Windows)


will cause the SOM Compiler to generate a "myclass.h" file that contains TypeCode constants for the types defined in "myclass.idl".

Using the IDL basic type 'any'

Some Interface Repository methods and TypeCode functions return information typed as the IDL basic type any. Usually this is done when a wide variety of different types of data may need to be returned through a common interface. The type any actually consists of a structure with two fields: a _type field and a _value field. The _value field is a pointer to the actual datum that was returned, while the _type field holds a TypeCode that describes the datum.

In many cases, the context in which an operation occurs makes the type of the datum apparent. If so, there is no need to examine the TypeCode unless it is simply as a consistency check. For example, when accessing the first parameter of a tk_struct TypeCode, the type of the result will always be the name of the structure (a string). Because this is known ahead of time, there is no need to examine the returned TypeCode in the any_type field to verify that it is a tk_string TypeCode. You can just rely on the fact that it is a string; or, you can check the TypeCode in the _type field to verify it, if you so choose.

An IDL any type can be used in an interface as a way of bypassing the strong type checking that occurs in languages like ANSI C and C++. Your compiler can only check that the interface returns the any structure; it has no way of knowing what type of data will be carried by the any during execution of the program. Consequently, in order to write C or C++ code that accesses the contents of the any correctly, you must always cast the _value field to reflect the actual type of the datum at the time of the access.

Here is an example of a code fragment written in C that illustrates how the casting must be done to extract various values from an any:

  1. include <som.h> /* For "any" & "Environment" typedefs */
  2. include <somtc.h> /* For TypeCode_kind prototype */

any result; Environment *ev;

printf ("result._value = "); switch (TypeCode_kind (result._type, ev)) {

   case tk_string:
       printf ("%s\n", *((string *) result._value));
       break;
   case tk_long:
       printf ("%ld\n", *((long *) result._value));
       break;
   case tk_boolean:
       printf ("%d\n", *((boolean *) result._value));
       break;
   case tk_float:
       printf ("%f\n", *((float *) result._value));
       break;
   case tk_double:
       printf ("%f\n", *((double *) result._value));
       break;
   default:
       printf ("something else!\n");

}


Note: Of course, an any has no restriction, per se, on the type of datum that it can carry. Frequently, however, methods that return an any or that accept an any as an argument do place semantic restrictions on the actual type of data they can accept or return. Always consult the reference page for a method that uses an any to determine whether it limits the range of types that may be acceptable.