Difference between revisions of "Tutorial for Implementing SOM Classes"

From EDM2
Jump to: navigation, search
(Basic Steps for Implementing SOM Classes)
m
Line 2: Line 2:
 
{{IBM-Reprint}}
 
{{IBM-Reprint}}
  
This tutorial contains five examples showing how SOM classes can be implemented to achieve various functionality. Obviously, for any person who wishes to become a class implementor, this tutorial is essential. However, even for those programmers who expect only to use SOM classes that were implemented by others, the tutorial is also necessary, as it presents several concepts that will help clarify the process of using SOM classes.  
+
This tutorial contains five examples showing how SOM classes can be implemented to achieve various functionality. Obviously, for any person who wishes to become a class implementor, this tutorial is essential. However, even for those programmers who expect only to use SOM classes that were implemented by others, the tutorial is also necessary, as it presents several concepts that will help clarify the process of using SOM classes.
  
 
==Basic Concepts of the System Object Model (SOM)==
 
==Basic Concepts of the System Object Model (SOM)==
 
 
The System Object Model (SOM), provided by the SOMobjects Developer Toolkit, is a set of libraries, utilities, and conventions used to create binary class libraries that can be used by application programs written in various object-oriented programming languages, such as C++ and Smalltalk, or in traditional procedural languages, such as C and Cobol. The following paragraphs introduce some of the basic terminology used when creating classes in SOM:
 
The System Object Model (SOM), provided by the SOMobjects Developer Toolkit, is a set of libraries, utilities, and conventions used to create binary class libraries that can be used by application programs written in various object-oriented programming languages, such as C++ and Smalltalk, or in traditional procedural languages, such as C and Cobol. The following paragraphs introduce some of the basic terminology used when creating classes in SOM:
 
 
* An object is an OOP entity that has behavior (its methods or operations) and state (its data values). In SOM, an object is a run-time entity with a specific set of methods and instance variables. The methods are used by a client programmer to make the object exhibit behavior (that is, to do something), and the instance variables are used by the object to store its state. (The state of an object can change over time, which allows the object's behavior to change.) When a method is invoked on an object, the object is said to be the receiver or target of the method call.
 
* An object is an OOP entity that has behavior (its methods or operations) and state (its data values). In SOM, an object is a run-time entity with a specific set of methods and instance variables. The methods are used by a client programmer to make the object exhibit behavior (that is, to do something), and the instance variables are used by the object to store its state. (The state of an object can change over time, which allows the object's behavior to change.) When a method is invoked on an object, the object is said to be the receiver or target of the method call.
 
 
* An object's implementation is determined by the procedures that execute its methods, and by the type and layout of its instance variables. The procedures and instance variables that implement an object are normally encapsulated (hidden from the caller), so a program can use the object's methods without knowing anything about how those methods are implemented. Instead, a user is given access to the object's methods through its interface (a description of the methods in terms of the data elements required as input and the type of value each method returns).
 
* An object's implementation is determined by the procedures that execute its methods, and by the type and layout of its instance variables. The procedures and instance variables that implement an object are normally encapsulated (hidden from the caller), so a program can use the object's methods without knowing anything about how those methods are implemented. Instead, a user is given access to the object's methods through its interface (a description of the methods in terms of the data elements required as input and the type of value each method returns).
 
 
* An interface through which an object may be manipulated is represented by an object type. That is, by declaring a type for an object variable, a programmer specifies the interface that is intended to be used to access that object. SOM IDL (the SOM Interface Definition Language) is used to define object interfaces. The interface names used in these IDL definitions are also the type names used by programmers when typing SOM object variables.
 
* An interface through which an object may be manipulated is represented by an object type. That is, by declaring a type for an object variable, a programmer specifies the interface that is intended to be used to access that object. SOM IDL (the SOM Interface Definition Language) is used to define object interfaces. The interface names used in these IDL definitions are also the type names used by programmers when typing SOM object variables.
 
 
* In SOM, as in most approaches to object-oriented programming, a class defines the implementation of objects. That is, the implementation of any SOM object (as well as its interface) is defined by some specific SOM class. A class definition begins with an IDL specification of the interface to its objects, and the name of this interface is used as the class name as well. Each object of a given class may also be called an instance of the class, or an instantiation of the class.
 
* In SOM, as in most approaches to object-oriented programming, a class defines the implementation of objects. That is, the implementation of any SOM object (as well as its interface) is defined by some specific SOM class. A class definition begins with an IDL specification of the interface to its objects, and the name of this interface is used as the class name as well. Each object of a given class may also be called an instance of the class, or an instantiation of the class.
 
 
* Inheritance, or class derivation, is a technique for developing new classes from existing classes. The original class is called the base class, or the parent class, or sometimes the direct ancestor class. The derived class is called a child class or a subclass. The primary advantage of inheritance is that a derived class inherits all of its parent's methods and instance variables. Also through inheritance, a new class can override (or redefine) methods of its parent, in order to provide enhanced functionality as needed. In addition, a derived class can introduce new methods of its own. If a class results from several generations of successive class derivation, that class "knows" all of its ancestors's methods (whether overridden or not), and an object (or instance) of that class can execute any of those methods.
 
* Inheritance, or class derivation, is a technique for developing new classes from existing classes. The original class is called the base class, or the parent class, or sometimes the direct ancestor class. The derived class is called a child class or a subclass. The primary advantage of inheritance is that a derived class inherits all of its parent's methods and instance variables. Also through inheritance, a new class can override (or redefine) methods of its parent, in order to provide enhanced functionality as needed. In addition, a derived class can introduce new methods of its own. If a class results from several generations of successive class derivation, that class "knows" all of its ancestors's methods (whether overridden or not), and an object (or instance) of that class can execute any of those methods.
 
 
* SOM classes can also take advantage of multiple inheritance, which means that a new class is jointly derived from two or more parent classes. In this case, the derived class inherits methods from all of its parents (and all of its ancestors), giving it greatly expanded capabilities. In the event that different parents have methods of the same name that execute differently, SOM provides ways for avoiding conflicts.
 
* SOM classes can also take advantage of multiple inheritance, which means that a new class is jointly derived from two or more parent classes. In this case, the derived class inherits methods from all of its parents (and all of its ancestors), giving it greatly expanded capabilities. In the event that different parents have methods of the same name that execute differently, SOM provides ways for avoiding conflicts.
 
 
* In the SOM run time, classes are themselves objects. That is, classes have their own methods and interfaces, and are themselves defined by other classes. For this reason, a class is often called a class object. Likewise, the terms class methods and class variables are used to distinguish between the methods/variables of a class object vs. those of its instances. (Note that the type of an object is not the same as the type of its class, which as a "class object" has its own type.)
 
* In the SOM run time, classes are themselves objects. That is, classes have their own methods and interfaces, and are themselves defined by other classes. For this reason, a class is often called a class object. Likewise, the terms class methods and class variables are used to distinguish between the methods/variables of a class object vs. those of its instances. (Note that the type of an object is not the same as the type of its class, which as a "class object" has its own type.)
 
 
* A class that defines the implementation of class objects is called a metaclass. Just as an instance of a class is an object, so an instance of a metaclass is a class object. Moreover, just as an ordinary class defines methods that its objects respond to, so a metaclass defines methods that a class object responds to. For example, such methods might involve operations that execute when a class (that is, a class object) is creating an instance of itself (an object). Just as classes are derived from parent classes, so metaclasses can be derived from parent metaclasses, in order to define new functionality for class objects.
 
* A class that defines the implementation of class objects is called a metaclass. Just as an instance of a class is an object, so an instance of a metaclass is a class object. Moreover, just as an ordinary class defines methods that its objects respond to, so a metaclass defines methods that a class object responds to. For example, such methods might involve operations that execute when a class (that is, a class object) is creating an instance of itself (an object). Just as classes are derived from parent classes, so metaclasses can be derived from parent metaclasses, in order to define new functionality for class objects.
 
 
* The SOM system contains three primitive classes that are the basis for all subsequent classes:
 
* The SOM system contains three primitive classes that are the basis for all subsequent classes:
 
+
; [[SOMObject]] : the root ancestor class for all SOM classes,
; [[SOMObject]] : the root ancestor class for all SOM classes,  
+
; [[SOMClass]] : the root ancestor class for all SOM metaclasses, and
 
+
; [[SOMClassMgr]] : the class of the SOMClassMgrObject, an object created automatically during SOM initialization, to maintain a registry of existing classes and to assist in dynamic class loading/unloading. SOMClass is defined as a subclass (or child) of SOMObject and inherits all generic object methods; this is why instances of a metaclass are class objects (rather than simply classes) in the SOM run time.
; [[SOMClass]] : the root ancestor class for all SOM metaclasses, and  
+
 
+
; [[SOMClassMgr]] : the class of the SOMClassMgrObject, an object created automatically during SOM initialization, to maintain a registry of existing classes and to assist in dynamic class loading/unloading. SOMClass is defined as a subclass (or child) of SOMObject and inherits all generic object methods; this is why instances of a metaclass are class objects (rather than simply classes) in the SOM run time.  
+
  
 
SOM classes are designed to be language neutral. That is, SOM classes can be implemented in one programming language and used in programs of another language. To achieve language neutrality, the interface for a class of objects must be defined separately from its implementation. That is, defining interface and implementation requires two completely separate steps (plus an intervening compile), as follows:
 
SOM classes are designed to be language neutral. That is, SOM classes can be implemented in one programming language and used in programs of another language. To achieve language neutrality, the interface for a class of objects must be defined separately from its implementation. That is, defining interface and implementation requires two completely separate steps (plus an intervening compile), as follows:
 
 
* An interface is the information that a program must know in order to use an object of a particular class. This interface is described in an interface definition (which is also the class definition), using a formal language whose syntax is independent of the programming language used to implement the class's methods. For SOM classes, this is the SOM Interface Definition Language (SOM IDL). The interface is defined in a file known as the IDL source file (or, using its extension, this is often called the .idl file).
 
* An interface is the information that a program must know in order to use an object of a particular class. This interface is described in an interface definition (which is also the class definition), using a formal language whose syntax is independent of the programming language used to implement the class's methods. For SOM classes, this is the SOM Interface Definition Language (SOM IDL). The interface is defined in a file known as the IDL source file (or, using its extension, this is often called the .idl file).
 
 
* An interface definition is specified within the interface declaration (or interface statement) of the .idl file, which includes:
 
* An interface definition is specified within the interface declaration (or interface statement) of the .idl file, which includes:
 
+
; (a) : the interface name (or class name) and the name(s) of the class's parent(s), and
; (a) : the interface name (or class name) and the name(s) of the class's parent(s), and  
+
; (b) : the names of the class's attributes and the signatures of its new methods. (Recall that the complete set of available methods also includes all inherited methods.) Each method signature includes the method name, and the type and order of its arguments, as well as the type of its return value (if any). Attributes are instance variables for which "set" and "get" methods will automatically be defined, for use by the application program. (By contrast, instance variables that are not attributes are hidden from the user.)
 
+
; (b) : the names of the class's attributes and the signatures of its new methods. (Recall that the complete set of available methods also includes all inherited methods.) Each method signature includes the method name, and the type and order of its arguments, as well as the type of its return value (if any). Attributes are instance variables for which "set" and "get" methods will automatically be defined, for use by the application program. (By contrast, instance variables that are not attributes are hidden from the user.)  
+
  
 
Once the IDL source file is complete, the SOM Compiler is used to analyze the .idl file and create the implementation template file, within which the class implementation will be defined. Before issuing the SOM Compiler command, sc, the class implementor can set an environment variable that determines which emitters (output-generating programs) the SOM Compiler will call and, consequently, which programming language and operating system the resulting binding files will relate to. (Alternatively, this emitter information can be placed on the command line for sc.) In addition to the implementation template file itself, the binding files include two language-specific header files that will be #included in the implementation template file and in application program files. The header files define many useful SOM macros, functions, and procedures that can be invoked from the files that include the header files.
 
Once the IDL source file is complete, the SOM Compiler is used to analyze the .idl file and create the implementation template file, within which the class implementation will be defined. Before issuing the SOM Compiler command, sc, the class implementor can set an environment variable that determines which emitters (output-generating programs) the SOM Compiler will call and, consequently, which programming language and operating system the resulting binding files will relate to. (Alternatively, this emitter information can be placed on the command line for sc.) In addition to the implementation template file itself, the binding files include two language-specific header files that will be #included in the implementation template file and in application program files. The header files define many useful SOM macros, functions, and procedures that can be invoked from the files that include the header files.
Line 51: Line 34:
  
 
===Development of the Tutorial examples===
 
===Development of the Tutorial examples===
 
 
; Example 1 - Implementing a simple class with one method : Prints a default message when the "sayHello" method is invoked on an object of the "Hello" class.
 
; Example 1 - Implementing a simple class with one method : Prints a default message when the "sayHello" method is invoked on an object of the "Hello" class.
 
 
; Example 2 -  Adding an attribute to the Hello class :  Defines a "msg" attribute for the "sayHello" method to use. The client program "sets" a message; then the "sayHello" method "gets" the message and prints it. (There is no defined message when an object of the "Hello" class is first created.)
 
; Example 2 -  Adding an attribute to the Hello class :  Defines a "msg" attribute for the "sayHello" method to use. The client program "sets" a message; then the "sayHello" method "gets" the message and prints it. (There is no defined message when an object of the "Hello" class is first created.)
 
 
; Example 3 - Overriding an inherited method : Overrides the SOMobjects method somPrintSelf so that invoking this method on an object of the "Hello" class will not only display the class name and the object's location, but will also include the object's message attribute.
 
; Example 3 - Overriding an inherited method : Overrides the SOMobjects method somPrintSelf so that invoking this method on an object of the "Hello" class will not only display the class name and the object's location, but will also include the object's message attribute.
 
 
; Example 4  - Initializing a SOM object : Overrides the default initialization method, somDefaultInit, to illustrate how an object's instance variables can be initialized when the object is created.
 
; Example 4  - Initializing a SOM object : Overrides the default initialization method, somDefaultInit, to illustrate how an object's instance variables can be initialized when the object is created.
 
+
; Example 5 -  Using multiple inheritance : Extends the "Hello" class to provide it with multiple inheritance (from the "Disk;" and "Printer" classes.) The "Hello" interface defines an enum and an "output" attribute that takes its value from the enum (either "screen," "printer," or "disk"). The client program "sets" the form of "output" before invoking the "sayHello" method to send a "msg"(defined as in Example 4).
; Example 5 -  Using multiple inheritance : Extends the "Hello" class to provide it with multiple inheritance (from the "Disk;" and "Printer" classes.) The "Hello" interface defines an enum and an "output" attribute that takes its value from the enum (either "screen," "printer," or "disk"). The client program "sets" the form of "output" before invoking the "sayHello" method to send a "msg"(defined as in Example 4).  
+
  
 
==Basic Steps for Implementing SOM Classes==
 
==Basic Steps for Implementing SOM Classes==
 
Implementing and using SOM classes in C or C++ involves the following steps, which are explicitly illustrated in the examples of this tutorial:
 
Implementing and using SOM classes in C or C++ involves the following steps, which are explicitly illustrated in the examples of this tutorial:
 
 
* Define the interface to objects of the new class (that is, the interface declaration), by creating a .idl file.
 
* Define the interface to objects of the new class (that is, the interface declaration), by creating a .idl file.
 
* Run the SOM Compiler on the .idl file by issuing the sc command to produce the following binding files:
 
* Run the SOM Compiler on the .idl file by issuing the sc command to produce the following binding files:
Line 121: Line 98:
  
 
2.Run the SOM Compiler to produce binding files and an implementation template (that is, issue the sc command):  
 
2.Run the SOM Compiler to produce binding files and an implementation template (that is, issue the sc command):  
 
+
> sc -s"c;h;ih" hello.idl      (for C bindings on AIX or OS/2)
> sc -s"c;h;ih" hello.idl      (for C bindings on AIX or OS/2)
+
> sc -s"xc;xh;xih" hello.idl  (for C++ bindings on AIX or OS/2)
> sc -s"xc;xh;xih" hello.idl  (for C++ bindings on AIX or OS/2)
+
When set to generate C binding files, the SOM Compiler generates the following implementation template file, named "hello.c". The template implementation file contains stub procedures for each new method. These are procedures whose bodies are largely vacuous, to be filled in by the implementor. (Unimportant details have been removed for this tutorial.)
 
+
 
+
When set to generate C binding files, the SOM Compiler generates the following implementation template file, named "hello.c". The template implementation file contains stub procedures for each new method. These are procedures whose bodies are largely vacuous, to be filled in by the implementor. (Unimportant details have been removed for this tutorial.)  
+
  
 
#include <hello.ih>
 
#include <hello.ih>
Line 138: Line 112:
 
     HelloMethodDebug("Hello", "sayHello");
 
     HelloMethodDebug("Hello", "sayHello");
 
}
 
}
 
  
 
The terms SOM_Scope and SOMLINK appear in the prototype for all stub procedures, but they are defined by SOM and are not of interest to the developer. In the method procedure for the "sayHello" method, "somSelf" is a pointer to the target object (here, an instance of the class "Hello") that will respond to the method. A "somSelf" parameter appears in the procedure prototype for every method, since SOM requires every method to act on some object.  
 
The terms SOM_Scope and SOMLINK appear in the prototype for all stub procedures, but they are defined by SOM and are not of interest to the developer. In the method procedure for the "sayHello" method, "somSelf" is a pointer to the target object (here, an instance of the class "Hello") that will respond to the method. A "somSelf" parameter appears in the procedure prototype for every method, since SOM requires every method to act on some object.  
Line 161: Line 134:
 
     printf("Hello, World!\n");
 
     printf("Hello, World!\n");
 
}
 
}
 
  
 
4.Create a client program that uses the class.  
 
4.Create a client program that uses the class.  
Line 186: Line 158:
 
     return (0);
 
     return (0);
 
}
 
}
 
  
 
Notice the statement obj = HelloNew(); The "hello.h" header file automatically contains the SOM-defined macro <className>New(), which is used to create an instance of the <className> class (here, the "Hello" class).  
 
Notice the statement obj = HelloNew(); The "hello.h" header file automatically contains the SOM-defined macro <className>New(), which is used to create an instance of the <className> class (here, the "Hello" class).  
Line 217: Line 188:
 
     return (0);
 
     return (0);
 
}
 
}
 
  
 
Notice that the only argument passed to the "sayHello" method by a C++ client program is the Environment pointer. (Contrast this with the invocation of "sayHello" in the C client program, above.  
 
Notice that the only argument passed to the "sayHello" method by a C++ client program is the Environment pointer. (Contrast this with the invocation of "sayHello" in the C client program, above.  
Line 225: Line 195:
 
Note: On AIX or OS/2, the environment variable SOMBASE represents the directory in which SOM has been installed.  
 
Note: On AIX or OS/2, the environment variable SOMBASE represents the directory in which SOM has been installed.  
  
Under AIX, for C programmers:  
+
Under AIX, for C programmers:
 
+
> xlc -I. -I$SOMBASE/include -o hello main.c hello.c  -L$SOMBASE/lib -l somtk
> xlc -I. -I$SOMBASE/include -o hello main.c hello.c  -L$SOMBASE/lib -l somtk
+
Under AIX, for C++ programmers:
 
+
> xlc -I. -I$SOMBASE/include -o hello main.C hello.C  -L$SOMBASE/lib -lsomtk
 
+
Under OS/2, for C programmers:
Under AIX, for C++ programmers:  
+
> set LIB=%SOMBASE%\lib;%LIB%
 
+
> icc -I. -I%SOMBASE%\include -Fe hello main.c hello.c  somtk.lib
> xlc -I. -I$SOMBASE/include -o hello main.C hello.C  -L$SOMBASE/lib -lsomtk
+
Under OS/2, for C++ programmers:
 
+
> set LIB=%SOMBASE%\lib;%LIB%
 
+
> icc -I. -I\%SOMBASE%\include -Fe hello main.cpp hello.cpp  somtk.lib
Under OS/2, for C programmers:  
+
6.Execute the client program.
 
+
> hello
> set LIB=%SOMBASE%\lib;%LIB%
+
Hello, World!
> icc -I. -I%SOMBASE%\include -Fe hello main.c hello.c  somtk.lib
+
Example 2 will extend the "Hello" class to introduce an "attribute."
 
+
 
+
Under OS/2, for C++ programmers:  
+
 
+
> set LIB=%SOMBASE%\lib;%LIB%
+
> icc -I. -I\%SOMBASE%\include -Fe hello main.cpp hello.cpp  somtk.lib
+
 
+
 
+
6.Execute the client program.  
+
 
+
> hello
+
Hello, World!
+
 
+
 
+
Example 2 will extend the "Hello" class to introduce an "attribute."  
+
 
+
  
 
File Extensions for SOM Files  
 
File Extensions for SOM Files  
Line 274: Line 228:
  
 
===Example 2===
 
===Example 2===
Example 1 introduced a class "Hello" which has a method "sayHello" that prints the fixed string "Hello, World!" Example 2 extends the "Hello" class so that clients can customize the output from the method "sayHello".  
+
Example 1 introduced a class "Hello" which has a method "sayHello" that prints the fixed string "Hello, World!" Example 2 extends the "Hello" class so that clients can customize the output from the method "sayHello".
  
 
1.Modify the interface declaration for the class definition in "hello.idl."  
 
1.Modify the interface declaration for the class definition in "hello.idl."  
  
Class "Hello" is extended by adding an attribute that we call "msg". Declaring an attribute is equivalent to defining "get" and "set" methods. For example, specifying:  
+
Class "Hello" is extended by adding an attribute that we call "msg". Declaring an attribute is equivalent to defining "get" and "set" methods. For example, specifying:
 
+
 
   attribute string msg;
 
   attribute string msg;
  
 
+
is equivalent to defining the two methods:
is equivalent to defining the two methods:  
+
 
+
 
   string _get_msg();
 
   string _get_msg();
 
   void  _set_msg(in string msg);
 
   void  _set_msg(in string msg);
 
  
 
Thus, for convenience, an attribute can be used (rather than an instance variable) in order to use the automatically defined "get" and "set" methods without having to write their method procedures. The new interface specification for "Hello" that results from adding attribute "msg" to the "Hello" class is as follows (with some comment lines omitted):  
 
Thus, for convenience, an attribute can be used (rather than an instance variable) in order to use the automatically defined "get" and "set" methods without having to write their method procedures. The new interface specification for "Hello" that results from adding attribute "msg" to the "Hello" class is as follows (with some comment lines omitted):  
Line 320: Line 270:
 
   }
 
   }
  
 +
This implementation assumes that "_set_msg" has been invoked to initialize the "msg" attribute before the "_get_msg" method is invoked by the "sayHello" method. This initialization can be done within the client program.
  
This implementation assumes that "_set_msg" has been invoked to initialize the "msg" attribute before the "_get_msg" method is invoked by the "sayHello" method. This initialization can be done within the client program.  
+
4.Update the client program.
  
4.Update the client program.
+
Modify the client program so that the "_set_msg" method is invoked to initialize the "msg" attribute before the "sayHello" method is invoked. Notice that, because the "_set_msg" method name begins with an underscore, the C client program invokes the method with two leading underscores.
 
+
Modify the client program so that the "_set_msg" method is invoked to initialize the "msg" attribute before the "sayHello" method is invoked. Notice that, because the "_set_msg" method name begins with an underscore, the C client program invokes the method with two leading underscores.  
+
 
+
For C programmers:
+
  
 +
For C programmers:
 
   #include <hello.h>
 
   #include <hello.h>
  
Line 346: Line 294:
 
   }
 
   }
  
 
+
For C++ programmers:
For C++ programmers:  
+
 
+
 
   #include <hello.xh>
 
   #include <hello.xh>
  
Line 366: Line 312:
 
   }
 
   }
  
 +
5.Compile and link the client program, as before.
  
5.Compile and link the client program, as before.
+
6.Execute the client program:
 
+
6.Execute the client program:  
+
 
+
 
   > hello
 
   > hello
 
   Hello World Again
 
   Hello World Again
 
+
The next example extends the "Hello" class to override (redefine) one of the methods it inherits from its parent class, SOMObject.
 
+
The next example extends the "Hello" class to override (redefine) one of the methods it inherits from its parent class, SOMObject.  
+
  
 
===Attributes vs instance variables===
 
===Attributes vs instance variables===
Line 562: Line 504:
 
   > hello
 
   > hello
 
     -- a Hello object at location 20062838 with msg: Hi There
 
     -- a Hello object at location 20062838 with msg: Hi There
 
  
  
Line 596: Line 537:
 
};
 
};
  
 
+
2.Re-run the SOM Compiler on the updated hello.idl file, as before. SOM Compiler extends the existing implementation file. Below is the initializer stub procedure that the SOM Compiler adds to the C language implementation file; C++ implementation files would be similarly revised:
2.Re-run the SOM Compiler on the updated hello.idl file, as before. SOM Compiler extends the existing implementation file. Below is the initializer stub procedure that the SOM Compiler adds to the C language implementation file; C++ implementation files would be similarly revised:  
+
 
+
 
+
  
 
SOM_Scope void SOMLINK
 
SOM_Scope void SOMLINK
Line 636: Line 574:
 
   __set_msg(somSelf, "Initial Message");
 
   __set_msg(somSelf, "Initial Message");
 
}
 
}
 
 
  
 
4.Update the client program to illustrate default initialization.  
 
4.Update the client program to illustrate default initialization.  
 
 
  
 
#include <hello.h>
 
#include <hello.h>
Line 740: Line 674:
 
   __set_msg(somSelf, "Initial Message");
 
   __set_msg(somSelf, "Initial Message");
 
}
 
}
 
  
 
3.Continue to customize the implementation file, hello.c, as follows. Notice that the "sayHello" method (last discussed in Example 2) is now modified to allow alternate ways of outputing a "msg".  
 
3.Continue to customize the implementation file, hello.c, as follows. Notice that the "sayHello" method (last discussed in Example 2) is now modified to allow alternate ways of outputing a "msg".  
Line 769: Line 702:
 
}
 
}
  
 +
The "switch" statement invokes the appropriate method depending on the value of the "output" attribute. Notice how the "case" statements utilize the enumeration values of "outputTypes" declared in "hello.idl" by prefacing the enumeration names with the class name (Hello_screen, Hello_printer, and Hello_disk).
  
The "switch" statement invokes the appropriate method depending on the value of the "output" attribute. Notice how the "case" statements utilize the enumeration values of "outputTypes" declared in "hello.idl" by prefacing the enumeration names with the class name (Hello_screen, Hello_printer, and Hello_disk).
+
4.Update the client program, as illustrated next:
 
+
4.Update the client program, as illustrated next:  
+
  
 
   #include <hello.h>
 
   #include <hello.h>
Line 795: Line 727:
 
   }
 
   }
  
 +
5.Compile and link the client program as before, except also include the implementation files for the "Printer" and "Disk" classes in the compilation.
  
5.Compile and link the client program as before, except also include the implementation files for the "Printer" and "Disk" classes in the compilation.
+
6.Execute the client program. Observe that the message that prints is the "msg" defined in Example 4 as part of the somDefaultInit initialization of the "Hello" object.
 
+
6.Execute the client program. Observe that the message that prints is the "msg" defined in Example 4 as part of the somDefaultInit initialization of the "Hello" object.  
+
 
+
  
 
     Initial Message
 
     Initial Message
Line 807: Line 737:
 
     Initial Message              - goes to Disk
 
     Initial Message              - goes to Disk
  
 +
This tutorial has described features of SOM IDL that will be useful to C and C++ programmers. SOM IDL also provides features such as full type checking, constructs for declaring private methods, and constructs for defining methods that receive and return pointers to structures. Chapter 4, "SOM IDL and the SOM Compiler" gives complete description of the SOM IDL syntax and also describes how to use the SOM Compiler. In addition, Chapter 5, "Implementing Classes in SOM," provides helpful information for completing the implementation template, for using initializer (somDefaultInit or user-defined initialization methods), for defining SOM class libraries, and for customizing various aspects of SOMobjects execution.
  
This tutorial has described features of SOM IDL that will be useful to C and C++ programmers. SOM IDL also provides features such as full type checking, constructs for declaring private methods, and constructs for defining methods that receive and return pointers to structures. Chapter 4, "SOM IDL and the SOM Compiler" gives complete description of the SOM IDL syntax and also describes how to use the SOM Compiler. In addition, Chapter 5, "Implementing Classes in SOM," provides helpful information for completing the implementation template, for using initializer (somDefaultInit or user-defined initialization methods), for defining SOM class libraries, and for customizing various aspects of SOMobjects execution.
 
 
[[Category:System Object Model Programming Guide]]
 
[[Category:System Object Model Programming Guide]]

Revision as of 12:57, 2 August 2020

System Object Model Programming Guide
  1. About This Book
  2. Introduction to the SOMobjects Developer Toolkit
  3. Tutorial for Implementing SOM Classes
  4. Using SOM Classes in Client Programs
  5. SOM IDL and the SOM Compiler

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

This tutorial contains five examples showing how SOM classes can be implemented to achieve various functionality. Obviously, for any person who wishes to become a class implementor, this tutorial is essential. However, even for those programmers who expect only to use SOM classes that were implemented by others, the tutorial is also necessary, as it presents several concepts that will help clarify the process of using SOM classes.

Basic Concepts of the System Object Model (SOM)

The System Object Model (SOM), provided by the SOMobjects Developer Toolkit, is a set of libraries, utilities, and conventions used to create binary class libraries that can be used by application programs written in various object-oriented programming languages, such as C++ and Smalltalk, or in traditional procedural languages, such as C and Cobol. The following paragraphs introduce some of the basic terminology used when creating classes in SOM:

  • An object is an OOP entity that has behavior (its methods or operations) and state (its data values). In SOM, an object is a run-time entity with a specific set of methods and instance variables. The methods are used by a client programmer to make the object exhibit behavior (that is, to do something), and the instance variables are used by the object to store its state. (The state of an object can change over time, which allows the object's behavior to change.) When a method is invoked on an object, the object is said to be the receiver or target of the method call.
  • An object's implementation is determined by the procedures that execute its methods, and by the type and layout of its instance variables. The procedures and instance variables that implement an object are normally encapsulated (hidden from the caller), so a program can use the object's methods without knowing anything about how those methods are implemented. Instead, a user is given access to the object's methods through its interface (a description of the methods in terms of the data elements required as input and the type of value each method returns).
  • An interface through which an object may be manipulated is represented by an object type. That is, by declaring a type for an object variable, a programmer specifies the interface that is intended to be used to access that object. SOM IDL (the SOM Interface Definition Language) is used to define object interfaces. The interface names used in these IDL definitions are also the type names used by programmers when typing SOM object variables.
  • In SOM, as in most approaches to object-oriented programming, a class defines the implementation of objects. That is, the implementation of any SOM object (as well as its interface) is defined by some specific SOM class. A class definition begins with an IDL specification of the interface to its objects, and the name of this interface is used as the class name as well. Each object of a given class may also be called an instance of the class, or an instantiation of the class.
  • Inheritance, or class derivation, is a technique for developing new classes from existing classes. The original class is called the base class, or the parent class, or sometimes the direct ancestor class. The derived class is called a child class or a subclass. The primary advantage of inheritance is that a derived class inherits all of its parent's methods and instance variables. Also through inheritance, a new class can override (or redefine) methods of its parent, in order to provide enhanced functionality as needed. In addition, a derived class can introduce new methods of its own. If a class results from several generations of successive class derivation, that class "knows" all of its ancestors's methods (whether overridden or not), and an object (or instance) of that class can execute any of those methods.
  • SOM classes can also take advantage of multiple inheritance, which means that a new class is jointly derived from two or more parent classes. In this case, the derived class inherits methods from all of its parents (and all of its ancestors), giving it greatly expanded capabilities. In the event that different parents have methods of the same name that execute differently, SOM provides ways for avoiding conflicts.
  • In the SOM run time, classes are themselves objects. That is, classes have their own methods and interfaces, and are themselves defined by other classes. For this reason, a class is often called a class object. Likewise, the terms class methods and class variables are used to distinguish between the methods/variables of a class object vs. those of its instances. (Note that the type of an object is not the same as the type of its class, which as a "class object" has its own type.)
  • A class that defines the implementation of class objects is called a metaclass. Just as an instance of a class is an object, so an instance of a metaclass is a class object. Moreover, just as an ordinary class defines methods that its objects respond to, so a metaclass defines methods that a class object responds to. For example, such methods might involve operations that execute when a class (that is, a class object) is creating an instance of itself (an object). Just as classes are derived from parent classes, so metaclasses can be derived from parent metaclasses, in order to define new functionality for class objects.
  • The SOM system contains three primitive classes that are the basis for all subsequent classes:
SOMObject 
the root ancestor class for all SOM classes,
SOMClass 
the root ancestor class for all SOM metaclasses, and
SOMClassMgr 
the class of the SOMClassMgrObject, an object created automatically during SOM initialization, to maintain a registry of existing classes and to assist in dynamic class loading/unloading. SOMClass is defined as a subclass (or child) of SOMObject and inherits all generic object methods; this is why instances of a metaclass are class objects (rather than simply classes) in the SOM run time.

SOM classes are designed to be language neutral. That is, SOM classes can be implemented in one programming language and used in programs of another language. To achieve language neutrality, the interface for a class of objects must be defined separately from its implementation. That is, defining interface and implementation requires two completely separate steps (plus an intervening compile), as follows:

  • An interface is the information that a program must know in order to use an object of a particular class. This interface is described in an interface definition (which is also the class definition), using a formal language whose syntax is independent of the programming language used to implement the class's methods. For SOM classes, this is the SOM Interface Definition Language (SOM IDL). The interface is defined in a file known as the IDL source file (or, using its extension, this is often called the .idl file).
  • An interface definition is specified within the interface declaration (or interface statement) of the .idl file, which includes:
(a) 
the interface name (or class name) and the name(s) of the class's parent(s), and
(b) 
the names of the class's attributes and the signatures of its new methods. (Recall that the complete set of available methods also includes all inherited methods.) Each method signature includes the method name, and the type and order of its arguments, as well as the type of its return value (if any). Attributes are instance variables for which "set" and "get" methods will automatically be defined, for use by the application program. (By contrast, instance variables that are not attributes are hidden from the user.)

Once the IDL source file is complete, the SOM Compiler is used to analyze the .idl file and create the implementation template file, within which the class implementation will be defined. Before issuing the SOM Compiler command, sc, the class implementor can set an environment variable that determines which emitters (output-generating programs) the SOM Compiler will call and, consequently, which programming language and operating system the resulting binding files will relate to. (Alternatively, this emitter information can be placed on the command line for sc.) In addition to the implementation template file itself, the binding files include two language-specific header files that will be #included in the implementation template file and in application program files. The header files define many useful SOM macros, functions, and procedures that can be invoked from the files that include the header files.

The implementation of a class is done by the class implementor in the implementation template file (often called just the implementation file or the template file). As produced by the SOM Compiler, the template file contains stub procedures for each method of the class. These are incomplete method procedures that the class implementor uses as a basis for implementing the class by writing the corresponding code in the programming language of choice.

In summary, the process of implementing a SOM class includes using the SOM IDL syntax to create an IDL source file that specifies the interface to a class of objects - that is, the methods and attributes that a program can use to manipulate an object of that class. The SOM Compiler is then run to produce an implementation template file and two binding (header) files that are specific to the designated programming language and operating system. Finally, the class implementor writes language-specific code in the template file to implement the method procedures.

At this point, the next step is to write the application (or client) program(s) that use the objects and methods of the newly implemented class. (Observe, here, that a programmer could write an application program using a class implemented entirely by someone else.) If not done previously, the SOM compiler is run to generate usage bindings for the new class, as appropriate for the language used by the client program (which may be different from the language in which the class was implemented). After the client program is finished, the programmer compiles and links it using a language-specific compiler, and then executes the program. (Notice again, the client program can invoke methods on objects of the SOM class without knowing how those methods are implemented.)

Development of the Tutorial examples

Example 1 - Implementing a simple class with one method 
Prints a default message when the "sayHello" method is invoked on an object of the "Hello" class.
Example 2 - Adding an attribute to the Hello class 
Defines a "msg" attribute for the "sayHello" method to use. The client program "sets" a message; then the "sayHello" method "gets" the message and prints it. (There is no defined message when an object of the "Hello" class is first created.)
Example 3 - Overriding an inherited method 
Overrides the SOMobjects method somPrintSelf so that invoking this method on an object of the "Hello" class will not only display the class name and the object's location, but will also include the object's message attribute.
Example 4 - Initializing a SOM object 
Overrides the default initialization method, somDefaultInit, to illustrate how an object's instance variables can be initialized when the object is created.
Example 5 - Using multiple inheritance 
Extends the "Hello" class to provide it with multiple inheritance (from the "Disk;" and "Printer" classes.) The "Hello" interface defines an enum and an "output" attribute that takes its value from the enum (either "screen," "printer," or "disk"). The client program "sets" the form of "output" before invoking the "sayHello" method to send a "msg"(defined as in Example 4).

Basic Steps for Implementing SOM Classes

Implementing and using SOM classes in C or C++ involves the following steps, which are explicitly illustrated in the examples of this tutorial:

  • Define the interface to objects of the new class (that is, the interface declaration), by creating a .idl file.
  • Run the SOM Compiler on the .idl file by issuing the sc command to produce the following binding files:
Template implementation file
a .c file for C programmers, or
a .cpp file for C++ programmers;
Header file to be included in the implementation file
a .ih file for C programmers, or
a .xih file for C++ programmers; and
Header file to be included in client programs that use the class
a .h file for C clients, or
a .xh file for C++ clients.

To specify whether the SOM Compiler should produce C or C++ bindings, set the value of the SMEMIT environment variable or use the "-s" option of the sc or somc command, as described in Section 4.3, "The SOM Compiler." By default, the SOM Compiler produces C bindings.

  1. Customize the implementation, by adding code to the template implementation file.
  2. Create a client program that uses the class.
  3. Compile and link the client code with the class implementation, using a C or C++ compiler.
  4. Execute the client program.

The following examples illustrate appropriate syntax for defining interface declarations in a .idl file, including designating the methods that the class's instances will perform. In addition, example template implementation files contain typical code that the SOM Compiler produces. Explanations accompanying each example discuss topics that are significant to the particular example; full explanations of the SOM IDL syntax are contained in Chapter 4, "SOM IDL and the SOM Compiler." Customization of each implementation file (step 3) is illustrated in C and C++.

Note:

  1. The Tutorial assumes you will work through the examples in order. If you do not do so, the code that the SOM Compiler generates from your revised .idl file may vary slightly from what you see in the Tutorial.
  1. When the SOMobjects Toolkit is installed, a choice is made between "somcorba" and "somstars" for the style of C bindings the SOM Compiler will generate. The Tutorial examples use the "somcorba" style, where an interface name used as a type indicates a pointer to an object, as required by strict CORBA bindings. Consequently, as the examples show, a "*" does not explicitly appear for types that are pointers to objects. If your system was installed for "somstars" C bindings, you can set the environment variable SMADDSTAR=1 or use the SOM Compiler option "-maddstar" to request bindings that use explicit pointer stars. For more information, see "Declaring object variables" in Chapter 3, "Using SOM Classes in Client Programs" and "Object types" in Chapter 4, "SOM IDL and the SOM Compiler."

Example 1

Example 1 defines a class "Hello" which introduces one new method, "sayHello". When invoked from a client program, the "sayHello" method will print the fixed string "Hello, World!" The example follows the six steps described in the preceding topic, "Basic Steps for Implementing SOM Classes."

1.Define the interface to class "Hello", which inherits methods from the root class SOMObject and introduces one new method, "sayHello". Define these IDL specifications in the file "hello.idl".

The "interface" statement introduces the name of a new class and any parents (base classes) it may have (here, the root class SOMObject). The body of the interface declaration introduces the method "sayHello." Observe that method declarations in IDL have syntax similar to C and C++ function prototypes:

  1. include <somobj.idl> //# Get the parent class definition.

interface Hello : SOMObject /* This is a simple class that demonstrates how to define the

*  interface to a new class of objects in SOM IDL.
*/

{

   void sayHello();
   // This method outputs the string "Hello, World!".
      /* On Windows, use: string sayHello();
       * This method returns the string "Hello, World!". */

};


Note that the method "sayHello" has no (explicit) arguments and returns no value (except on Windows, which returns a string). The characters "//" start a line comment, which finishes at the end of the line. The characters "/*" start a block comment which finishes with the "*/". Block comments do not nest. The two comment styles can be used interchangeably. Throw-away comments are also permitted in a .idl file; they are ignored by the SOM Compiler. Throw-away comments start with the characters "//#" and terminate at the end of the line.

Note: For simplicity, this IDL fragment does not include a releaseorder modifier; consequently, the SOM Compiler will issue a warning for the method "sayHello". For directions on using the releaseorder modifier to remove this warning, see the topic "Modifier statements" in Chapter 4, "Implementing Classes in SOM."

2.Run the SOM Compiler to produce binding files and an implementation template (that is, issue the sc command):

> sc -s"c;h;ih" hello.idl      (for C bindings on AIX or OS/2)
> sc -s"xc;xh;xih" hello.idl   (for C++ bindings on AIX or OS/2)

When set to generate C binding files, the SOM Compiler generates the following implementation template file, named "hello.c". The template implementation file contains stub procedures for each new method. These are procedures whose bodies are largely vacuous, to be filled in by the implementor. (Unimportant details have been removed for this tutorial.)

  1. include <hello.ih>

/*

*  This method outputs the string "Hello, World!".
*/

SOM_Scope void SOMLINK sayHello(Hello somSelf, Environment *ev) {

   /* HelloData *somThis = HelloGetData(somSelf); */
   HelloMethodDebug("Hello", "sayHello");

}

The terms SOM_Scope and SOMLINK appear in the prototype for all stub procedures, but they are defined by SOM and are not of interest to the developer. In the method procedure for the "sayHello" method, "somSelf" is a pointer to the target object (here, an instance of the class "Hello") that will respond to the method. A "somSelf" parameter appears in the procedure prototype for every method, since SOM requires every method to act on some object.

The target object is always the first parameter of a method's procedure, although it should not be included in the method's IDL specification. The second parameter (which also is not included in the method's IDL specification) is the parameter (Environment *ev). This parameter can be used by the method to return exception information if the method encounters an error. (Contrast the prototype for the "sayHello" method in steps 1 and 2 above.)

The remaining lines of the template above are not pertinent at this point. (For those interested, they are discussed in section 5.4 of Chapter 5, "Implementing SOM Classes.") The file is now ready for customization with the C code needed to implement method "sayHello".

When set to generate C++ binding files, the SOM Compiler generates an implementation template file, "hello.C" (on AIX) or "hello.cpp (on OS/2), similar to the one above. (Chapter 5 discusses the implementation template in more detail.)

Recall that, in addition to generating a template implementation file, the SOM Compiler also generates implementation bindings (in a header file to be included in the implementation file) and usage bindings (in a header file to be included in client programs). These files are named "hello.ih" and "hello.h" for C bindings, and are "hello.xih" and "hello.xh" for C++ bindings. Notice that the "hello.c" file shown above includes the "hello.ih" implementation binding file.

3.Customize the implementation, by adding code to the template implementation file.

Modify the body of the "sayHello" method procedure in the "hello.c" (or, for C++,"hello.C" on AIX, "hello.cpp" on OS/2) implementation file so that the "sayHello" method prints "Hello, World!":

SOM_Scope void SOMLINK sayHello(Hello somSelf, Environment *ev) {

   /* HelloData *somThis = HelloGetData(somSelf); */
   HelloMethodDebug("Hello","sayHello");
   printf("Hello, World!\n");

}

4.Create a client program that uses the class.

Write a program "main" that creates an instance (object) of the "Hello" class and invokes the method "sayHello" on that object.

A C programmer would write the following program in "main.c", using the bindings defined in the "hello.h" header file:

  1. include <hello.h>

int main(int argc, char *argv[]) {

   /* Declare a variable to point to an instance of Hello */
   Hello obj;
   /* Create an instance of the Hello class */
   obj = HelloNew();
   /* Execute the "sayHello" method */
   _sayHello(obj, somGetGlobalEnvironment());
   /* Free the instance: */
   _somFree(obj);
   return (0);

}

Notice the statement obj = HelloNew(); The "hello.h" header file automatically contains the SOM-defined macro <className>New(), which is used to create an instance of the <className> class (here, the "Hello" class).

Also notice that, in C, a method is invoked on an object by using the form:

_<methodName>(<objectName>, <environment_argument>, <other_method_arguments>)


as used above in the statement _sayHello(obj, somGetGlobalEnvironment()). As shown in this example, the SOM-provided somGetGlobalEnvironment function can be used to supply the (Environment *) argument of the method.

Finally, the code uses the method somFree, which SOM also provides, to free the object created by HelloNew(). Notice that somFree does not require an (Environment *) argument. This is because the method procedures for some of the classes in the SOMobjects Toolkit (including SOMObject, SOMClass, and SOMClassMgr) do not have an Environment parameter, to ensure compatibility with the previous release of SOM. The documentation for each SOM-kernel method in the SOMobjects Developer Toolkit: Programmers Reference Manual indicates whether an Environment parameter is used.

A C++ programmer would write the following program in "main.C" (on AIX) or "main.cpp" (on OS/2), using the bindings defined in the "hello.xh" header file:

  1. include <hello.xh>

int main(int argc, char *argv[]) {

   /* Declare a variable to point to an instance of Hello */
   Hello *obj;
   /* Create an instance of the Hello class */
   obj = new Hello;
   /* Execute the "sayHello" method */
   obj->sayHello(somGetGlobalEnvironment());
   obj->somFree();
   return (0);

}

Notice that the only argument passed to the "sayHello" method by a C++ client program is the Environment pointer. (Contrast this with the invocation of "sayHello" in the C client program, above.

5.Compile and link the client code with the class implementation.

Note: On AIX or OS/2, the environment variable SOMBASE represents the directory in which SOM has been installed.

Under AIX, for C programmers:

> xlc -I. -I$SOMBASE/include -o hello main.c hello.c  -L$SOMBASE/lib -l somtk

Under AIX, for C++ programmers:

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

Under OS/2, for C programmers:

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

Under OS/2, for C++ programmers:

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

6.Execute the client program.

> hello
Hello, World!

Example 2 will extend the "Hello" class to introduce an "attribute."

File Extensions for SOM Files

┌───────────────────────────────────┬────────┬───────────────────────────┐ │IDL source file: │.idl │for all users │ ├───────────────────────────────────┼────────┼───────────────────────────┤ │Implementation template file: │.c │for C, all systems │ │ │.C │for C++, on AIX │ │ │.cpp │for C++, on OS/2 or Windows│ ├───────────────────────────────────┼────────┼───────────────────────────┤ │Header file for implementation │.ih │for C │ │file: │.xih │for C++ │ ├───────────────────────────────────┼────────┼───────────────────────────┤ │Header file for program file: │.h │for C │ │ │.xh │for C++ │ └───────────────────────────────────┴────────┴───────────────────────────┘


Example 2

Example 1 introduced a class "Hello" which has a method "sayHello" that prints the fixed string "Hello, World!" Example 2 extends the "Hello" class so that clients can customize the output from the method "sayHello".

1.Modify the interface declaration for the class definition in "hello.idl."

Class "Hello" is extended by adding an attribute that we call "msg". Declaring an attribute is equivalent to defining "get" and "set" methods. For example, specifying:

  attribute string msg;

is equivalent to defining the two methods:

  string _get_msg();
  void  _set_msg(in string msg);

Thus, for convenience, an attribute can be used (rather than an instance variable) in order to use the automatically defined "get" and "set" methods without having to write their method procedures. The new interface specification for "Hello" that results from adding attribute "msg" to the "Hello" class is as follows (with some comment lines omitted):

  #include <somobj.idl>
  interface Hello : SOMObject
  {
    void  sayHello();
    attribute string msg;
         //# This is equivalent to defining the methods:
         //#     string _get_msg();
         //#      void  _set_msg(string msg);
  };


2.Re-run the SOM Compiler on the updated idl file, as in example 1. This produces new header files and updates the existing implementation file, if needed, to reflect changes made to the .idl file. In this example, the implementation file is not modified by the SOM Compiler.

3.Customize the implementation.

Customize the implementation file by modifying the print statement in the "sayHello" method procedure. This example prints the contents of the "msg" attribute (which must be initialized in the client program) by invoking the "_get_msg" method. Notice that, because the "_get_msg" method name begins with an underscore, the method is invoked with two leading underscores (for C only).

  SOM_Scope void   SOMLINK sayHello(Hello somSelf, Environment  *ev)
  {
      /* HelloData *somThis = HelloGetData(somSelf); */
      HelloMethodDebug("Hello", "sayHello");
      printf("%s\n", __get_msg(somSelf, ev));
      /* for  C++, use   somSelf->_get_msg(ev);  */
  }

This implementation assumes that "_set_msg" has been invoked to initialize the "msg" attribute before the "_get_msg" method is invoked by the "sayHello" method. This initialization can be done within the client program.

4.Update the client program.

Modify the client program so that the "_set_msg" method is invoked to initialize the "msg" attribute before the "sayHello" method is invoked. Notice that, because the "_set_msg" method name begins with an underscore, the C client program invokes the method with two leading underscores.

For C programmers:

  #include <hello.h>
  int main(int argc, char *argv[])
  {
      Hello obj;
      obj = HelloNew();
      /* Set the msg text */
      __set_msg(obj, somGetGlobalEnvironment(), "Hello World Again");
      /* Execute the "sayHello" method */
      _sayHello(obj, somGetGlobalEnvironment());
      _somFree(obj);
      return (0);
  }

For C++ programmers:

  #include <hello.xh>
  int main(int argc, char *argv[])
  {
      Hello *obj;
      obj = new Hello;
      /* Set the msg text */
      obj->_set_msg(somGetGlobalEnvironment(), "Hello World Again");
      /* Execute the "sayHello" method */
      obj->sayHello(somGetGlobalEnvironment());
      obj->somFree();
      return (0);
  }

5.Compile and link the client program, as before.

6.Execute the client program:

  > hello
  Hello World Again

The next example extends the "Hello" class to override (redefine) one of the methods it inherits from its parent class, SOMObject.

Attributes vs instance variables

As an alternative to defining "numberObjs" as an attribute, it could be defined as an instance variable, with a "get_numberObjs" method also defined for retrieving its value. Instance variables are declared in an implementation statement, as shown below:

  interface Hello
  {
  string get_msg() ;
  void set_msg(in string msg);
  #ifdef __SOMIDL__
  implementation
  {
      string message;
  };
  #endif
  };


As demonstrated in this example, one disadvantage to using an instance variable is that the "get_msg" and "set_msg" methods must be defined in the implementation file by the class implementor. For attributes, by contrast, default implementations of the "get" and "set" methods are generated automatically by the SOM Compiler in the .ih and .xih header files.

Note: For some attributes (particularly those involving structures, strings, and pointers) the default implementation generated by the SOM Compiler for the "set" method may not be suitable. This happens because the SOM Compiler only performs a "shallow copy," which typically is not useful for distributed objects with these types of attributes. In such cases, it is possible to write your own implementations, as you do for any other method, by specifying the "noset/noget" modifiers for the attribute. (See the subtopic "Modifier statements" in Chapter 4 "SOM IDL and the SOM Compiler.")

Regardless of whether you let the SOM Compiler generate your implementations or not, if access to instance data is required, either from a subclass or a client program, then this access should be facilitated by using an attribute. Otherwise, instance data can be defined in the "implementation" statement as above (using the same syntax as used to declare variables in C or C++), with appropriate methods defined to access it. For more information about "implementation" statements, see the topic "Implementation statements" in Chapter 4.

As an example where instance variables would be used (rather than attributes), consider a class "Date" that provides a method for returning the current date. Suppose the date is represented by three instance variables-"mm", "dd", and "yy". Rather than making "mm", "dd", and "yy" attributes (and allowing clients to access them directly), "Date" defines "mm", "dd", and "yy" as instance variables in the "implementation" statement, and defines a method "get_date" that converts "mm", "dd", and "yy" into a string of the form "mm/dd/yy":

  interface Date
  {
          string get_date() ;
  #ifdef __SOMIDL__
  implementation
  {
      long mm,dd,yy;
  };
  #endif
  };


To access instance variables that a class introduces from within the class implementation file, two forms of notation are available:

somThis->variableName

or

_variableName

For example, the implementation for "get_date" would


access the "mm" instance variable as somThis->mm or _mm, access "dd" as somThis->dd or _dd, and access "yy" as somThis->yy or _yy.

In C++ programs, the _variableName form is available only if the programmer first defines the macro VARIABLE_MACROS (that is, enter #define VARIABLE_MACROS) in the implementation file prior to including the .xih file for the class.

Example 3

An important aspect of OOP programming is the ability of a subclass to replace an inherited method implementation with a new implementation especially appropriate to its instances. This is called overriding llo"q. a method. Sometimes, a class may introduce methods that every descendant class is expected to override. For example, SOMobject ves introduces the somPrintSelf method, and a good SOM programmer will generally override this method when implementing a new class.

The purpose of somPrintSelf is to print a brief description of an object. The method can be useful when debugging an application that deals with a number of objects of the same class -assuming the class designer has overridden somPrintSelf with a message that is useful in distinguishing different objects of the class. For example, the implementation of somPrintSelf provided by SOMobjects simply prints the class of the object and its address in memory. SOMclass overrides this method so that, when somPrintSelf is invoked on a class object, the name of the class will print.

This example illustrates how somPrintSelf might be overridden for the "Hello" class. An important identifying characteristic of "Hello" objects is the message they hold; thus, the following steps illustrate how somPrintSelf could be overridden in "Hello" to provide this information.

1.Modify the interface declaration in "hello.idl."

To override the somPrintSelf method in "Hello", additionalinformation must be provided in "hello.idl" in the form of an implementation statement, which gives extra information about the class, its methods and attributes, and any instance variables. (The previous examples omitted the optional "implementation" statement, because it was not needed.)

In the current example, the "implementation" statement introduces the modifiers for the "Hello" class, as follows.

  #include <somobj.idl>
  interface Hello : SOMObject
  {
    void sayHello();
    attribute string msg;
    #ifdef __SOMIDL__
    implementation
    {
            //# Method Modifiers:
            somPrintSelf: override;
            // Override the inherited implementation of somPrintSelf.
    };
    #endif
    };


Here, "somPrintSelf:" introduces a list of modifiers for the (inherited) somPrintSelf method in the class "Hello". Modifiers are like C/C ++ #pragma commands and give specific implementation details to the compiler. This example uses only one modifier, "override." Because of the "override" modifier, when somPrintSelf is invoked on an instance of class "Hello", Hello's implementation of somPrintSelf (in the implementation file) will be called, instead of the implementation inherited from the parent class, SOMObject.

The "#ifdef__SOMIDL__" and "#endif" are standard C and C++ preprocessor commands that cause the "implementation" statement to be read only when using the SOM IDL compiler (and not some other IDL compiler).

2.Re-run the SOM Compiler on the updated .idl file, as before. The SOM Compiler extends the existing implementation file from Example 2 to include new stub procedures as needed (in this case, for somPrintSelf). Below is a shortened version of the C language implementation file as updated by the SOM Compiler; C++ implementation files are similarly revised. Notice that the code previously added to the "sayHello" method is not disturbed when the SOM Compiler updates the implementation file.

  #include <hello.ih>
  SOM_Scope void   SOMLINK sayHello(Hello somSelf, Environment  *ev)
  {
      /* HelloData *somThis = HelloGetData(somSelf); */
      HelloMethodDebug("Hello","sayHello");
      printf("%s\n", __get_msg(somSelf, ev));
  }
  SOM_Scope void   SOMLINK  somPrintSelf(Hello somSelf)
  {
      HelloData *somThis = HelloGetData(somSelf);
      HelloMethodDebug("hello","somPrintSelf");
        Hello_parent_SOMObject_somPrintSelf(somSelf);
  }


Note that the SOM Compiler added code allowing the "Hello" class to redefine somPrintSelf. The SOM Compiler provides a default implementation for overriding the somPrintSelf method. This default implementation simply calls the "parent method" [the procedure that the parent class of "Hello" (SOMObject) uses to implement the somPrintSelf method]. This parent method call is accomplished by the macro Hello_parent_SOMObject_somPrintSelf defined in "hello.ih."

Notice that the stub procedure for overriding the somPrintSelf method does not include an Environment parameter. This is because somPrintSelf is introduced by SOMObject, which does not include the Environment parameter in any of its methods (to ensure backward compatibility). The signature for a method cannot change after it has been introduced.

3.Customize the implementation.

Within the new somPrintSelf method procedure, display a brief description of the object, appropriate to "Hello" objects. Note that the parent method call is not needed, so it has been deleted. Also, direct access to instance data introduced by the "Hello" class is not required, so the assignment to "somThis" has been commented out (see the first line of the procedure).

  SOM_Scope void   SOMLINK somPrintSelf(Hello somSelf)
  {
      HelloData *somThis = HelloGetData(somSelf);
      HelloMethodDebug("Hello","somPrintSelf");
    somPrintf("--a %s object at location %x with msg:s\n",
                  _somGetClassName(somSelf),
                  somSelf,
                  __get_msg(somSelf,0));
  }


4.Update the client program to illustrate the change (also notice the new message text):

For C programmers:

  #include <hello.h>
  int main(int argc, char *argv[])
  {
      Hello obj;
      Environment *ev = somGetGlobalEnvironment();
      obj = HelloNew();
      /* Set the msg text */
      __set_msg(obj, ev, "Hi There");
      /* Execute the "somPrintSelf" method */
      _somPrintSelf(obj);
           _somFree(obj);
           return (0);
  }


For C++ programmers:

  #include <hello.xh>
  int main(int argc, char *argv[])
  {
      Hello *obj;
      Environment *ev = somGetGlobalEnvironment();
      obj = new Hello;
      /* Set the msg text */
      __setmsg(obj, ev, "Hi There");
     /* Execute the "somPrintSelf" method */
        obj->somPrintSelf();
      obj->somFree();
      return (0);
  }


5.Compile and link the client program, as before.

6.Execute the client program, which now outputs the message:

  > hello
   -- a Hello object at location 20062838 with msg: Hi There


Example 4

The previous example showed how to override the method somPrintSelf, introduced by SOMObject. As mentioned in that example, somPrintSelf should generally be overridden when implementing a new class. Another method introduced by SOMObject that should generally be overridden is somDefaultInit. The purpose of somDefaultInit is to provide a "default" initializer for the instance variables introduced by a class.

This example shows how to override somDefaultInit to give each "Hello" object's message an initial value when the object is first created. To learn more about initializers than shown in this example (including how to introduce new initializers that take arbitrary arguments, and how to explicitly invoke initializers) read Section 5.5, "Initializing and Uninitializing Objects," in Chapter 5, "Implementing Classes in SOM."

The overall process of overriding somDefaultInit is similar to that of the previous example. First, the IDL for "Hello" is modified. In addition to an override modifier, an init modifier is used to indicate that a stub procedure for an initialization method is desired (the stub procedures for initializers are different from those of normal methods).


1.Modify the interface declaration in "hello.idl."


  1. include <somobj.idl>

interface Hello : SOMObject {

 void sayHello();
 attribute string msg;
  1. ifdef __SOMIDL__

implementation {

   //# Method Modifiers:
   somPrintSelf: override;
   somDefaultInit: override, init;

};

  1. endif

};

2.Re-run the SOM Compiler on the updated hello.idl file, as before. SOM Compiler extends the existing implementation file. Below is the initializer stub procedure that the SOM Compiler adds to the C language implementation file; C++ implementation files would be similarly revised:

SOM_Scope void SOMLINK

         somDefaultInit(Hello somSelf, somInitCtrl *ctrl)

{

 HelloData *somThis; /* set by BeginInitializer */
 somInitCtrl globalCtrl;
 somBooleanVector myMask;
 HelloMethodDebug("Hello", "somDefaultInit");
 Hello_BeginInitializer_somDefaultInit;
 Hello_Init_SOMObject_somDefaultInit(somSelf, ctrl);
 /*
  * local Hello initialization code added by programmer
  */

}


3.Customize the implementation.

Here, the "msg" instance variable is set in the implementation template (rather than in the client program, as before). Thus, the "msg" is defined as part of the "Hello" object's initialization.

SOM_Scope void SOMLINK

              somDefaultInit(Hello somSelf, somInitCtrl *ctrl)

{

 HelloData *somThis; /* set by BeginInitializer */
 somInitCtrl globalCtrl;
 somBooleanVector myMask;
 HelloMethodDebug("Hello", "somDefaultInit");
 Hello_BeginInitializer_somDefaultInit;
 Hello_Init_SOMObject_somDefaultInit(somSelf, ctrl);
 /*
  * local Hello initialization code added by programmer
  */
 __set_msg(somSelf, "Initial Message");

}

4.Update the client program to illustrate default initialization.

  1. include <hello.h>

main() {

   Hello h = HelloNew();
   /* Execute the "somPrintSelf" method */
   _somPrintSelf(h);

}


5.Compile and link the client program, as before.

6.Execute the client program.


> hello -- a Hello object at 200633A8 with msg: Initial Message


Example 5

The "Hello" class is useful for writing messages to the screen. So that clients can also write messages to printers and disk files, this example references two additional classes: "Printer" and "Disk." The "Printer" class will manage messages to a printer, and the "Disk" class will manage messages sent to files. These classes can be defined as follows:

  #include <somobj.idl>
  interface Printer : SOMObject
  {
      void stringToPrinter(in string s) ;
       // This method writes a string to a printer.
  };
  #include <somobj.idl>
  interface Disk : SOMObject
  {
       void stringToDisk(in string s) ;
       // This method writes a string to disk.
  };


This example assumes the "Printer" and "Disk" classes are defined separately (in "print.idl" and "disk.idl," for example), are implemented in separate files, and are linked with the other example code. Given the implementations of the "Printer" and "Disk" interfaces, the "Hello" class can use them by inheriting from them, as illustrated next.

1.Modify the interface declaration in "hello.idl".

  #include <disk.idl>
  #include <printer.idl>
  interface Hello : Disk, Printer
  {
      void sayHello();
      attribute string msg;
      enum outputTypes {screen, printer, disk};
      // Declare an enumeration for the different forms of output
      attribute outputTypes output;
      // The current form of output
  #ifdef __SOMIDL__
    implementation {
    somDefaultInit: override, init; 
    };
  #endif //#  __SOMIDL__
  };


Notice that SOMObject is not listed as a parent of "Hello" above, because SOMObject is a parent of "Disk" (and of "Printer").

The IDL specification above declares an enumeration "outputTypes" for the different forms of output, and an attribute "output" whose value will depend on where the client wants the output of the "sayHello" method to go.

Note: SOM IDL allows the use of structures, unions (though with a syntax different from C or C++), enumerations, constants, and typedefs, both inside and outside the body of an interface statement. Declarations that appear inside an interface body will be emitted in the header file (that is, in "hello.h" or "hello.xh"). Declarations that appear outside of an interface body do not appear in the header file (unless required by a special #pragma directive; see the SOM Compiler options in Chapter 4).

SOM IDL also supports all of the C and C++ preprocessor directives, including conditional compilation, macro processing, and file inclusion.

2.Re-run the SOM Compiler on the updated idl file..

Unfortunately, when this is done, the implementation for somDefaultInit is not correctly updated to reflect the addition of two new parents to "Hello." This is because the implementation file emitter never changes the bodies of existing method procedures. As a result, method procedures for initializer methods are not given new parent calls when the parents of a class are changed. One way to deal with this (when the parents of a class are changed) is to temporarily rename the method procedure for initializer methods, and then run the implementation emitter. Once this is done, the code in the renamed methods can be merged into the new templates, which will include all the appropriate parent method calls. When this is done here, the new implementation for somDefaultInit would appear as:

     SOM_Scope void SOMLINK
                    somDefaultInit(Hello somSelf, somInitCtrl *ctrl)
{
  HelloData *somThis; /* set by BeginInitializer */
  somInitCtrl globalCtrl;
  somBooleanVector myMask;
  HelloMethodDebug("Hello", "somDefaultInit");
  Hello_BeginInitializer_somDefaultINit;
   Hello_Init_Disk_somDefaultInit(somSelf, ctrl);
   Hello_Init_Printer_somDefaultInit(somSelf, ctrl);
  /*
   * local Hello initialization code added by programmer
  /*
  __set_msg(somSelf, "Initial Message");

}

3.Continue to customize the implementation file, hello.c, as follows. Notice that the "sayHello" method (last discussed in Example 2) is now modified to allow alternate ways of outputing a "msg".

SOM_Scope void SOMLINK sayHello(Hello somSelf, Environment *ev) {

   /* HelloData *somThis = HelloGetData(somSelf) ; */
   HelloMethodDebug("Hello","sayHello") ;
   switch (__get_output(somSelf, ev) ) {
           /*  for C++, use:  somSelf->_get_output(ev) */
   case Hello_screen:
       printf("%s\n", __get_msg(somSelf, ev) );
           /*  for C++, use:  somSelf->_get_msg(ev) */
       break;
   case Hello_printer:
       _stringToPrinter(somSelf, ev, __get_msg(somSelf, ev) );
           /* for C++, use:
            * somSelf->stringToPrinter(ev, somSelf->_get_msg(ev) );
            */
       break;
   case Hello_disk:
       _stringToDisk(somSelf, ev, __get_msg(somSelf, ev) );
           /*  for C++, use:
            *   somSelf->stringToDisk(ev, somSelf->_get_msg(ev) );
            */
  break;
   }

}

The "switch" statement invokes the appropriate method depending on the value of the "output" attribute. Notice how the "case" statements utilize the enumeration values of "outputTypes" declared in "hello.idl" by prefacing the enumeration names with the class name (Hello_screen, Hello_printer, and Hello_disk).

4.Update the client program, as illustrated next:

  #include <hello.h>
          /* for C++, use "hello.xh" and <stdio.h> */
  int main(int argc, char *argv[])
  {
      Hello a = HelloNew();
      Environment *ev = somGetGlobalEnvironment();
      /*Invoke "sayHello" on an object and use each output */
      _sayHello(a, ev) ; /* for c++, use:a->sayHello(ev);*/
       __set_output(a, ev, Hello_printer); 
                                /* C++: a->_set_output(ev,Hello_printer);*/
      _sayHello(a, ev);
      __set_output(a, ev, Hello_disk);
                 /* C++:a->_set_output(ev,Hello-disk);       */
      _sayHello(a, ev) ; 
      _somFree(a0 ;  /* for C++, use: a->somFree(); */
       return (0);
  }

5.Compile and link the client program as before, except also include the implementation files for the "Printer" and "Disk" classes in the compilation.

6.Execute the client program. Observe that the message that prints is the "msg" defined in Example 4 as part of the somDefaultInit initialization of the "Hello" object.

    Initial Message
    Initial Message               - goes to a Printer
    Initial Message               - goes to Disk

This tutorial has described features of SOM IDL that will be useful to C and C++ programmers. SOM IDL also provides features such as full type checking, constructs for declaring private methods, and constructs for defining methods that receive and return pointers to structures. Chapter 4, "SOM IDL and the SOM Compiler" gives complete description of the SOM IDL syntax and also describes how to use the SOM Compiler. In addition, Chapter 5, "Implementing Classes in SOM," provides helpful information for completing the implementation template, for using initializer (somDefaultInit or user-defined initialization methods), for defining SOM class libraries, and for customizing various aspects of SOMobjects execution.