Difference between revisions of "Using SOM Classes in Client Programs"

From EDM2
Jump to: navigation, search
(Standard exceptions)
(Methods)
Line 950: Line 950:
 
The following methods and functions are used to obtain information about an object (instance) or to determine whether a variable holds a valid SOM object.  
 
The following methods and functions are used to obtain information about an object (instance) or to determine whether a variable holds a valid SOM object.  
 
====Methods====
 
====Methods====
;somGetClass :Gets the class object of a specified object. The receiver of the method is the object whose class is desired. The method returns a pointer to the object's corresponding class object.  
+
;[[somGetClass]] :Gets the class object of a specified object. The receiver of the method is the object whose class is desired. The method returns a pointer to the object's corresponding class object.  
  
;somGetClassName :Obtains the class name of an object. The receiver of the method is the object whose class name is desired. The method returns a pointer to the name of the class of which the specified object is an instance.  
+
;[[somGetClassName]] :Obtains the class name of an object. The receiver of the method is the object whose class name is desired. The method returns a pointer to the name of the class of which the specified object is an instance.  
  
;somGetSize :Obtains the size of an object. The receiver of the method is the object. The method returns the amount of contiguous space, in bytes, that is needed to hold the object itself (not including any additional space that the object may be using or managing outside of this area).  
+
;[[somGetSize]] :Obtains the size of an object. The receiver of the method is the object. The method returns the amount of contiguous space, in bytes, that is needed to hold the object itself (not including any additional space that the object may be using or managing outside of this area).  
  
;somIsA :Determines whether an object is an instance of a given class or of one of its descendant classes. The receiver of the method is the object to be tested. An additional argument is the name of the class to which the object will be compared. This method returns TRUE if the object is an instance of the specified class or if (unlike somIsInstanceOf) it is an instance of any descendant class of the given class; otherwise, the method returns FALSE.  
+
;[[somIsA]] :Determines whether an object is an instance of a given class or of one of its descendant classes. The receiver of the method is the object to be tested. An additional argument is the name of the class to which the object will be compared. This method returns TRUE if the object is an instance of the specified class or if (unlike somIsInstanceOf) it is an instance of any descendant class of the given class; otherwise, the method returns FALSE.  
  
;somIsInstanceOf :Determines whether an object is an instance of a specific class (but not of any descendant class).The receiver of the method is the object. The argument is the name of the class to which the object will be compared. The method returns TRUE if the object is an instance of the specified class, or FALSE otherwise.  
+
;[[somIsInstanceOf]] :Determines whether an object is an instance of a specific class (but not of any descendant class).The receiver of the method is the object. The argument is the name of the class to which the object will be compared. The method returns TRUE if the object is an instance of the specified class, or FALSE otherwise.  
  
;somRespondsTo :Determines whether an object supports a given method. The receiver of the method is the object. The argument is the somId for the method in question. (A somId can be obtained from a string by using the somIdFromString function.) The somRespondsTo method returns TRUE if the object supports the method, or FALSE otherwise.  
+
;[[somRespondsTo]] :Determines whether an object supports a given method. The receiver of the method is the object. The argument is the somId for the method in question. (A somId can be obtained from a string by using the somIdFromString function.) The somRespondsTo method returns TRUE if the object supports the method, or FALSE otherwise.
  
 
====Functions====
 
====Functions====

Revision as of 03:59, 3 August 2020

This chapter discusses how to use SOM classes that have already been fully implemented. That is, these topics describe the steps that a programmer uses to instantiate an object and invoke some method(s) on it from within an application program.

Who should read this chapter?

  • Programmers who wish to use SOM classes that were originally developed by someone else will need to know the information in this chapter. These programmers often may not need the information from any subsequent chapters.
  • By contrast, class implementers who are creating their own SOM classes should continue with Chapter 4, "SOM IDL and the SOM Compiler," and Chapter 5, "Implementing Classes in SOM "for complete information on the SOM Interface Definition Language (SOM IDL) syntax and other details of class implementation.

Programs that use a class are referred to as client programs. A client program can be written in C, in C++, or in another language. As noted, this chapter describes how client programs can use SOM classes (classes defined using SOM, as described in Chapter 2, "Tutorial for Implementing SOM Classes" and in Chapter 4, "SOM IDL and the SOM Compiler"and Chapter 5 "Implementing Classes in SOM"). Using a SOM class involves creating instances of a class, invoking methods on objects, and so forth. All of the methods, functions, and macros described here can also be used by class implementers within the implementation file for a class.

Note
"Using a SOM class," as described in this chapter, does not include subclassing the class in a client program. In particular, the C++ compatible SOM classes made available in the .xh binding file can not be subclassed in C++ to create new C++ or SOM classes.

Some of the macros and functions described here are supplied as part of SOM's C and C++ usage bindings. These bindings are functions and macros defined in header files to be included in client programs. The usage bindings make it more convenient for C and C++ programmers to create and use instances of SOM classes. SOM classes can be also used without the C or C++ bindings, however. For example, users of other programming languages can use SOM classes, and C and C++ programmers can use a SOM class without using its language bindings. The language 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.

To use the C or C++ bindings for a class, a client program must include a header file for the class (using the #include preprocessor directive). For a C language client program, the file <classFileStem>.h must be included. For a C++ language client program, the file <classFileStem>.xh must be included. The SOM Compiler generates these header files from an IDL interface definition. The header files contain definitions of the macros and functions that make up the C or C++ bindings for the class. Whether the header files include bindings for the class's private methods and attributes (in addition to the public methods and attributes) depends on the IDL interface definition available to the user, and on how the SOM Compiler was invoked when generating bindings.

Usage binding headers automatically include any other bindings upon which they may rely. Client programs not using the C or C++ bindings for any particular class of SOM object (for example, a client program that does not know at compile time what classes it will be using) should simply include the SOM-supplied bindings for SOMObject, provided in the header file "somobj.h" (for C programs) or"somobj.xh" (for C++ programs).

For each task that a user of a SOM class might want to perform, this chapter shows how the task would be accomplished by:

  • a C programmer using the C bindings,
  • a C++ programmer using the C++ bindings, or
  • a programmer not using SOM's C or C++ language bindings.

If neither of the first two approaches is applicable, the third approach can always be used.

An Example Client Program

Following is a C program that uses the class "Hello" (as defined in the Tutorial in Chapter 2). The "Hello" class provides one attribute, "msg", of type string, and one method, "sayHello". The "sayHello" method simply displays the value of the "msg" attribute of the object on which the method is invoked.

   #include <hello.h>  /* include the header file for Hello */

   int main(int argc, char *argv[])
   {
       /* declare a variable (obj) that is a
        * pointer to an instance of the Hello class: */
       Hello obj;

       /* create an instance of the Hello class
        * and store a pointer to it in obj: */
       obj = HelloNew();

       /* invoke method _set_msg on obj with the argument
        * "Hello World Again". This method sets the value of
        * obj's 'msg' attribute to the specified string.
        */
        __set_msg(obj, somGetGlobalEnvironment(), "Hello World Again");

        /* invoke method sayHello on obj. This method prints
         * the value of obj's 'msg' attribute.  */
        _sayHello(obj, somGetGlobalEnvironment());

        _somFree(obj);
        return(0);
   }

The C++ version of the foregoing client program is shown below:

   #include <hello.xh>  /* include the header file for Hello */

   int main(int argc, char *argv[])
   {
       /* declare a variable (obj) that is a
        * pointer to an instance of the Hello class: */
       Hello *obj;

       /* create an instance of the Hello class
        * and store a pointer to it in obj: */
       obj = new Hello;

      /* invoke method _set_msg on obj with the argument
       * "Hello World Again". This method sets the value of
       * obj's 'msg' attribute to the specified string.  */
      obj->_set_msg(somGetGlobalEnvironment(), "Hello World Again");

      /* invoke method sayHello on obj. This method prints
       * the value of obj's 'msg' attribute.  */
      obj->sayHello(somGetGlobalEnvironment());

      obj->somFree();
      return(0);
   }

These client programs both produce the output:

Hello World Again

Using SOM Classes: the Basics

This section covers the following subjects:

  • Declaring object variables
  • Creating instances of a class
  • Invoking methods on objects
  • Using class objects
  • Compiling and linking

Declaring object variables

When declaring an object variable, an object interface name defined in IDL is used as the type of the variable. The exact syntax is slightly different for C vs. C++ programmers. Specifically,

<interfaceName> obj ; in C programs 

or

<interfaceName> *obj ; in C++ programs 

declares "obj" to be a pointer to an object that has type <interfaceName>. In SOM, objects of this type are instances of the SOM class named <interfaceName>, or of any SOM class derived from this class. Thus, for example,

Animal obj; in C programs 

or

Animal *obj; in C++ programs 

declares "obj" as pointer to an object of type "Animal" that can be used to reference an instance of the SOM class "Animal" or any SOM class derived from "Animal". Note that the type of an object need not be the same as its class; an object of type "Animal" might not be an instance of the "Animal" class (rather, it might be an instance of some subclass of "Animal" - the "Cat" class, perhaps).

All SOM objects are of type SOMObject, even though they may not be instances of the SOMObject class. Thus, if it is not known at compile time what type of object the variable will point to, the following declaration can be used:

SOMObject obj; in C programs 

or

SOMObject *obj; in C++ programs. 

Because the sizes of SOM objects are not known at compile time, instances of SOM classes must always be dynamically allocated. Thus, a variable declaration must always define a pointer to an object.

Note: In the C usage bindings, as within an IDL specification, an interface name used as a type implicitly indicates a pointer to an object that has that interface (this is required by the CORBA specification). The C usage bindings for SOM classes therefore hide the pointer with a C typedef for <interfaceName>. But this is not appropriate in the C++ usage bindings, which define a C++ class for <interfaceName>. Thus, it is not correct in C++ to use a declaration of the form:

<interfaceName> obj ; not valid in C++ programs 

Note: If a C programmer also prefers to use explicit pointers to <interfaceName> types, then the SOM Compiler option -maddstar can be used when the C binding files are generated, and the explicit " *" will then be required in declarations of object variables. (This option is required for compatibility with existing SOM OIDL code. For information on using the -maddstar option, see "Running the SOM Compiler" in Chapter 4, "SOM IDL and the SOM Compiler.")

Users of other programming languages must also define object variables to be pointers to the data structure used to represent SOM objects. The way this is done is programming-language dependent. The header file "somtypes.h" defines the structure of SOM objects for the C language.

Creating instances of a class

For C programmers with usage bindings, SOM provides the <className>New and the <className>Renew macros for creating instances of a class.

These macros are illustrated with the following two examples, each of which creates a single instance of class "Hello":

  obj = HelloNew();
  obj = HelloRenew(buffer);

Using <className> New

After verifying that the <className> class object exists, the <className>New macro invokes the somNew method on the class object. This allocates enough space for a new instance of <className>, creates a new instance of the class, initializes this new object by invoking somDefaultIniton it, and then returns a pointer to it. The <className>Newmacro automatically creates the the class object for <className>, as well as its ancestor classes and metaclass, if these objects have not already been created.

After a client program has finished using an object created using the <className >New macro, the object should be freed by invoking the method somFree on it:

  _somFree(obj);

After uninitializing the object by invoking somDestruct on it, somFree calls the class object for storage deallocation. This is important because storage for an object created using the <className>New macro is allocated by the class of the object. Thus, only the class of the object can know how to reclaim the object's storage. object for storage deallocation.

Using <className> Renew

After verifying that the <className> class object exists, the <className>Renew macro invokes the somRenew method on the class object. <className>Renew is only used when the space for the object has been allocated previously. (Perhaps the space holds an old uninitialized object that is not needed anymore.) This macro converts the given space into a new, initialized instance of <className> and returns a pointer to it. The programmer is responsible for ensuring that the argument of <className>Renew points to a block of storage large enough to hold an instance of class <className>. The SOM method somGetInstanceSize can be invoked on the class to determine the amount of memory required. Like <className>New, the <className>Renew macro automatically creates any required class objects that have not already been created.

Hint: When creating a large number of class instances, it may be more efficient to allocate at once enough memory to hold all the instances, and then invoke <className>Renew once for each object to be created, rather than performing separate memory allocations.

Using <className> NewClass

The C and C++ usage bindings for a SOM class also provide static linkage to a <className>NewClass function that can be used to create the class object. This can be useful if the class object is needed before its instances are created.

For example, the following C code uses the function HelloNewClass to create the "Hello" class object. The arguments to this function are defined by the usage bindings, and indicate the version of the class implementation that is assumed by the bindings. (For more detail on creation of classes, see the later section, "Creating a class object.") Once the class object has been created, the example invokes the method somGetInstanceSize on this class to determine the size of a "Hello" object, uses SOMMalloc to allocate storage, and then uses the HelloRenew macro to create ten instances of the "Hello" class:

   #include <hello.h>
   main()
   {
   SOMClass helloCls; /*  A pointer for the Hello class object */
   Hello objA[10];    /*  an array of Hello instances */
   unsigned char *buffer;
   int i;
   int size;

   /* create the Hello class object:  */
   helloCls =  HelloNewClass(Hello_MajorVersion, Hello_MinorVersion);

   /* get the amount of space needed for a Hello instance:
    * (somGetInstanceSize is a method provided by SOM.) */
   size =  _somGetInstanceSize(helloCls);
   size = ((size+3)/4)*4;  /* round up to doubleword multiple */

   /* allocate the total space needed for ten instances: */
   buffer =  SOMMalloc(10*size);

   /* convert the space into ten separate Hello instances: */
   for (i=0; i<10; i++)
       objA[i] = HelloRenew(buffer+i*size);
   ...
   ...
   /* Uninitialize the objects and free them */
   for (i=0; i<10; i++)
     _somDestruct(objA[i],0,0);
   SOMFree(buffer);
   }

When an object created with the <className>Renew macro is no longer needed, its storage must be freed using the dual to whatever method was originally used to allocate the storage. Two method pairs are typical:

  • For example, if an object was originally initialized using the <className>New macro, then, as discussed previously, the client should use the somFree method on it.
  • On the other hand, if the program uses the SOMMalloc function to allocate memory, as illustrated in the example above, then the SOMFree function must be called to free the objects' storage (because SOMFree is the dual to SOMMalloc). Before this is done, the objects in the region to be freed should be deinitialized by invoking the somDestruct method on them. This allows each object to free any memory that may have been allocated without the programmer's knowledge. (The somFree method also calls the somDestruct method.)


Note
In the somDestruct method call above, the first zero indicates that memory should not be freed by the class of the object (that is, the programmer will do it explicitly). The second zero indicates that the class of the object is responsible for overall control of object uninitialization. For further discussion, see Section 5.5, "Initializing and Uninitializing Objects," in Chapter 5, "Implementing Classes in SOM."

For C++ programmers with usage bindings, instances of a class <className> can be created with a new operator provided by the usage bindings of each SOM class. The new operator automatically creates the class object for <className>, as well as its ancestor classes and metaclass, if they do not yet exist. After verifying the existence of the desired class object, the new operator then invokes the somNewNoInit method on the class. This allocates memory and creates a new instance of the class, but it does not initialize the new object. Initialization of the new object is then performed using one of the C++ constructors defined by the usage bindings. (For further discussion, see Section 5.5 "Initializing and Uninitializing Objects," in Chapter 5, "Implementing Classes in SOM.") Two variations of the new operator require no arguments. When either is used, the C++ usage bindings provide a default constructor that invokes the somDefaultInit method on the new object. Thus, a new object initialized by somDefaultInit would be created using either of the forms:

new <className>

new <classname>()

For example:

  obj = new Hello;
  obj1 = new Hello();


For convenience, pointers to SOM objects created using the newoperator can be freed using the delete operator, just as for normal C++ objects (or, the somFree method could be used):

 delete obj;
 obj1->somFree;


When previously allocated space will be used to hold a new object, C++ programmers should use the somRenew method, described below. C++ bindings do not provide a macro for this purpose.

somNew and somRenew
C and C++ programmers, as well programmers using other languages, can create instances of a class using the SOM methods somNew and somRenew, invoked on the class object. As discussed and illustrated above for the C bindings, the class object must first be created using the <className>NewClass procedure (or, perhaps, using the somFindClass method-see the section "Using class objects," to follow later in this chapter).

The somNew method invoked on the class object creates a new instance of the class, initializes the object using somDefaultInit, and then returns a pointer to the new object. For instance, the following C example creates a new object of the "Hello" class.

   #include <hello.h>
   main()
   {
     SOMClass helloCls;   /* a pointer to the Hello class */
     Hello obj;           /* a pointer to an Hello instance */
     /* create the Hello class  */
     helloCls = HelloNewClass(Hello_MajorVersion, Hello_MinorVersion);
     obj = _somNew(helloCls); /* create the Hello instance */
   }

An object created using the somNew method should be freed by invoking the somFree method on it after the client program is finished using the object.

The somRenew method invoked on the class object creates a new instance of a class using the given space, rather than allocating new space for the object. The method converts the given space into an instance of the class, initializes the new object using somDefaultInit, and then returns a pointer to it. The argument to somRenew must point to a block of storage large enough to hold the new instance. The method somGetInstanceSize can be used to determine the amount of memory required. For example, the following C++ code creates ten instances of the "Hello" class:

   #include <hello.xh>
   #include <somcls.xh>
   main()
   {
     SOMClass *helloCls; // a pointer to the Hello class
     Hello *objA[10]  //  an array of Hello instance pointers
     unsigned char *buffer;
     int i;
     int size;

    // create the Hello class object
    helloCls =  HelloNewClass(Hello_MajorVersion, Hello_MinorVersion);

    // get the amount of space needed for a Hello instance:
    size = helloCls-> somGetInstanceSize();
    size = ((size+3)/4)*4;  // round up to doubleword multiple

    // allocate the total space needed for ten instances
    buffer =  SOMMalloc(10*size);

    // convert the space into ten separate Hello objects
    for (i=0; i<10; i++)
       objA[i] = helloCls-> somRenew(buffer+i*size);

    // Uninitialize the objects and free them
    for (i=0; i<10; i++)
       objA[i]-> somDestruct(0,0);
    SOMFree(buffer);
   }

The somNew and somRenew methods are useful for creating instances of a class when the header file for the class is not included in the client program at compile time. (The name of the class might be specified by user input, for example.) However, the <className>New macro (for C) and the new operator (for C++) can only be used for classes whose header file is included in the client program at compile time.

Objects created using the somRenew method should be freed by the client program that allocated it, using the dual to whatever allocation approach was initially used. If the somFree method is not appropriate (because the method somNew was not initially used), then, before memory is freed, the object should be explicitly deinitialized by invoking the somDestruct method on it. (The somFree method calls the somDestruct method. Refer to the previous C example for Renew for an explanation of the arguments to somDestruct.)

Invoking methods on objects

This topic describes the general way to invoke methods in C/C+ + and in other languages, and then presents subtopics for more specialized situations.

Making typical method calls

For C programmers with usage bindings: To invoke a method in C, use the macro:

_<methodName> (receiver, args) 

(that is, an underscore followed by the method name). Arguments to the macro are the receiver of the method followed by all of the arguments to the method. For example:

_foo(obj, somGetGlobalEnvironment(), x, y)


This invokes method "foo" on "obj" (the remaining arguments are arguments to the method "foo"). This expression can be used anywhere that a standard function call can be used in C.

Required arguments

In C, calls to methods defined using IDL require at least two arguments- a pointer to the receiving object (the object responding to the method) and a value of type (Environment *). The Environment data structure is specified by CORBA, and is used to pass environmental information between a caller and a called method. For example, it is used to return exceptions. (For more information on how to supply and use the Environment structure, see the later section entitled "Exceptions and error handling.")

In the IDL definition of a method, by contrast, the receiver and the Environment pointer are not listed as parameters to the method. (Unlike the receiver, the Environment pointer is considered a method parameter, even though it is never explicitly specified in IDL. For this reason, it is called an implicit method parameter.) For example, if a method is defined in a .idl file with two parameters, as in:

  int foo (in char c, in float f);


then, with the C usage bindings, the method would be invoked with four arguments, as in:

  intvar = _foo(obj, somGetGlobalEnvironment(), x, y);


where "obj" is the object responding to the method and "x" and "y" are the arguments corresponding to "c" and "f", above.

If the IDL specification of the method includes a context specification, then the method has an additional (implicit) context parameter. Thus, when invoking the method, this argument must follow immediately after the Environment pointer argument. (None of the SOM-supplied methods require context arguments.) The Environment and context method parameters are prescribed by the CORBA standard.

If the IDL specification of the class that introduces the method includes the callstyle=oidl modifier, then the (Environment*) and context arguments should not be supplied when invoking the method. That is, the receiver of the method call is followed immediately by the arguments to the method (if any). Some of the classes supplied in the SOMobjects Toolkit (including SOMObject, SOMClass, and SOMClassMgr) are defined in this way, to ensure compatibility with the previous release of SOM. The System Object Model Programming Reference specifies for each method whether these arguments are used.

If you use a C expression to compute the first argument to a method call (the receiver), you must use an expression without side effects, because the first argument is evaluated twice by the _<methodName> macro expansion. In particular, a somNew method call or a macro call of <className>New can not be used as the first argument to a C method call, because doing so would create two new class instances rather than one.

Following the initial, required arguments to a method (the receiving object, the Environment, if any, and the context, if any), you enter any additional arguments required by that method, as specified in IDL. For a discussion of how IDL in/out/inout argument types may to C/C++ data types, see the topic "Parameter list" in Chapter 4, "SOM IDL and the SOM Compiler."

Short form vs long form

If a client program uses the bindings for two different classes that introduce or inherit two different methods of the same name, then the _<methodName> macro described above (called the short form) will not be provided by the bindings, because the macro would be ambiguous in that circumstance. The following long form macro, however, is always provided by the usage bindings for each class that supports the method:

<className>_<methodName> (receiver, args) 

For example, method "foo" supported by class "Bar" can be invoked as:

 Bar_foo(obj, somGetGlobalEnvironment(), x, y)     (in C)

where "obj" has type "Bar" and "x" and "y" are the arguments to method "foo".

In most cases (where there is no ambiguity, and where the method is not a va_list method, as described in the subsequent subtopic "Using 'va_list' methods"), a C programmer may use either the short or the long form of a method invocation macro interchangeably. However, only the long form complies with the CORBA standard for C usage bindings. If you wish to write code that can be easily ported to other vendor platforms that support the CORBA standard, use the long form exclusively. The long form is always available for every method that a class supports. The short form is provided both as a programming convenience and for source code compatibility with release 1 of SOM.

In order to use the long form, a programmer will usually know what type an object is expected to have. If this is not known, but the different methods have the same signature, the method can be invoked using name-lookup resolution, as described in a following subtopic of this section.

For C++ programmers with usage bindings: To invoke a method, use the standard C++ form shown below:

obj-><methodName> (args) 

where args are the arguments to the method. For instance, the following example invokes method "foo" on "obj":

  obj->foo(somGetGlobalEnvironment(), x, y)
Required arguments

All methods introduced by classes declared using IDL (except those having the SOM IDL callstyle=oidl modifier) have at least one parameter-a value of type (Environment *). The Environment data structure is used to pass environmental information between a caller and a called method. For example, it is used to return exceptions. For more information on how to supply and use the Environment structure, see the later section entitled "Exceptions and error handling."

The Environment pointer is an implicit parameter; in the IDL definition of a method, the Environment pointer is not explicitly listed as a parameter to the method. For example, if a method is defined in IDL with two explicit parameters, as in:

  int foo (in char c, in float f);


then the method would be invoked from C++ bindings with three arguments, as in:

  intvar = obj->foo(somGetGlobalEnvironment(), x, y);


where "obj" is the object responding to the method and "x" and "y" are the arguments corresponding to "c" and "f", above.

If the IDL specification of the method includes a context specification, then the method has a second implicit parameter, of type context, and the method must be invoked with an additional context argument. This argument must follow immediately after the Environment pointer argument. (No SOM-supplied methods require context arguments.) The Environment and context method parameters are prescribed by the CORBA standard.

If the IDL specification of the class that introduces the method includes the callstyle=oidl modifier, then the (Environment *) and context arguments should not be supplied when the method is invoked. Some of the classes supplied in the SOMobjects Toolkit (including SOMObject, SOMClass, and SOMClassMgr) are defined in this way, to ensure compatibility with the previous release of SOM. The System Object Model Programming Reference specifies for each method whether these arguments are used.

Following the initial, required arguments to a method (the receiving object, the Environment, if any, and the context, if any), you enter any additional arguments required by that method, as specified in IDL. For a discussion of how IDL in/out/inout argument types map to C/C++ data types, see the topic "Parameter list" in Chapter 4, "SOM IDL and the SOM Compiler."

For non-C/C++ programmers: To invoke a static method (that is, a method declared when defining an OIDL or IDL object interface) without using the C or C++ usage bindings, a programmer can use the somResolve procedure. The somResolve procedure takes as arguments a pointer to the object on which the method is to be invoked and a method token for the desired method. It returns a pointer to the method's procedure (or raises a fatal error if the object does not support the method). Depending on the language and system, it may be necessary to cast this procedure pointer to the appropriate type; the way this is done is language-specific.

The method is then invoked by calling the procedure returned by somResolve (the means for calling a procedure, given a pointer to it, is language-specific), passing the method's receiver, the Environment pointer (if necessary), the context argument (if necessary) and the remainder of the method's arguments, if any. (See the section above for C programmers; the arguments to a method procedure are the same as the arguments passed using the long form of the C-language method-invocation macro for that method.)

Using somResolve requires the programmer to know where to find the method token for the desired method. Method tokens are available from class objects that support the method (via the method somGetMethodToken), or from a global data structure, called the ClassData structure, corresponding to the class that introduces the method. In C and C++ programs with access to the definitions for ClassData structures provided by usage bindings, the method token for method methodName introduced by class className may be accessed by the following expression:

<className>ClassData.<methodName > 

For example, the method token for method "sayHello"introduced by class "Hello" is stored at location HelloClassData.sayHello, for C and C++ programmers. The way method tokens are accessed in other languages is language-specific.

As an example of using offset resolution to invoke methods from a programming language other than C/C++, one would do the following to create an instance of a SOM Class X in Smalltalk:

1.Initialize the SOM run-time environment, if it has not previously been initialized, using the somEnvironmentNew function.

2.If the class object for class X has not yet been created, use somResolve with arguments SOMClassMgrObject (returned by somEnvironmentNew in step 1) and the method token for the somFindClass method, to obtain a method procedure pointer for the somFindClass method. Use the method procedure for somFindClass to create the class object for class X: Call the procedure with arguments SOMClassMgrObject, the result of calling the somIdFromString function with argument "X", and the major and minor version numbers for class X (or zero). The procedure returns the class object for class X.

3.Use somResolve with arguments representing the class object for X (returned by somFindClass in step 2) and the method token for the somNew method, to obtain a method procedure pointer for method somNew. (The somNew method is used to create instances of class X.)

4.Call the method procedure for somNew (using the method procedure pointer obtained in step 3) with the class object for X (returned by somFindClass in step 3) as the argument. The procedure returns a new instance of class X.

In addition to somResolve, SOM also supplies the somClassResolve procedure. Instead of an object, the somClassResolve procedure takes a class as its first argument, and then selects a method procedure from the instance method table of the passed class. (The somResolve procedure, by contrast, selects a method procedure from the instance method table of the class of which the passed object is an instance.) The somClassResolve procedure therefore supports casted method resolution. See the System Object Model Programming Reference for more information on somResolve and somClassResolve.

If the programmer does not know at compile time which class introduces the method to be invoked, or if the programmer cannot directly access method tokens, then the procedure somResolveByName can be used to obtain a method procedure using name-lookup resolution, as described in the next section.

If the signature of the method to be invoked is not known at compile time, but can be discovered at run time, use somResolve or somResolveByName to get a pointer to the somDispatch method procedure, then use it to invoke the specific method, as described below under "Method name or signature not known at compile time."

Accessing Attributes

In addition to methods, SOM objects can also have attributes. An attribute is an IDL shorthand for declaring methods, and does not necessarily indicate the presence of any particular instance data in an object of that type. Attribute methods are called "get" and "set" methods. For example, if a class "Hello" declares an attribute called "msg", then object variables of type "Hello" will support the methods _get_msg and _set_msg to access or set the value of the "msg" attribute. (Attributes that are declared as "readonly" have no "set" method, however.)

The "get" and "set" methods are invoked in the same way as other methods. For example, given class "Hello" with attribute "msg" of type string, the following code segments set and get the value of the "msg" attribute:

For C:

   #include <hello.h>
   Hello obj;
   Environment *ev = somGetGlobalEnvironment();

   obj = HelloNew();
   __set_msg(obj, ev, "Good Morning");/*note: two leading underscores */
   printf("%s\n", __get_msg(obj, ev));

For C++:

   #include <hello.xh>
   #include <stdio.h>
   Hello *obj;
   Environment *ev = somGetGlobalEnvironment();

   obj = new Hello;
   obj->_set_msg(ev, "Good Morning");
   printf("%s\n", obj->_get_msg(ev));

Attributes available with each class, if any, are described in the documentation of the class itself in the System Object Model Programming Reference.

Using 'va_list' methods

SOM supports methods whose final argument is a va_list. A va_list is a data type whose representation depends on the operating-system platform. To aid construction of portable code, SOM supports a platform-neutral API for building and manipulating va_lists. Use of this API is recommended on all platforms because it is compliant with the ANSI C standard and because of its portability.

Note
Initially support for va_list functions is available only for the PowerPC platform. With the release of SOM 3.0 however, support is extended to the PC platform.


A function to create a va_list is not provided. Instead, users declare local variables of type somVaBuf and va_list.

The following sequence of calls is used to create and destroy a va_list:

somVaBuf_create 
Creates a SOM buffer for variable arguments from which the va_list will be built.
somVaBuf_add 
Adds an argument to the SOM buffer for variable arguments.
somVaBuf_get_valist 
Copies the va_list from the SOM buffer.
somVaBuf_destroy 
Releases the SOM buffer and its associated va_list.
somvalistSetTarget 
Modifies the first scalar value on the va_list without other side effects.
somvalistGetTarget 
Gets the first scalar value from the va_list without other side effects.

Detailed information on these functions is provided in the System Object Model Programming Reference.

Examples of va_list usage

The following code segments pass a va_list to the somDispatch method by using the SOMobjects functions that build the va_list.

The somDispatch method (introduced by SOMObject) is a particularly useful method whose final argument is a va_list. As explained in the System Object Model Programming Reference, somDispatch can be used to invoke some other method (called the "dispatched" method) on an object when usage bindings for the dispatched method are not available or when the method to be dispatched is not known until run time. The va_list argument for somDispatch holds the arguments that will be passed to the dispatched method, including the target object for the dispatched method.

For C
#include <somobj.h>
void f1(SOMObject obj, Environment *ev)
{
        char *msg;
        va_list start_val;
        somVaBuf vb;
        char *msg1 = "Good Morning";

        vb = (somVaBuf)somVaBuf_create(NULL, 0);
        somVaBuf_add(vb, (char *)&obj, tk_pointer);
                                                            /* target for _set_msg */
        somVaBuf_add(vb, (char *)&ev, tk_pointer);
                                                            /* next argument  */
        somVaBuf_add(vb, (char *)&msg1, tk_pointer);
                                                            /* final argument */
        somVaBuf_get_valist(vb, &start_val);

        /* dispatch _set_msg on object */
        SOMObject_somDispatch(
                obj,    /* target for somDispatch */
                0,      /* says ignore dispatched method result */
                somIdFromString("_set_msg"),  /* the somId for _set_msg */
                start_val);     /* target and args for _set_msg */

        /* dispatch _get_msg on obj:      */
        /* Get a fresh copy of the va_list      */
        somVaBuf_get_valist(vb, &start_val);
        SOMObject_somDispatch(
                obj,
                (somToken *)&msg,
                               /* address to store dispatched result */
                somIdFromString("_get_msg"),
                start_val);     /* target and arguments for _get_msg */
        printf("%s\n",msg);
        somVaBuf_destroy(vb);
}
For C++
#include <somobj.h>
void f1(SOMObject obj, Environment *ev)
{
        char *msg;
        va_list start_val;
        somVaBuf vb;
        char *msg1 = "Good Morning";

        vb = (somVaBuf)somVaBuf_create(NULL, 0);
        somVaBuf_add(vb, (char *)&obj, tk_pointer);
                                         /* target for _set_msg */
        somVaBuf_add(vb, (char *)&ev, tk_pointer);
                                              /* next argument  */
        somVaBuf_add(vb, (char *)&msg1, tk_pointer);
                                              /* final argument */
        somVaBuf_get_valist(vb, &start_val);

        /* dispatch _set_msg on object */
        obj->SOMObject_somDispatch(
                0,      /* says ignore dispatched method result */
                somIdFromString("_set_msg"),  /* the somId for _set_msg */
                start_val);     /* target and args for _set_msg */

        /* dispatch _get_msg on obj:      */
        /* Get a fresh copy of the va_list      */
        somVaBuf_get_valist(vb, &start_val);
        obj->SOMObject_somDispatch(
                (somToken *)&msg,
                               /* address to store dispatched result */
                somIdFromString("_get_msg"),
                start_val);     /* target and arguments for _get_msg */
        printf("%s\n",msg);
        somVaBuf_destroy(vb);
}

As a convenience, methods whose final argument is a va_list can be invoked from C and C++ by using the short form of method invocation and specifying a variable number of arguments in place of the va_list. That is, beginning at the syntax position where the va_list argument is expected, SOMobjects interprets all subsequent arguments as being the components of the va_list. This is illustrated below, using the somDispatch method.

As an example of using the variable-argument interface to somDispatch, the following code segments illustrate how an example of attribute access (in the topic "Accessing attributes") could be recoded to operate without usage bindings for the "Hello" class. These code segments are expressed as functions that accept an argument of type SOMObject under the assumption that bindings for "Hello" are not available. (This requires usage bindings for SOMObject, which are also required for calling somDispatch.)

For C
#include <somobj.h>
void f1(SOMObject obj, Environment *ev)
{
        char *msg;
        /* dispatch _set_msg on obj:   */
        _somDispatch(
                obj,    /* the target for somDispatch */
                0,      /* says ignore the dispatched method result */
                somIdFromString("_set_msg"),  /* the somId for _set_msg */
                obj,    /* the target for _set_msg */
                ev,     /* the other arguments for _set_msg */
                "Good Morning");
        /* dispatch _get_msg on obj:     */
        _somDispatch(
                obj,
                (somToken *)&msg,
                            /* address to hold dispatched method result */
                somIdFromString("_get_msg"),
                obj,    /* the target for _get_msg */
                ev);    /* the other argument for _get_msg */
        printf("%s\n", msg);
}
For C++
#include <somobj.h>
void f1(SOMObject obj, Environment *ev)
{
        char *msg;
        /* dispatch _set_msg on obj:   */
        obj->somDispatch(
                0,      /* says ignore the dispatched method result */
                somIdFromString("_set_msg"),  /* dispatched method id */
                obj,    /* the target for _set_msg */
                ev,     /* the other arguments for _set_msg */
                "Good Morning");
        /* dispatch _get_msg on obj:     */
        obj->somDispatch(
                (somToken *)&msg,
                      /* address to store dispatched result */
                somIdFromString("_get_msg"),
                obj,
                ev);
        printf("%s\n", msg);
}

C programmers must be aware that the "short form" of the invocation macro that is used above to pass a variable number of arguments to a va_list method is only available in the absence of ambiguity. The long-form macro (which is always available) requires an explicit va_list argument. (See "Short form vs long form" under "Making typical method calls" earlier in this chapter.)

Using name-lookup method resolution

For C/C++ programmers: Offset resolution is the most efficient way to select the method procedure appropriate to a given method call. Client programs can, however, invoke a method using "name-lookup" resolution instead of offset resolution. The C and C++ bindings for method invocation use offset resolution by default, but methods defined with the namelookup SOM IDL modifier result in C bindings in which the short form invocation macro uses name-lookup resolution instead. Also, for both C and C++ bindings, a special lookup_<methodName> macro is defined.

Name-lookup resolution is appropriate in the case where a programmer knows at compile time which arguments will be expected by a method (that is, its signature), but does not know the type of the object on which the method will be invoked. For example, name-lookup resolution can be used when two different classes introduce different methods of the same name and signature, and it is not known which method should be invoked (because the type of the object is not known at compile time).

Name-lookup resolution is also used to invoke dynamic methods (that is, methods that have been added to a class's interface at run time rather than being specified in the class's IDL specification). For more information on name-lookup method resolution, see the topic "Method Resolution" in Chapter 4, "SOM IDL and the SOM Compiler."

For C: To invoke a method using name-lookup resolution, when using the C bindings for a method that has been implemented with the namelookup modifier, use either of the following macros:


_<methodName> (receiver, args)

lookup_<methodName> (receiver, args)

Thus, the short-form method invocation macro results in name-lookup resolution (rather than offset resolution), when the method has been defined as a namelookup method. (The long form of the macro for offset resolution is still available in the C usage bindings.) If the method takes a variable number of arguments, then the first form shown above is used when supplying a variable number of arguments, and the second form is used when supplying a va_list argument in place of the variable number of arguments.

For C++
To invoke a method using name-lookup resolution, when using the C++ bindings for a method that has been defined with the namelookup modifier, use either of the following macros:
lookup_<methodName> (receiver, args)

<className>_lookup_<methodName> (receiver, args)

If the method takes a variable number of arguments, then the first form shown above is used when supplying a variable number of arguments, and the second form is used when supplying a va_list argument in place of the variable number of arguments. Note that the offset-resolution forms for invoking methods using the C++ bindings are also still available, even if the method has been defined as a namelookup method.

For C/C++
To invoke a method using name-lookup resolution, when the method has not been defined as a namelookup method:
  • Use the somResolveByName procedure (described in the following section), or any of the methods somLookupMethod, somFindMethod or somFindMethodOk to obtain a pointer to the procedure that implements the desired method.
  • Then, invoke the desired method by calling that procedure, passing the method's intended receiver, the Environment pointer (if needed), the context argument (if needed), and the remainder of the method's arguments, if any.

The somLookupMethod, somFindMethod and somFindMethodOK methods are invoked on a class object (the class of the method receiver should be used), and take as an argument the somId for the desired method (which can be obtained from the method's name using the somIdFromString function). For more information on these methods, see the System Object Model Programming Reference.

Important Note: SOM provides many ways for a SOM user to acquire a pointer to a method procedure. Once this is done, it becomes the user's responsibility to make appropriate use of this procedure.

  • First, the procedure should only be used on objects for which this is appropriate-otherwise, run-time errors are likely to result.
  • Second, when the procedure is used, it is essential that the compiler be given correct information concerning the signature of the method and the linkage required by the method. (On many systems, there are different ways to pass method arguments, and linkage information tells a compiler how to pass the arguments indicated by a method's signature).

SOM method procedures on OS/2 must be called with "system" linkage. On AIX, there is only one linkage convention for procedure calls. While C and C++ provide standard ways to indicate a method signature, the way to indicate linkage information depends on the specific compiler and system. For each method declared using OIDL or IDL, the C and C++ usage bindings therefore use conditional macros and a typedef to name a type that has the correct linkage convention. This type name can then be used by programmers with access to the usage bindings for the class that introduces the method whose procedure pointer is used. The type is named somTD_<className>_<methodName>. This is illustrated in the following example, and further details are provided in the section below, titled "Obtaining a method's procedure pointer."

A name-lookup example

The following example shows the use of name-lookup by a SOM client programmer. Name-lookup resolution is appropriate when a programmer knows that an object will respond to a method of some given name, but does not know enough about the type of the object to use offset method resolution. How can this happen? It normally happens when a programmer wants to write generic code, using methods of the same name and signature that are applicable to different classes of objects, and yet these classes have no common ancestor that introduces the method. This can easily occur in single-inheritance systems (such as Smalltalk and SOM release 1) and can also happen in multiple-inheritance systems such as SOM release 2-when class hierarchies designed by different people are brought together for clients' use.

If multiple inheritance is available, it is always possible to create a common class ancestor into which methods of this kind can be migrated. A refactoring of this kind often implements a semantically pleasing generalization that unifies common features of two previously unrelated class hierarchies. This step is most practical, however, when it does not require the redefinition or recompilation of current applications that use offset resolution. SOM is unique in that it allows this.

However, such refactoring must redefine the classes that originally introduced the common methods (so the methods can be inherited from the new "unifying" class instead). A client programmer who simply wants to create an application may not control the implementations of the classes. Thus, the use of name-lookup method resolution seems the best alternative for programmers who do not want to define new classes, but simply to make use of available ones.

For example, assume the existence of two different SOM classes, "classX" and "classY", whose only common ancestor is SOMObject, and who both introduce a method named "reduce" that accepts a string as an argument and returns a long. We assume that the classes were not designed in conjunction with each other. As a result, it is unlikely that the "reduce" method was defined with a namelookup modifier. The following figure illustrates the class hierarchy for this example.

SOMPG 001.png

Following is a C++ generic procedure that uses name-lookup method resolution to invoke the "reduce" method on its argument, which may be either of type "classX" or "classY". Note that there is no reason to include classY's usage bindings, since the typedef provided for the "reduce" method procedure in "classX" is sufficient for invoking the method procedure, independently of whether the target object is of type "classX" or "classY".

   #include <classX.xh> // use classX's method proc typedef

   // this procedure can be invoked on a target of type
   // classX or classY.

   long generic_reduce1(SOMObject *target, string arg)
   {
      somTD_classX_reduce reduceProc = (somTD_classX_reduce)
      somResolveByName(target, "reduce");
      return reduceProc(target, arg);
   }

On the other hand, If the classes were designed in conjunction with each other, and the class designer felt that programmers might want to write generic code appropriate to either class of object, the namelookup modifier might have been used. This is a possibility in SOM release 2, even with multiple inheritance, but it is much more likely that the class designer would use multiple inheritance to introduce the reduce method in a separate class, and then use this other class as a parent for both classX and classY (thereby allowing the use of offset resolution).

In any case, if the "reduce" method in "classX" were defined as a namelookup method, the following code would be appropriate. Note that the name-lookup support provided by "classX" usage bindings is still appropriate for use on targets that do not have type "classX". As a result, the "reduce" method introduced by "classY" need not have been defined as a namelookup method.

   #include <classX.xh> // use classX's name-lookup support

   // this procedure can be invoked on a target of type
   // classX or classY.

   long generic_reduce2(SOMObject *target, string arg)
   {
       return lookup_reduce(target, arg);
   }

For non-C/C++ programmers: Name-lookup resolution is useful for non-C/C++ programmers when the type of an object on which a method must be invoked is not known at compile time or when method tokens cannot be directly accessed by the programmer. To invoke a method using name-lookup resolution when not using the C or C++ usage bindings, use the somResolveByName procedure to acquire a procedure pointer. How the programmer indicates the method arguments and the linkage convention in this case is compiler specific.

The somResolveByName procedure takes as arguments a pointer to the object on which the method is to be invoked and the name of the method, as a string. It returns a pointer to the method's procedure (or NULL if the method is not supported by the object). The method can then be invoked by calling the method procedure, passing the method's receiver, the Environment pointer (if necessary), the context argument (if necessary), and the rest of the method's arguments, if any. (See the section above for C programmers; the arguments to a method procedure are the same as the arguments passed to the long-form C-language method-invocation macro for that method.)

As an example of invoking methods using name-lookup resolution using the procedure somResolveByName, the following steps are used to create an instance of a SOM Class X in Smalltalk:

1.Initialize the SOM run-time environment (if it is not already initialized) using the somEnvironmentNew function.

2.If the class object for class X has not yet been created, use somResolveByName with the arguments SOMClassMgrObject (returned by somEnvironmentNew in step 1) and the string "somFindClass", to obtain a method procedure pointer for the somFindClass method. Use the method procedure for somFindClass to create the class object for class X: Call the method procedure with these four arguments: SOMClassMgrObject; the variable holding class X 's somId (the result of calling the somIdFromString function with argument "X"); and the major and minor version numbers for class X (or zero). The result is the class object for class X.

3.Use somResolveByName with arguments the class object for X (returned by somFindClass in step 2) and the string "somNew", to obtain a method procedure pointer for method somNew. (This somNew method is used to create instances of a class.)

4.Call the method procedure for somNew (using the method procedure pointer obtained in step 3) with the class object for X (returned by somFindClass in step 3) as the argument. The result is a new instance of class X. How the programmer indicates the method arguments and the linkage convention is compiler-specific.

Obtaining a method's procedure pointer

Method resolution is the process of obtaining a pointer to the procedure that implements a particular method for a particular object at run time. The method is then invoked subsequently by calling that procedure, passing the method's intended receiver, the Environment pointer (if needed), the context argument (if needed), and the method's other arguments, if any. C and C++ programmers may wish to obtain a pointer to a method's procedure for efficient repeated invocations.

Obtaining a pointer to a method's procedure is achieved in one of two ways, depending on whether the method is to be resolved using offset resolution or name-lookup resolution. Obtaining a method's procedure pointer via offset resolution is faster, but it requires that the name of the class that introduces the method and the name of the method be known at compile time. It also requires that the method be defined as part of that class's interface in the IDL specification of the class. (See the topic "Method Resolution" in Chapter 4, "SOM IDL and the SOM Compiler" for more information on offset and name-lookup method resolution.)

Offset resolution

To obtain a pointer to a procedure using offset resolution, the C/C++ usage bindings provide the SOM_Resolve and SOM_ResolveNoCheck macros. The usage bindings themselves use the first of these, SOM_Resolve, for offset-resolution method calls. The difference in the two macros is that the SOM_Resolve macro performs consistency checking on its arguments, but the macro SOM_ResolveNoCheck, which is faster, does not. Both macros require the same arguments:

SOM_Resolve(<receiver>, <className>, <methodName>) SOM_ResolveNoCheck(<receiver>, <className>, <methodName>) 

where the arguments are as follows:

receiver
The object to which the method will apply. It should be specified as an expression without side effects.
className
The name of the class that introduces the method.
methodName
The name of the desired method.

These two names (className and methodName) must be given as tokens, rather than strings or expressions. (For example, as Animal rather than "Animal".) If the symbol SOM_TestOn is defined and the symbol SOM_NoTest is not defined in the current compilation unit, then SOM_Resolve verifies that receiver is an instance of className or some class derived from className. If this test fails, an error message is output and execution is terminated.

The SOM_Resolve and SOM_ResolveNoCheck macros use the procedure somResolve to obtain the entry-point address of the desired method procedure (or raise a fatal error if methodName is not introduced by className). This result can be directly applied to the method arguments, or stored in a variable of generic procedure type (for example, somMethodPtr) and retained for later method use. This second possibility would result in a loss of information, however, for the reasons now given.

The SOM_Resolve or SOM_ResolveNoCheck macros are especially useful because they cast the method procedure they obtain to the right type to allow the C or C++ compiler to call this procedure with system linkage and with the appropriate arguments. This is why the result of SOM_Resolve is immediately useful for calling the method procedure, and why storing the result of SOM_Resolve in a variable of some "generic" procedure type results in a loss of information. The correct type information can be regained, however, because the type used by SOM_Resolve for casting the result of somResolve is available from C/C++ usage bindings using the typedef name somTD_<className>_<methodName>. This type name describes a pointer to a method procedure for methodName introduced by class className. If the final argument of the method is a va_list, then the method procedure returned by SOM_Resolve or SOM_ResolveNoCheck must be called with a va_list argument, and not a variable number of arguments.

Below is a C example of using SOM_Resolve to obtain a method procedure pointer for method "sayHello", introduced by class "Hello", and using it to invoke the method on "obj." (Assume that the only argument required by the "sayHello" method is the Environment pointer.)

  somMethodProc *p;
  SOMObject obj = HelloNew();
  p = SOM_Resolve(obj, Hello, sayHello);
  ((somTD_Hello_sayHello)p) (obj, somGetGlobalEnvironment());

SOM_Resolve and SOM_ResolveNoCheck can only be used to obtain method procedures for static methods (methods that have been declared in an IDL specification for a class) and not methods that are added to a class at run time. See the System Object Model Programming Reference for more information and examples on SOM_Resolve and SOM_ResolveNoCheck.

Name-lookup method resolution

To obtain a pointer to a method's procedure using name-lookup resolution, use the somResolveByName procedure (described in the following section), or any of the somLookupMethod, somFindMethod and somFindMethodOK methods. These methods are invoked on a class object that supports the desired method, and they take an argument specifying the a somId for the desired method (which can be obtained from the method's name using the somIdFromString function). For more information on these methods and for examples of their use, see the System Object Model Programming Reference.

Method name or signature not known at compile time

If the programmer does not know a method's name at compile time (for example, it might be specified by user input), then the method can be invoked in one of two ways, depending upon whether its signature is known:

  • Suppose the signature of the method is known at compile time (even though the method name is not). In that case, when the name of the method becomes available at run time, the somLookupMethod, somFindMethod or somFindMethodOk methods or the somResolveByName procedure can be used to obtain a pointer to the method's procedure using name-lookup method resolution, as described in the preceding topics. That method procedure can then be invoked, passing the method's intended receiver, the Environment pointer (if needed), the context argument (if needed), and the remainder of the method's arguments.
  • If the method's signature is unknown until run time, then dispatch-function resolution is indicated, as described in the next topic.
Dispatch-function method resolution

If the signature of the method is not known at compile time (and hence the method's argument list cannot be constructed until run time), then the method can be invoked at run time by (a) placing the arguments in a variable of type va_list at run time and (b) either using the somGetMethodData method followed by use of the somApply function, or by invoking the somDispatch or somClassDispatch method. Using somApply is more efficient, since this is what the somDispatch method does, but it requires two steps instead of one. In either case, the result invokes a "stub" procedure called an apply stub, whose purpose is to remove the method arguments from the va_list, and then pass them to the appropriate method procedure in the way expected by that procedure. For more information on these methods and for examples of their use, see the somApply function, and the somGetMethodData, somDispatch, and somClassDispatch methods in the System Object Model Programming Reference.

Using class objects

Using a class object encompasses three aspects: getting the class of an object, creating a new class object, or simply referring to a class object through the use of a pointer.

Getting the class of an object

To get the class that an object is an instance of, SOM provides a method called somGetClass. The somGetClass method takes an object as its only argument and returns a pointer to the class object of which it is an instance. For example, the following statements store in "myClass" the class object of which "obj" is an instance.

myClass = _somGetClass(obj); (for C)
myClass = obj->somGetClass(); (for C++)

Getting the class of an object is useful for obtaining information about the object; in some cases, such information cannot be obtained directly from the object, but only from its class. The section below entitled "Getting information about a class" describes the methods that can be invoked on a class object after it is obtained using somGetClass.

The somGetClass method can be overridden by a class to provide enhanced or alternative semantics for its objects. Because it is usually important to respect the intended semantics of a class of objects, the somGetClass method should normally be used to access the class of an object.

In a few special cases, it is not possible to make a method call on an object in order to determine its class. For such situations, SOM provides the SOM_GetClass macro. In general, the somGetClass method and the SOM_GetClass macro may have different behavior (if somGetClass has been overridden). This difference may be limited to side effects, but it is possible for their results to differ as well. The SOM_GetClass macro should only be used when absolutely necessary.

Creating a class object

A class object is created automatically the first time the <className>New macro (for C) or the new operator (C++) is invoked to create an instance of that class. In other situations, however, it may be necessary to create a class object explicitly, as this section describes.


Using <classname> Renew or somRenew

It is sometimes necessary to create a class object before creating any instances of the class. For example, creating instances using the <className> Renew macro or the somRenew method requires knowing how large the created instance will be, so that memory can be allocated for it. Getting this information requires creating the class object (see the example under "Creating instances of a class" early in this chapter). As another example, a class object must be explicitly created when a program does not use the SOM bindings for a class. Without SOM bindings for a class, its instances must be created using somNew or somRenew, and these methods require that the class object be created in advance.

Use the <className>NewClass procedure to create a class object:

  • When using the C/C++ language bindings for the class, and
  • When the name of the class is known at compile time.
Using <classname> NewClass

The <className>NewClass procedure initializes the SOM run-time environment, if necessary, creates the class object (unless it already exists), creates class objects for the ancestor classes and metaclass of the class, if necessary, and returns a pointer to the newly created class object. After its creation, the class object can be referenced in client code using the macro

_<className> (for C and C++ programs) 

or the expression

<className>ClassData.classObject (for C and C++ programs). 

The <className>NewClass procedure takes two arguments, the major version number and minor version number of the class. These numbers are checked against the version numbers built into the class library to determine if the class is compatible with the client's expectations. The class is compatible if it has the same major version number and the same or a higher minor version number. If the class is not compatible, an error is raised. Major version numbers usually only change when a significant enhancement or incompatible change is made to a class. Minor version numbers change when minor enhancements or fixes are made. Downward compatibility is usually maintained across changes in the minor version number. Zero can be used in place of version numbers to bypass version number checking.

When using SOM bindings for a class, these bindings define constants representing the major and minor version numbers of the class at the time the bindings were generated. These constants are named <className>_MajorVersion and <className>_MinorVersion. For example, the following procedure call:

  AnimalNewClass(Animal_MajorVersion, Animal_MinorVersion);

creates the class object for class "Animal". Thereafter, _Animal can be used to reference the "Animal" class object.

The preceding technique for checking version numbers is not failsafe. For performance reasons, the version numbers for a class are only checked when the class object is created, and not when the class object or its instances are used. Thus, run-time errors may result when usage bindings for a particular version of a class are used to invoke methods on objects created by an earlier version of the class.

Using somFindClass or somFindClsInFile

To create a class object when not using the C/C++ language bindings for the class, or when the class name is not known at compile time:

  • First, initialize the SOM run-time environment by calling the somEnvironmentNew function (unless it is known that the SOM run-time environment has already been initialized).
  • Then, use the somFindClass or somFindClsInFile method to create the class object. (The class must already be defined in a dynamically linked library, or DLL.)

The somEnvironmentNew function initializes the SOM run-time environment. That is, it creates the four primitive SOM objects (SOMClass, SOMObject, SOMClassMgr, and the SOMClassMgrObject), and it initializes SOM global variables. The function takes no arguments and returns a pointer to the SOMClassMgrObject.


Note
Although somEnvironmentNew must be called before using other SOM functions and methods, explicitly calling somEnvironmentNew is usually not necessary when using the C/C++ bindings, because the macros for <className>NewClass, <className>New , and <className>Renew call it automatically, as does the new operator for C++. Calling somEnvironmentNew repeatedly does no harm.


After the SOM run-time environment has been initialized, the methods somFindClass and somFindClsInFile can be used to create a class object. These methods must be invoked on the class manager, which is pointed to by the global variable SOMClassMgrObject. (It is also returned as the result of somEnvironmentNew.)

The somFindClass method takes the following arguments:

classId
A somId identifying the name of the class to be created. The somIdFromString function returns a classId given the name of the class.
major version number
The expected major version number of the class.
minor version number
The expected minor version number of the class.

The version numbers are checked against the version numbers built into the class library to determine if the class is compatible with the client's expectations.

The somFindClass method dynamically loads the DLL containing the class's implementation, if needed, creates the class object (unless it already exists) by invoking its <className>NewClass procedure, and returns a pointer to it. If the class could not be created, somFindClass returns NULL. For example, the following C code fragment creates the class "Hello" and stores a pointer to it in "myClass":

   SOMClassMgr cm = somEnvironmentNew();
   somId classId = somIdFromString("Hello");
   SOMClass myClass = _somFindClass(SOMClassMgrObject, classId
                          Hello_MajorVersion, Hello_MinorVersion);
   ...
   SOMFree(classId);

The somFindClass method uses somLocateClassFile to get the name of the library file containing the class. If the class was defined with a "dllname" class modifier, then somLocateClassFile returns that file name; otherwise, it assumes that the class name is the name of the library file. The somFindClsInFile method is similar to somFindClass, except that it takes an additional (final) argument-the name of the library file containing the class. The somFindClsInFile method is useful when a class is packaged in a DLL along with other classes and the "dllname" class modifier has not been given in the class's IDL specification.

Warning
On AIX, the somFindClass and somFindClsInFile methods should not be used to create a class whose implementation is statically linked with the client program. Instead, the class object should be created using the <className>NewClass procedure provided by the class's .h/.xh header file. Static linkage is not created by including usage bindings in a program, but by use of the offset-resolution method-invocation macros.
Invoking methods without corresponding class usage bindings

This topic builds on the preceding discussion, and illustrates how a client program can apply dynamic SOM mechanisms to utilize classes and objects for which specific usage bindings are not available. This process can be applied when a class implementor did not ship the C/C++ language bindings. Furthermore, the process allows more programming flexibility, because it is not necessary to know the class and method names at compile time in order to access them at run time. (At run time, however, you must be able to provide the method arguments, either explicitly or via a va_list, and provide a generalized way to handle return values.) As an example application, a programmer might create an online class viewer that can access many classes without requiring usage bindings for all those classes, and the person using the viewer can select class names at run time.

As another aspect of flexibility, a code sequence similar to the following C++ example could be re-used to access any class or method. After getting the somId for a class name, the example uses the somFindClass method to create the class object. The somNew method is then invoked to create an instance of the specified class, and the somDispatch method is used to invoke a method on the object.

#include <stdio.h>
#include <somcls.xh>

int main()
{
        SOMClass *classobj;
        somId tempId;
        somId methId;
        SOMObject *s2;
        Environment * main_ev = somGetGlobalEnvironment();

        tempId = SOM_IdFromString("myClassName");
        classobj = SOMClassMgrObject->somFindClass(tempId,0,0);
        SOMFree(tempId);

        if (NULL==classobj)
        {
                printf("somFindClass could not find the selected class\n");
        }
        else
        {
                s2 = (SOMObject *) (classobj->somNew());
                methId = somIdFromString("sayHello");
                if (s2->somDispatch((somToken *) 0, methId, s2, ev))

                        printf("Method successfully called.\n");
        }

        return 0;
}

Referring to class objects

Saving a pointer as the class object is created
The <className>NewClass macro and the somFindClass method, used to create class objects, both return a pointer to the newly created class object. Hence, one way to obtain a pointer to a class object is to save the value returned by <className>NewClass or somFindClass when the class object is created.
Getting a pointer after the class object is created
After a class object has been created, client programs can also get a pointer to the class object from the class name. When the class name is known at compile time and the client program is using the C or C++ language bindings, the macro
_<className> 

can be used to refer to the class object for <className>. Also, when the class name is known at compile time and the client program is using the C or C++ language bindings, the expression

<className>ClassData.classObject 

refers to the class object for <className>. For example, _Hello refers to the class object for class "Hello" in C or C++ programs, and HelloClassData.classObject refers to the class object for class "Hello." in C or C++ programs.

Getting a pointer to the class object from an instance
If any instances of the class are known to exist, a pointer to the class object can also be obtained by invoking the somGetClass method on such an instance. (See "Getting the class of an object," above.)
Getting a pointer in other situations
If the class name is not known until run time, or if the client program is not using the C or C++ language bindings, and no instances of the class are known to exist, then the somClassFromId method can be used to obtain a pointer to a class object after the class object has been created. The somClassFromId method should be invoked on the class manager, which is pointed to by the global variable SOMClassMgrObject. The only argument to the method is a somId for the class name, which can be obtained using the somIdFromString function. The method somClassFromId returns a pointer to the class object of the specified class. For example, the following C code stores in "myClass" a pointer to the class object for class "Hello" (or NULL, if the class cannot be located):
  SOMClassMgr cm = somEnvironmentNew();
  somId classId = somIdFromString("Hello");
  SOMClass myClass =_somClassFromId(SOMClassMgrObject,classId
                         Hello_MajorVersion, Hello_MinorVersion);
  SOMFree(classId);

Compiling and linking

This section describes how to compile and link C and C++ client programs. Compiling and linking a client program with a SOM class is done in one of two ways, depending on how the class is packaged.

Note
If you are building an application that uses a combination of C and C++ compiled object modules, then the C++ linker must be used to link them.

If the class is not packaged as a library (that is, the client program has the implementation source code for the class, as in the examples given in the SOM IDL tutorial), then the client program can be compiled together with the class implementation file as follows. (This assumes that the client program and the class are both implemented in the same language, C or C++. If this is not the case, then each module must be compiled separately to produce an object file and the resulting object files linked together to form an executable.)

In the following examples, the environment variable SOMBASE refers to the directory in which SOM has been installed. The examples also assume that the header files and the import library for the "Hello" class reside in the "include" and "lib" directories where SOM has been installed. If this is not the case, additional path information should be supplied for these files. For client program "main" and class "Hello":

Under AIX, for C programmers:

  > xlc -I. -I$SOMBASE/include main.c hello.c -L$SOMBASE/lib -lsomtk \
        -o main


Under AIX, for C++ programmers:

  > xlC -I. -I$SOMBASE/include main.C hello.C -L$SOMBASE/lib -lsomtk \
        -o main


Under OS/2, for C programmers:

  > set LIB=%SOMBASE%\lib;%LIB%
  > icc -I. -I%SOMBASE%\include main.c hello.c somtk.lib


Under OS/2, for C++ programmers:

  > set LIB=%SOMBASE%\lib;%LIB%
  > icc -I. -I%SOMBASE%\include main.cpp hello.cpp somtk.lib


If the class is packaged as a class library, then the client program, "main", is compiled as above, except that the class implementation file is not part of the compilation. Instead, the "import library" provided with the class library is used to resolve the symbolic references that appear in "main". For example, to compile the C client program "main.c" that uses class "Hello":

Under AIX:

  > xlc -I. -I$SOMBASE/include main.c -lc -L$SOMBASE/lib -lsomtk \
        -lhello -o main


Under OS/2:

  > set LIB=%SOMBASE%\lib;%LIB%
  > icc -I. -I%SOMBASE%\include main.c somtk.lib hello.lib

Language-neutral Methods and Functions

This section describes methods, functions, and macros that client programs can use regardless of the programming language in which they are written. In other words, these functions and methods are not part of the C or C++ bindings.

Generating output

The following functions and methods are used to generate output, including descriptions of SOM objects. They all produce their output using the character-output procedure held by the global variable SOMOutCharRoutine. The default procedure for character output simply writes the character to stdout, but it can be replaced to change the output destination of the methods and functions below. (See Chapter 5 for more information on customizing SOM.)

somDumpSelf
(method) writes a detailed description of an object, including its class, its location, and its instance data. The receiver of the method is the object to be dumped. An additional argument is the "nesting level" for the description. [All lines in the description will be indented by (2 * level) spaces.]
somPrintSelf
(method) Writes a brief description of an object, including its class and location in memory. The receiver of the method is the object to be printed.
somPrintf
(function) SOM's version of the C "printf" function. It generates character stream output via SOMOutCharRoutine. It has the same interface as the C "printf" function.
somVprintf
(function) Represents the "vprint" form of somPrintf. Its arguments are a formatting string and a va_list holding the remaining arguments.
somPrefixLevel
(function) Generates (via somPrintf) spaces to prefix a line at the indicated level. The return type is void. The argument is an integer specifying the level. The number of spaces generated is (2 * level).
somLPrintf
(function) Combines somPrefixLevel and somPrintf. The first argument is the level of the description (as for somPrefixLevel) and the remaining arguments are as for somPrintf (or for the C "printf" function).

See the System Object Model Programming Reference for more information on a specific function or method.

Getting information about a class

The following methods are used to obtain information about a class or to locate a particular class object:

somCheckVersion
Checks a class for compatibility with the specified major and minor version numbers. The receiver of the method is the SOM class about which information is needed. Additional arguments are values of the major and minor version numbers. The method returns TRUE if the class is compatible, or FALSE otherwise.
somClassFromId
Finds the class object of an existing class when given its somId, but without loading the class. The receiver of the method is the class manager (pointed to by the global variable SOMClassMgrObject). The additional argument is the class's somId. The method returns a pointer to the class (or NULL if the class does not exist).
somDescendedFrom
Tests whether one class is derived from another. The receiver of the method is the class to be tested, and the potential ancestor class is the argument. The method returns TRUE if the relationships exists, or FALSE otherwise.
somFindClass
Finds or creates the class object for a class, given the class's somId and its major and minor version numbers. The receiver of the method is the class manager (pointed to by the global variable SOMClassMgrObject). Additional arguments are the class's somId and the major and minor version numbers. The method returns a pointer to the class object, or NULL if the class could not be created.
somFindClsInFile
Finds or creates the class object for a class. This method is similar to somFindClass, except the user also provides the name of a file to be used for dynamic loading, if needed. The receiver of the method is the class manager (pointed to by the global variable SOMClassMgrObject). Additional arguments are the class's somId, the major and minor version numbers, and the file name. The method returns a pointer to the class object, or NULL if the class could not be created.
somGetInstancePartSize
Obtains the size of the instance variables introduced by a class. The receiver of the method is the class object. The method returns the amount of space, in bytes, needed for the instance variables.
somGetInstanceSize
Obtains the total size requirements for an instance of a class. The receiver of the method is the class object. The method returns the amount of space, in bytes, required for the instance variables introduced by the class itself and by all of its ancestor classes.
somGetName
Obtains the name of a class. The receiver of the method is the class object. The method returns the class name.
somGetNumMethods
Obtains the number of methods available for a class. The receiver of the method is the class object. The method returns the total number of currently available methods (static or otherwise, including inherited methods).
somGetNumStaticMethods
Obtains the number of static methods available for a class. (A static method is one declared in the class's interface specification [.idl] file.) The receiver of the method is the class object. The method returns the total number of available static methods, including inherited ones.
somGetParents
Obtains a sequence of the parent (base) classes of a specified class. The receiver of the method is the class object. The method returns a pointer to a linked list of the parent (base) classes (unless the receiver is SOMObject, for which it returns NULL).
somGetVersionNumbers
Obtains the major and minor version numbers of a class. The return type is void, and the two arguments are pointers to locations in memory where the method can store the major and minor version numbers (of type long).
somSupportsMethod
Indicates whether instances of a given class support a given method. The receiver of the somSupportsMethod method is the class object. The argument is the somId for the method in question. The somSupportsMethod method returns TRUE if the method is supported, or FALSE otherwise.

See the System Object Model Programming Reference for more information on a specific method.

Getting information about an object

The following methods and functions are used to obtain information about an object (instance) or to determine whether a variable holds a valid SOM object.

Methods

somGetClass 
Gets the class object of a specified object. The receiver of the method is the object whose class is desired. The method returns a pointer to the object's corresponding class object.
somGetClassName 
Obtains the class name of an object. The receiver of the method is the object whose class name is desired. The method returns a pointer to the name of the class of which the specified object is an instance.
somGetSize 
Obtains the size of an object. The receiver of the method is the object. The method returns the amount of contiguous space, in bytes, that is needed to hold the object itself (not including any additional space that the object may be using or managing outside of this area).
somIsA 
Determines whether an object is an instance of a given class or of one of its descendant classes. The receiver of the method is the object to be tested. An additional argument is the name of the class to which the object will be compared. This method returns TRUE if the object is an instance of the specified class or if (unlike somIsInstanceOf) it is an instance of any descendant class of the given class; otherwise, the method returns FALSE.
somIsInstanceOf 
Determines whether an object is an instance of a specific class (but not of any descendant class).The receiver of the method is the object. The argument is the name of the class to which the object will be compared. The method returns TRUE if the object is an instance of the specified class, or FALSE otherwise.
somRespondsTo 
Determines whether an object supports a given method. The receiver of the method is the object. The argument is the somId for the method in question. (A somId can be obtained from a string by using the somIdFromString function.) The somRespondsTo method returns TRUE if the object supports the method, or FALSE otherwise.

Functions

somIsObj
Takes as its only argument an address (which may not be valid). The function returns TRUE (1) if the address contains a valid SOM object, or FALSE (0) otherwise. This function is designed to be failsafe.

See the System Object Model Programming Reference for more information on a specific method or function.

Debugging

The following macros are used to conditionally generate output for debugging. All output generated by these macros is written using the replaceable character-output procedure pointed to by the global variable SOMOutCharRoutine. The default procedure simply writes the character to stdout, but it can be replaced to change the output destination of the methods and functions below. (See Chapter 5 for more information on customizing SOM.)

Debugging output is produced or suppressed based on the settings of three global variables, SOM_TraceLevel, SOM_WarnLevel, and SOM_AssertLevel:

  • SOM_TraceLevel controls the behavior of the <className>MethodDebug macro;
  • SOM_WarnLevel controls the behavior of the macros SOM_WarnMsg, SOM_TestC, and SOM_Expect; and
  • SOM_AssertLevel controls the behavior of the SOM_Assert macro.

Available macros for generating debugging output are as follows:

<className>MethodDebug
(macro for C and C++ programmers using the SOM language bindings for <className>)

The arguments to this macro are a class name and a method name. If the SOM_TraceLevel global variable has a nonzero value, the <className>MethodDebug macro produces a message each time the specified method (as defined by the specified class) is executed. This macro is typically used within the procedure that implements the specified method. (The SOM Compiler automatically generates calls to the <className>MethodDebug macro within the implementation template files it produces.) u To suppress method tracing for all methods of a class, put the following statement in the implementation file after including the header file for the class:

#define <className>MethodDebug(c,m) \
                   SOM_NoTrace(c,m)


This can yield a slight performance improvement. The SOMMTraced metaclass, discussed below, provides a more extensive tracing facility that includes method parameters and returned values.

SOM_TestC
The SOM_TestC macro takes as an argument a boolean expression. If the boolean expression is TRUE (nonzero) and SOM_AssertLevel is greater than zero, then an informational message is output. If the expression is FALSE (zero) and SOM_WarnLevel is greater than zero, a warning message is produced.
SOM_WarnMsg
The SOM_WarnMsg macro takes as an argument a character string. If the value of SOM_WarnLevel is greater than zero, the specified message is output.
SOM_Assert
The SOM_Assert macro takes as arguments a boolean expression and an error code (an integer). If the boolean expression is TRUE (nonzero) and SOM_AssertLevel is greater than zero, then an informational message is output. If the expression is FALSE (zero), and the error code indicates a warning-level error and SOM_WarnLevel is greater than zero, then a warning message is output. If the expression is FALSE and the error code indicates a fatal error, then an error message is produced and the process is terminated.
SOM_Expect
The SOM_Expect macro takes as an argument a boolean expression. If the boolean expression is FALSE (zero) and SOM_WarnLevel is set to be greater than zero, then a warning message is output. If condition is TRUE and SOM_AssertLevel is set to be greater than zero, then an informational message is output.

See the System Object Model Programming Reference for more information on a specific macro.

The somDumpSelf and somPrintSelf methods can be useful in testing and debugging. The somPrintSelf method produces a brief description of an object, and the somDumpSelf method produces a more detailed description. See the System Object Model Programming Reference for more information.

Checking the validity of method calls

The C and C++ language bindings include code to check the validity of method calls at run time. If a validity check fails, the SOM_Error macro ends the process. (SOM_Error is described below.) To enable method-call validity checking, place the following directive in the client program prior to any #include directives for SOM header files:

#define SOM_TestOn

Alternatively, the -DSOM_TestOn option can be used when compiling the client program to enable method-call validity checking.

Exceptions and error handling

In the classes provided in the SOM run-time library (that is, SOMClass, SOMObject, and SOMClassMgr), error handling is performed by a user-replaceable procedure, pointed to by the global variable SOMError, that produces an error message and an error code and, if appropriate ate, ends the process where the error occurred. (Chapter 5 describes how to customize the error handling procedure.)

Each error is assigned a unique integer error code. Errors are grouped into three categories, based on the last digit of the error code:

SOM_Ignore
This category of error represents an informational event. The event is considered normal and can be ignored or logged at the user's discretion. Error codes having a last digit of 2 belong to this category.
SOM_Warn
This category of error represents an unusual condition that is not a normal event, but is not severe enough to require program termination. Error codes having a last digit of 1 belong to this category.
SOM_Fatal
This category of error represents a condition that should not occur or that would result in loss of system integrity if processing were allowed to continue. In the default error handling procedure, these errors cause the termination of the process in which they occur. Error codes having a last digit of 9 belong to this category.

The various codes for all errors detected by SOM are listed in Appendix A, "Customer Support and Error Codes."

When errors are encountered in client programs or user defined-classes, the following two macros can be used to invoke the error-handling procedure:

SOM_Error >The SOM_Error macro takes an error code as its only argument and invokes the SOM error handling procedure (pointed to by the global variable SOMError) to handle the error. The default error handling procedure prints a message that includes the error code, the name of the source file, and the line number where the macro was invoked. If the last digit of the error code indicates a serious error (of category SOM_Fatal), the process causing the error is terminated. (Chapter 5 describes how to customize the error handling procedure.)
SOM_Test
The SOM_Test macro takes a boolean expression as an argument. If the expression is TRUE (nonzero) and the SOM_AssertLevel is greater than zero, then an informational message is output. If the expression is FALSE (zero), an error message is produced and the program is terminated.

See the System Object Model Programming Reference for more information on a specific macro.

Other classes provided by the SOMobjects Toolkit (including those in the Persistence, Replication, DSOM, and Interface Repository frameworks, and the utility classes and metaclasses) handle errors differently. Rather than invoking SOMError with an error code, their methods return exceptions via the (Environment *) inout parameter required by these methods. The following sections describe the exception declarations, the standard exceptions, and how to set and get exception information in an Environment structure.

Exception declarations

Standard exceptions

The Environment

Setting an exception value

Getting an exception value

Example

Memory management

SOM manipulations using somId's