Implementing Classes in SOM: Difference between revisions
Line 112: | Line 112: | ||
There are three important aspects of SOM's approach to derived metaclasses: | There are three important aspects of SOM's approach to derived metaclasses: | ||
*First, the creation of SOM-derived metaclasses is integrated with programmer-specified metaclasses. If a programmer-specified metaclass already supports all the class methods and variables needed by a new class, then the programmer-specified metaclass will be used as is. | |||
*Second, if SOM must derive a different metaclass than the one explicitly indicated by the programmer (in order to support all the necessary class methods and variables), then the SOM-derived metaclass inherits from the explicitly indicated metaclass first. As a result, the method procedures defined by the specified metaclass take precedence over other possibilities (see the following section on inheritance and the discussion of resolution of ambiguity in the case of multiple inheritance). | |||
*Finally, the class methods defined by the derived metaclass invoke the appropriate initialization methods of its parents to ensure that the class variables of its instances are correctly initialized. | |||
As further explanation for the automatic derivation of metaclasses, consider the following multiple-inheritance example. Class "C" (derived from classes "A" and "B") does not have an explicit metaclass declaration in its SOM IDL, yet its parents "A" and "B" do. As a result, class "C" requires a derived metaclass. (If you still have trouble following the reasoning behind derived metaclasses, ask yourself the following question: What class should "C" be an instance of? After a bit of reflection, you will conclude that, if SOM did not build the derived metaclass, you would have to do so yourself.) | As further explanation for the automatic derivation of metaclasses, consider the following multiple-inheritance example. Class "C" (derived from classes "A" and "B") does not have an explicit metaclass declaration in its SOM IDL, yet its parents "A" and "B" do. As a result, class "C" requires a derived metaclass. (If you still have trouble following the reasoning behind derived metaclasses, ask yourself the following question: What class should "C" be an instance of? After a bit of reflection, you will conclude that, if SOM did not build the derived metaclass, you would have to do so yourself.) | ||
Line 128: | Line 128: | ||
At the same time, SOM is unique in that it relieves programmers of the responsibility for avoiding metaclass incompatibility when defining a new class. At first glance, this might seem to be merely a useful (though very important) convenience. But, in fact, it is absolutely essential, because SOM is predicated on binary compatibility with respect to changes in class implementations. | At the same time, SOM is unique in that it relieves programmers of the responsibility for avoiding metaclass incompatibility when defining a new class. At first glance, this might seem to be merely a useful (though very important) convenience. But, in fact, it is absolutely essential, because SOM is predicated on binary compatibility with respect to changes in class implementations. | ||
A programmer might, at one point in time, know the metaclasses of all ancestor classes of a new subclass, and, as a result, be able to explicitly derive an appropriate metaclass for the new class. Nevertheless, SOM must guarantee that this new class will still execute and perform correctly when any of its ancestor class's implementations are changed (which could even include specifying different metaclasses). Derived metaclasses allow SOM to make this guarantee. A SOM programmer need never worry about the problem of metaclass incompatibility; SOM does this for the programmer. Instead, explicit metaclasses can simply be used to "add in" whatever behavior is desired for a new class. SOM automatically handles anything else that is needed. Chapter 10 provides useful examples of such metaclasses. A SOM programmer should find numerous applications for the techniques that are illustrated there. | A programmer might, at one point in time, know the metaclasses of all ancestor classes of a new subclass, and, as a result, be able to explicitly derive an appropriate metaclass for the new class. Nevertheless, SOM must guarantee that this new class will still execute and perform correctly when any of its ancestor class's implementations are changed (which could even include specifying different metaclasses). Derived metaclasses allow SOM to make this guarantee. A SOM programmer need never worry about the problem of metaclass incompatibility; SOM does this for the programmer. Instead, explicit metaclasses can simply be used to "add in" whatever behavior is desired for a new class. SOM automatically handles anything else that is needed. Chapter 10 provides useful examples of such metaclasses. A SOM programmer should find numerous applications for the techniques that are illustrated there. | ||
==Inheritance== | ==Inheritance== | ||
==Method Resolution== | ==Method Resolution== |
Revision as of 03:47, 28 August 2020
Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation
This chapter begins with a more in-depth discussion of SOM concepts and the SOM run-time environment than was appropriate in Tutorial for Implementing SOM Classes. Subsequent sections then provide information about completing an implementation template file, updating the template file, compiling and linking, packaging classes in libraries, and other useful topics for class implementors. During this process, you can refer to Chapter 4, "SOM IDL and the SOM Compiler," if you want to read the reference information or see the full syntax related to topics discussed in this chapter. The current chapter ends with topics describing how to customize SOMobjects execution in various ways.
The SOM Run-Time Environment
The SOMobjects Developer Toolkit provides
- The SOM Compiler, used when creating SOM class libraries, and
- The SOM run-time library, for using SOM classes at execution time.
The SOM run-time library provides a set of functions used primarily for creating objects and invoking methods on them. The data structures and objects that are created, maintained, and used by the functions in the SOM run-time library constitute the SOM run-time environment.
A distinguishing characteristic of the SOM run-time environment is that SOM classes are represented by run-time objects; these objects are called class objects. By contrast, other object-oriented languages such as C++ treat classes strictly as compile-time structures that have no properties at run time. In SOM, however, each class has a corresponding run-time object. This has three advantages: First, application programs can access information about a class at run time, including its relationships with other classes, the methods it supports, the size of its instances, and so on. Second, because much of the information about a class is established at run time rather than at compile time, application programs needn't be recompiled when this information changes. Finally, because class objects can be instances of user-defined classes in SOM, users can adapt the techniques for subclassing and inheritance in order to build object-oriented solutions to problems that are otherwise not easily addressed within an OOP context.
Run-time environment initialization
When the SOM run-time environment is initialized, four primitive SOM objects are automatically created. Three of these are class objects (SOMObject, SOMClass, and SOMClassMgr), and one is an instance of SOMClassMgr, called the SOMClassMgrObject. Once loaded, application programs can invoke methods on these class objects to perform tasks such as creating other objects, printing the contents of an object, freeing objects, and the like. These four primitive objects are discussed below.
In addition to creating the four primitive SOM objects, initialization of the SOM run-time environment also involves initializing global variables to hold data structures that maintain the state of the environment. Other functions in the SOM run-time library rely on these global variables.
For application programs written in C or C++ that use the language-specific bindings provided by SOM, the SOM run-time environment is automatically initialized the first time any object is created. Programmers using other languages must initialize the run-time environment explicitly by calling the somEnvironmentNew function (provided by the SOM run-time library) before using any other SOM functions or methods.
SOMObject class object
SOMObject is the root class for all SOM classes. It defines the essential behavior common to all SOM objects. All user-defined SOM classes are derived, directly or indirectly, from this class. That is, every SOM class is a subclass of SOMObject or of some other class derived from SOMObject. SOMObject has no instance variables, thus objects that inherit from SOMObject incur no size increase. They do inherit a suite of methods that provide the behavior required of all SOM objects.
SOMClass class object
Because SOM classes are run-time objects, and since all run-time objects are instances of some class, it follows that a SOM class object must also be an instance of some class. The class of a class is called a metaclass. Hence, the instances of an ordinary class are individuals (nonclasses), while the instances of a metaclass are class objects.
In the same way that the class of an object defines the "instance methods" that the object can perform, the metaclass of a class defines the "class methods" that the class itself can perform. Class methods (sometimes called factory methods or constructors) are performed by class objects. Class methods perform tasks such as creating new instances of a class, maintaining a count of the number of instances of the class, and other operations of a "supervisory" nature. Also, class methods facilitate inheritance of instance methods from parent classes. For information on the distinction between parent classes and metaclasses, see the section "Parent Class vs. metaclass," later in this chapter.
SOMClass is the root class for all SOM metaclasses. That is, all SOM metaclasses must be subclasses of SOMClass or of some metaclass derived from SOMClass. SOMClass defines the essential behavior common to all SOM class objects. In particular, SOMClass provides:
- Six class methods for creating new class instances: somNew, somNewNoInit, somRenew, somRenewNoInit, somRenewNoZero and somRenewNoInitNoZero.
- A number of class methods that dynamically obtain or update information about a class and its methods at run time, including:
- somInitMIClass, for implementing multiple inheritance from parent classes,
- somOverrideSMethod, for overriding inherited methods, and
- somAddStaticMethod and somAddDynamicMethod, for including new methods.
SOMClass is a subclass (or child) of SOMObject. Hence, SOM class objects can also perform the same set of basic instance methods common to all SOM objects. This is what allows SOM classes to be real objects in the SOM run-time environment. SOMClass also has the unique distinction of being its own metaclass (that is, SOMClass defines its own class methods).
A user-defined class can designate as its metaclass either SOMClass or another user-written metaclass descended from SOMClass. If a metaclass is not explicitly specified, SOM determines one automatically.
SOMClassMgr class object and SOMClassMgrObject
The third primitive SOM class is SOMClassMgr. A single instance of the SOMClassMgr class is created automatically during SOM initialization. This instance is referred to as the SOMClassMgrObject, because it is pointed to by the global variable SOMClassMgrObject. The object SOMClassMgrObject has the responsibility to
- Maintain a registry (a run-time directory) of all SOM classes that exist within the current process, and to
- Assist in the dynamic loading and unloading of class libraries.
For C/C++ application programs using the SOM C/C++ language bindings, the SOMClassMgrObject automatically loads the appropriate library file and constructs a run-time object for the class the first time an instance of a class is created. For programmers using other languages, SOMClassMgr provides a method, somFindClass, for directing the SOMClassMgrObject to load the library file for a class and create its class object.
Again, the primitive classes supplied with SOM are SOMObject, SOMClass, and SOMClassMgr. During SOM initialization, the latter class generates an instance called SOMClassMgrObject. The SOMObject class is the parent class of SOMClass and SOMClassMgr. The SOMClass class is the metaclass of itself, of SOMObject, and of SOMClassMgr, which are all class objects at run time. SOMClassMgr is the class of SOMClassMgrObject.
Parent class vs. metaclass
There is a distinct difference between the notions of "parent" (or base) class and "metaclass." Both notions are related to the fact that a class defines the methods and variables of its instances, which are therefore called instance methods and instance variables.
A parent of a given class is a class from which the given class is derived by subclassing. (Thus, the given class is called a child or a subclass of the parent.) A parent class is a class from which instance methods and instance variables are inherited. For example, the parent of class "Dog" might be class "Animal". Hence, the instance methods and variables introduced by "Animal" (such as methods for breathing and eating, or a variable for storing an animal's weight) would also apply to instances of "Dog", because "Dog" inherits these from "Animal", its parent class. As a result, any given dog instance would be able to breath and eat, and would have a weight.
A metaclass is a class whose instances are class objects, and whose instance methods and instance variables (as described above) are therefore the methods and variables of class objects. For this reason, a metaclass is said to define class methods-the methods that a class object performs. For example, the metaclass of "Animal" might be "AnimalMClass", which defines the methods that can be invoked on class "Animal" (such as, to create Animal instances-objects that are not classes, like an individual pig or cat or elephant or dog).
Note: It is important to distinguish the methods of a class object (that is, the methods that can be invoked on the class object, which are defined by its metaclass) from the methods that the class defines for its instances.
To summarize: the parent of a class provides inherited methods that the class's instances can perform; the metaclass of a class provides class methods that the class itself can perform. These distinctions are further summarized below: The distinctions between parent class and metaclass are summarized in A class has both parent classes and a metaclass.
A class has both parent classes and a metaclass
Any class "C" has both a metaclass and one or more parent class(es).
- The parent class(es) of "C" provide the inherited instance methods that individual instances (objects "O{i}") of class "C" can perform. Instance methods that an instance "O{i}" performs might include (a) initializing itself, (b) performing computations using its instance variables, (c) printing its instance variables, or (d) returning its size. New instance methods are defined by "C" itself, in addition to those inherited from C's parent classes.
- The metaclass "M"defines the class methods that class "C" can perform. For example, class methods defined by metaclass "M" include those that allow "C" to (a) inherit its parents' instance methods and instance variables, (b) tell its own name, (c) create new instances, and (d) tell how many instance methods it supports. These methods are inherited from SOMClass. Additional methods supported by "M" might allow "C" to count how many instances it creates.
- Each class "C" has one or more parent classes and exactly one metaclass. (The single exception is SOMObject, which has no parent class.) Parent class(es) must be explicitly identified in the IDL declaration of a class. (SOMObject is given as a parent if no subsequently-derived class applies.) If a metaclass is not explicitly listed, the SOM run time will determine an applicable metaclass.
- An instance of a metaclass is always another class object. For example, class "C" is an instance of metaclass "M". SOMClass is the SOM-provided metaclass from which all subsequent metaclasses are derived.
A metaclass has its own inheritance hierarchy (through its parent classes) that is independent of its instances' inheritance hierarchies. For example, suppose a series of classes is defined (or derived), stemming from SOMObject. The child class (or subclass) at the end of this line ("C[2]") inherits instance methods from all of its ancestor classes (here, SOMObject and "C[1]"). An instance created by "C2" can perform any of these instance methods. In an analogous manner, a line of metaclasses can be defined, stemming from SOMClass. Just as a new class is derived from an existing class (such as SOMObject), a new metaclass is derived from an existing metaclass (such as SOMClass).
Parent classes and metaclasses each have their own independent inheritance hierrarchies
SOM-derived metaclasses
As previously discussed, a class object can perform any of the class methods that its metaclass defines. New metaclasses are typically created to modify existing class methods or introduce new class method(s). Chapter 8, "Metaclass Framework," discusses metaclass programming.
Three factors are essential for effective use of metaclasses in SOM:
- First, every class in SOM is an object that is implemented by a metaclass.
- Second, programmers can define and name new metaclasses, and can use these metaclasses when defining new SOM classes.
- Finally, and most importantly, metaclasses cannot interfere with the fundamental guarantee required of every OOP system: specifically, any code that executes without method-resolution error on instances of a given class will also execute without method-resolution errors on instances of any subclass of this class.
Surprisingly, SOM is currently the only OOP system that can make this final guarantee while also allowing programmers to explicitly define and use named metaclasses. This is possible because SOM automatically determines an appropriate metaclass that supports this guarantee, automatically deriving new metaclasses by subclassing at run time when this is necessary. As an example, suppose class "A" is an instance of metaclass "AMeta".
Assume that "AMeta" supports a method "bar" and that "A" supports a method "foo" that uses the expression "_bar( _somGetClass(somSelf ) )." That is, method "foo" invokes "bar" on the class of the object on which "foo" is invoked. For example, when method "foo" is invoked on an instance of class "A" (say, object "O{1}"), this in turn invokes "bar" on class "A" itself.
Now consider what happens if class "A" were subclassed by "B," a class that has the explicit metaclass "BMeta" declared in its SOM IDL source file (and assuming "BMeta" is not derived from "AMeta"). Also assume that object "O{2}" is an instance of class "B."
Recall that "AMeta" supports method "bar" and that class "A" supports method "foo" (which incorporates "bar" in its definition). Given the hierarchy described above, an invocation of "foo" on "O {2}" would fail, because metaclass "BMeta" does not support the "bar" method.
Example of Metaclass Incompatibility
There is only one way that "BMeta" can support this specific method-by inheriting it from "AMeta" ("BMeta" could introduce another method named "bar", but this would be a different method from the one introduced by "AMeta"). Therefore, in this example, because "BMeta" is not a subclass of "AMeta", "BMeta" cannot be allowed to be the metaclass of "B". That is, "BMeta" is not compatible with the requirements placed on "B" by the fundamental principle of OOP referred to above. This situation is referred to as metaclass incompatibility.
SOM does not allow hierarchies with metaclass incompatibilities. Instead, SOM automatically builds derived metaclasses when this is necessary. For example, SOM would create a "DerivedMeta" metaclass that has both "AMeta" and "BMeta" as parents. This ensures that the invocation of method "foo" on instances of class "B" will not fail, and also ensures that the desired class methods provided by "BMeta" will be available on class "B".
Example of a Derived Metaclass
There are three important aspects of SOM's approach to derived metaclasses:
- First, the creation of SOM-derived metaclasses is integrated with programmer-specified metaclasses. If a programmer-specified metaclass already supports all the class methods and variables needed by a new class, then the programmer-specified metaclass will be used as is.
- Second, if SOM must derive a different metaclass than the one explicitly indicated by the programmer (in order to support all the necessary class methods and variables), then the SOM-derived metaclass inherits from the explicitly indicated metaclass first. As a result, the method procedures defined by the specified metaclass take precedence over other possibilities (see the following section on inheritance and the discussion of resolution of ambiguity in the case of multiple inheritance).
- Finally, the class methods defined by the derived metaclass invoke the appropriate initialization methods of its parents to ensure that the class variables of its instances are correctly initialized.
As further explanation for the automatic derivation of metaclasses, consider the following multiple-inheritance example. Class "C" (derived from classes "A" and "B") does not have an explicit metaclass declaration in its SOM IDL, yet its parents "A" and "B" do. As a result, class "C" requires a derived metaclass. (If you still have trouble following the reasoning behind derived metaclasses, ask yourself the following question: What class should "C" be an instance of? After a bit of reflection, you will conclude that, if SOM did not build the derived metaclass, you would have to do so yourself.)
Multiple Inheritance requires Derived Metaclasses
In summary, SOM allows and encourages the definition and explicit use of named metaclasses. With named metaclasses, programmers can not only affect the behavior of class instances by choosing the parents of classes, but they can also affect the behavior of the classes themselves by choosing their metaclasses. Because the behavior of classes in SOM includes the implementation of inheritance itself, metaclasses in SOM provide an extremely flexible and powerful capability allowing classes to package solutions to problems that are otherwise very difficult to address within an OOP context.
At the same time, SOM is unique in that it relieves programmers of the responsibility for avoiding metaclass incompatibility when defining a new class. At first glance, this might seem to be merely a useful (though very important) convenience. But, in fact, it is absolutely essential, because SOM is predicated on binary compatibility with respect to changes in class implementations.
A programmer might, at one point in time, know the metaclasses of all ancestor classes of a new subclass, and, as a result, be able to explicitly derive an appropriate metaclass for the new class. Nevertheless, SOM must guarantee that this new class will still execute and perform correctly when any of its ancestor class's implementations are changed (which could even include specifying different metaclasses). Derived metaclasses allow SOM to make this guarantee. A SOM programmer need never worry about the problem of metaclass incompatibility; SOM does this for the programmer. Instead, explicit metaclasses can simply be used to "add in" whatever behavior is desired for a new class. SOM automatically handles anything else that is needed. Chapter 10 provides useful examples of such metaclasses. A SOM programmer should find numerous applications for the techniques that are illustrated there.
Inheritance
Method Resolution
Offset resolution
Name-lookup resolution
Dispatch-function resolution
Customizing Method Resolution
The four kinds of SOM methods
Static methods Nonstatic methods Dynamic methods Direct-call procedures
Implementing SOM Classes
The implementation template
Stub procedures for methods
Extending the implementation template
Accessing internal instance variables
Making parent method calls
Converting C++ classes to SOM classes
Running incremental updates of the implementation template file
Considerations to ensure that updates work
If you change the parents of a class...
Compiling and linking
Initializing and Uninitializing Objects
Initializer methods Declaring new initializers in SOM IDL Considerations re: 'somInit' initialization from earlier SOM releases Implementing initializers Selecting non-default ancestor initializer calls Using initializers when creating new objects Uninitialization Using 'somDestruct' A complete example Implementation code Customizing the initialization of class objects
Creating a SOM Class Library
General guidelines for class library designers
Types of class libraries
Building export files
Specifying the initialization function
Using Windows class libraries