home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-387-Vol-3of3.iso
/
s
/
somcls.zip
/
SOMCLASS.TXT
< prev
Wrap
Text File
|
1992-12-02
|
29KB
|
760 lines
CLASS OBJECTS IN SOM
by Nurcan Coskun and Roger Sessions
THIS ARTICLE WAS PUBLISHED IN THE PERSONAL SYSTEMS DEVELOPER
AND IS COPYRIGHT BY IBM
-----------------------------------------------------
Note:
1. Words inside angle brackets should be computer font.
2. Bold lines are indicated by "<--" in right margin.
Author contact information:
Nurcan Coskun AUSVM1(NURCAN) tieline 678-8073
Roger Sessions AUSMV1(SESSIONS) tieline 678-8085
-----------------------------------------------------
ABSTRACT
This article continues the discussion of SOM begun in the winter
issue of the Personal Systems Developer. In this article we look
at one of the advanced programming features of SOM: the class
object. The class object can be used to retrieve at run time a
great deal of useful information about classes, including the name
of the class and the methods supported. The class object also
knows how to instantiate objects of a given class. We give an
overview of the class object, and discuss the concept of metaclass.
A metaclass of a given class is the class of that class's class
object. We show the two important techniques for changing a
class's metaclass.
INTRODUCTION
In the winter issue of the Personal Systems Developer [Sessions and
Coskun, 92], we gave an introduction to the System Object Model
(SOM). SOM is a new methodology for creating object-oriented class
libraries introduced by IBM in OS/2 2.0. In our last article we
discussed the basic concepts of object-oriented programming:
developing classes and instantiating objects; deriving new classes
through inheritance; and resolving methods with polymorphism. We
also discussed the primary goals of SOM: multi-language support
(including both procedural and object-oriented languages); upwardly
compatible binaries; and object-oriented extensions to nonobject-
oriented languages.
In this article we are going to explore an advanced feature of
SOM: the class object. We assume that you are familiar with the
introductory concepts of our last article. If you find yourself
getting lost here, you should go back and revisit our winter
article.
GOALS OF SOM
SOM is a language neutral mechanism for developing object-oriented
class libraries. SOM consists of a run-time repository for class
information and a series of language bindings. Pictorially we can
describe SOM as shown in Figure 1.
................................................................
Figure 1. SOM Run-Time Class Repository and Language Bindings
+-+ +-----+ +-------+ +---+ +---------+
|C| |Cobol| |Fortran| |C++| |Smalltalk|
+-+ +-----+ +-------+ +---+ +---------+
| | | | |
V V V V V ... etc
+------------------------------------------+
| SOM Run-Time Class Repository |
+------------------------------------------+
| | | | | ... etc
V V V V V
+-+ +-----+ +-------+ +---+ +---------+
|C| |Cobol| |Fortran| |C++| |Smalltalk|
+-+ +-----+ +-------+ +---+ +---------+
................................................................
The box in Figure 1 with C pointing to the Run-Time Repository
represents a binding which allows classes to be defined in C and
added to the SOM run-time repository. The box from the run-time
repository to C is a binding which allows C to use classes from the
repository regardless of the language in which the classes were
originally written. When we say "use a class", we mean either
instantiating and invoking methods on objects of a class, or
deriving new classes using that class as a base class.
In the current version of OS/2 2.0, we support only the C
language bindings. The other languages shown in figure 1 are for
illustration only. This is neither a complete list of languages
under consideration nor a committed list. We are working with a
wide variety of language suppliers, both internally and externally,
to make this list as complete as possible.
As we discussed in the Winter issue, the C bindings allow
object-oriented programs to be developed much like they are
developed in C++. However in contrast to C++, SOM allows classes
to be used by a wide variety of languages as new language bindings
become available. SOM also solves other weaknesses in C++ when it
comes to developing class libraries. For a good introduction to
C++ and these library issues, see Class Construction in C and C++
[Sessions, 92].
CLASSES IN SOM
Lets reconsider a set of classes we first looked at in the winter
issue. We will start with the <Student> class. An object of type
<Student> knows how to set up its id and name. It knows how to
return its ID. Finally, it knows how to print itself. The class
definition file for <Student> is shown in Figure 2.
....................................................
Figure 2. <Student> class definition
include <somobj.sc>
class:
Student;
parent:
SOMObject;
data:
char id[16]; /* student id */
char name[32]; /* student name */
methods:
override somInit;
void setUpStudent(char *id, char *name);
void printStudentInfo();
char *getStudentType();
char *getStudentId();
....................................................
The class implementation file for <Student> is shown in Figure
3. Recall from our last article that most of the class
implementation file is produced automatically by the SOM compiler.
Those lines shown in bold face are the only lines the class
implementor actually needs to write.
................................................................
Figure 3. <Student> class implementation
(Programmer added lines shown in bold type)
#define Student_Class_Source
#include "student.ih"
SOM_Scope void SOMLINK somInit(Student *somSelf)
{
StudentData *somThis = StudentGetData(somSelf);
parent_somInit(somSelf);
_id[0] = _name[0] = '\0'; <--
}
SOM_Scope void SOMLINK setUpStudent(Student *somSelf,
char *id, char *name)
{
StudentData *somThis = StudentGetData(somSelf);
strcpy(_id, id); <--
strcpy(_name, name); <--
}
SOM_Scope void SOMLINK printStudentInfo(Student *somSelf)
{
StudentData *somThis = StudentGetData(somSelf);
printf("\n Id : %s \n", _id); <--
printf(" Name : %s \n", _name); <--
printf(" Type : %s \n", _getStudentType(somSelf));
}
SOM_Scope char * SOMLINK getStudentType(Student *somSelf)
{
StudentData *somThis = StudentGetData(somSelf);
static char *type = "student"; <--
return (type); <--
}
SOM_Scope char * SOMLINK getStudentId(Student *somSelf)
{
StudentData *somThis = StudentGetData(somSelf);
return (_id); <--
}
................................................................
Given this definition of <Student>, we can derive a new class,
say <GraduateStudent>, which is just like a student, but has
information about a thesis and a degree. The class definition file
for <GraduateStudent> is shown in Figure 4, and the class
implementation file is shown in Figure 5.
................................................................
Figure 4. <GraduateStudent> class definition file.
include "student.sc"
class:
GraduateStudent;
parent:
Student, local;
data:
char thesis[128]; /* thesis title */
char degree[16]; /* graduate degree type */
methods:
override somInit;
override printStudentInfo;
override getStudentType;
void setUpGraduateStudent(char *id, char *name,
char *thesis, char *degree);
................................................................
................................................................
Figure 5. <GraduateStudent> class implementation file.
(Programmer added lines shown in bold type.)
#define GraduateStudent_Class_Source
#include "graduate.ih"
SOM_Scope void SOMLINK somInit(GraduateStudent *somSelf)
{
GraduateStudentData *somThis =
GraduateStudentGetData(somSelf);
parent_somInit(somSelf);
_thesis[0] = _degree[0] = '\0'; <--
}
SOM_Scope void SOMLINK printStudentInfo(
GraduateStudent *somSelf)
{
GraduateStudentData *somThis =
GraduateStudentGetData(somSelf);
parent_printStudentInfo(somSelf);
printf(" Thesis : %s \n", _thesis); <--
printf(" Degree : %s \n", _degree); <--
}
SOM_Scope char * SOMLINK getStudentType(
GraduateStudent *somSelf)
{
GraduateStudentData *somThis =
GraduateStudentGetData(somSelf);
static char *type = "Graduate"; <--
return (type); <--
}
SOM_Scope void SOMLINK setUpGraduateStudent(
GraduateStudent *somSelf, char *id, char *name,
char *thesis, char *degree)
{
GraduateStudentData *somThis =
GraduateStudentGetData(somSelf);
_setUpStudent(somSelf,id,name); <--
strcpy(_thesis, thesis); <--
strcpy(_degree, degree); <--
}
................................................................
With these two classes defined and implemented, we can write
client code to instantiate and manipulate <Student>s and
<GraduateStudent>s. The program shown in Figure 6 instantiates two
<Student>s and two <GraduateStudent>s, initializes the objects with
data, and asks the objects to print themselves.
.................................................................
Figure 6. Client Program For <Student> and <GraduateStudent>
Program:
#include "graduate.h"
main()
{
Student *student1 = StudentNew();
Student *student2 = StudentNew();
GraduateStudent *grad1 = GraduateStudentNew();
GraduateStudent *grad2 = GraduateStudentNew();
_setUpStudent(student1,"120455","Joe Doe");
_setUpStudent(student2,"103606","Mary Smith");
_setUpGraduateStudent(grad1,"203230","Janet Brown",
"Parser Generators", "M.S.");
_setUpGraduateStudent(grad2,"240900","Mark Black",
"Code Optimization", "Ph.D.");
_printStudentInfo(student1);
_printStudentInfo(student2);
_printStudentInfo(grad1);
_printStudentInfo(grad2);
}
Output:
Id : 120455
Name : Joe Doe
Type : student
Id : 103606
Name : Mary Smith
Type : student
Id : 203230
Name : Janet Brown
Type : Graduate
Thesis : Parser Generators
Degree : M.S.
Id : 240900
Name : Mark Black
Type : Graduate
Thesis : Code Optimization
Degree : Ph.D.
.................................................................
CLASS OBJECTS
Every class with at least one instantiated object has exactly one
associated class object. Every instantiated object maintains a
pointer to its class object which it can return through the method
<somGetClass>. This method is inherited from <SOMObject>, a class
from which every class is derived, either directly or indirectly.
In the program shown in Figure 6, <student1> and <student2> are
both objects of class <Student>, and therefore both share the same
class object. Similarly <grad1> and <grad2> are both objects of
class <GraduateStudent>, and therefore both share another class
object, different than the class object shared by <student1> and
<student2>. This is shown pictorially in Figure 7.
..............................................................
Figure 7. Relationship between objects and class objects
+--------+ +-----+
|student1|---> +-------+ +---------------+ <---|grad1|
+--------+ |Student| |GraduateStudent| +-----+
|class | |class |
+--------+ |object | |object | +-----+
|student2|---> +-------+ +---------------+ <---|grad2|
+--------+ +-----+
..............................................................
Another way to access a class object is through the SOM
defined macro <_classname>. For <Student>, this macro is
<_Student>. This macro returns a pointer to the appropriate class
object, if it exists, or zero, if it does not exist. SOM
instantiates the class object the first time an object of its
controlled class is instantiated. The <_Student> will, therefore,
return 0 if and only if no <Student> objects have yet been
instantiated.
The class object is quite a useful object. The class of a
class object is either <SOMClass> or, as we will see later, some
class derived from <SOMClass>. There are a variety of methods
defined for <SOMClass>, and these are all documented in the class
definition file for <SOMClass>, <somcls.sc> and described in the
SOM documentation [SOM]. There are methods which can tell you the
size the class, the parent of the class, whether the class is
derived from some other class, and whether the class supports a
specific method, to name a few.
One of the <SOMClass> methods is <somGetName()>, which returns
a pointer to a character string containing the name of the class
for which this is the class object. For the <Student> class
object, this string would be "Student".
Another interesting <SOMClass> method is <somNew()>, which
returns a newly instantiated object of the appropriate class. For
the <Student> class object, the newly instantiated object would be
of the <Student> class. The method <somNew()> is always invoked to
instantiate new objects. Many programmers are not aware of this
because they use a macro wrapper of the form <classnameNew>, for
example, <StudentNew()>. This wrapper first makes sure the
appropriate class object exists, and then passes through the
instantiation request via <somNew()>.
In most cases, we would instantiate objects using the class
instantiation macro, say <StudentNew()>, but there might be cases
when we can access the class object but not the class instantiation
macro. One such example might be a function which accepts a
pointer to any object, and returns another newly instantiated
object of the same class. Figure 8 modifies the client program
shown in Figure 6 to make use of the <Student> and
<GraduateStudent> class objects and their <somGetClass()> and
<somNew()> methods.
.........................................................
Figure 8. Client Program Using Class Object
#include "graduate.h"
SOMAny *createObject(SOMAny *object)
{
return(_somNew(_somGetClass(object)));
}
main()
{
Student *student1 = StudentNew();
GraduateStudent *grad1 = GraduateStudentNew();
Student *student2;
GraduateStudent *grad2;
student2 = createObject(student1);
grad2 = createObject(grad1);
_setUpStudent(student1,"120455","Joe Doe");
_setUpStudent(student2,"103606","Mary Smith");
_setUpGraduateStudent(grad1,"203230","Janet Brown",
"Parser Generators", "M.S.");
_setUpGraduateStudent(grad2,"240900","Mark Black",
"Code Optimization", "Ph.D.");
_printStudentInfo(student1);
_printStudentInfo(student2);
_printStudentInfo(grad1);
_printStudentInfo(grad2);
}
.........................................................
Several of the <SOMClass> methods have passthrough methods
defined in <SOMObject>. In those cases, the actual code for the
passthrough methods just gets the object's class object, and passes
through the call using the appropriate <SOMClass> method. For
example, the <SOMObject> method <somGetClassName()> passes through
to the <SOMClass> method <somGetName()>. In this article we will
use either the <SOMObject> methods or the <SOMClass> passthrough,
depending on the point we are making.
THE CLASS OF THE CLASS OBJECT
Every object in SOM is associated with a class. The class of
<student> is <Student>, which we can see from this program:
#include "student.h"
main()
{
Student *student = StudentNew();
printf("student object is of <%s> class\n",
_somGetClassName(student));
}
which gives this output:
student object is of <Student> class
The class objects are also associated with a class. The class
of any class object is by default <SOMClass>. The class <SOMClass>
is, like all classes, derived from <SOMObject>, and therefore
supports all of the <SOMObject> methods. We can, for example,
verify the class of the <Student> class object as in the following
program
#include "student.h"
main()
{
Student *student = StudentNew();
SOMClass *studentClassObject;
studentClassObject = _somGetClass(student);
printf("student class object is of <%s> class\n",
_somGetClassName(studentClassObject));
}
giving this output:
student class object is of <SOMClass> class
We use the word metaclass to describe the class of an objects'
class object. Since the class of the student's class object is
<SOMClass>, we say the metaclass of <Student> is <SOMClass>. Don't
confuse class with metaclass. The class of <student> is <Student>.
The metaclass of <student> is <SOMClass>.
We can change the metaclass of a given class by a two stage
process:
1. Derive a new class from SOMClass, either directly or indirectly.
2. Tell the given class that its class object will be of the new
type through the metaclass directive.
There are at least three reasons one might want to change the
class of a class object. The first is to add new methods for
initializing new objects. Such methods are referred to as
constructor methods. The second is to modify how memory is
allocated for objects. The third is to keep track of global
information about the class, such as how many objects have been
instantiated.
Lets go through the steps necessary to change the class of the
<Student> class object from <SOMClass> to <StudentFactory>, or, in
other words, changing the metaclass of <student> from <SOMClass> to
<Studentfactory>. First, we define the <StudentFactory> to be
derived from <SOMClass>. This class will add one new method to
those found in <SOMClass>. The new method will be
<countObjects()>. We will also override the client invoked
<SOMClass> methods <somNew()>, which instantiates a new object. We
also need to override the <somInit()> method invoked on a newly
instantiated class object, to initialize the count to zero. The
class definition and implementation files for <StudentFactory> are
shown in Figure 9.
............................................................
Figure 9: Class Definition and Implementation Files for
<StudentFactory>
Class Definition File:
include <somobj.sc>
class:
StudentFactory;
parent:
SOMClass;
data:
int count;
methods:
override somInit;
override somNew;
int countObjects();
Class Implementation File:
#define StudentFactory_Class_Source
#include "countmet.ih"
SOM_Scope void SOMLINK somInit(StudentFactory *somSelf)
{
StudentFactoryData *somThis = StudentFactoryGetData(somSelf);
parent_somInit(somSelf);
_count = 0;
}
SOM_Scope SOMAny * SOMLINK somNew(StudentFactory *somSelf)
{
StudentFactoryData *somThis = StudentFactoryGetData(somSelf);
_count ++;
return (parent_somNew(somSelf));
}
SOM_Scope int SOMLINK countObjects(StudentFactory *somSelf)
{
StudentFactoryData *somThis = StudentFactoryGetData(somSelf);
return (int) _count;
}
............................................................
We now modify the student class definition files as shown in
Figure 10.
...........................................................
Figure 10: Newly Modified student class definition files
(Modified lines shown in bold face.)
include <somobj.sc>
include "countmet.sc"
class:
Student;
metaclass: <--
StudentFactory, local; <--
parent:
SOMObject;
data:
char id[16]; /* student id */
char name[32]; /* student name */
methods:
override somInit;
void setUpStudent(char *id, char *name);
void printStudentInfo();
char *getStudentType();
char *getStudentId();
..........................................................
No changes are needed to the class implementation file for
<Student>.
We can now use our new <StudentFactory> object to find out how
many student objects have been instantiated, as shown in this
program:
#include "student.h"
main()
{
Student *student1 = StudentNew();
Student *student2 = StudentNew();
SOMClass *studentClassObject;
_setUpStudent(student1,"120455","Joe Doe");
_setUpStudent(student2,"103606","Mary Smith");
studentClassObject = _somGetClass(student1);
printf("Student count: %d \n",
_countObjects(studentClassObject));
printf("student1 class object is of <%s> class\n",
_somGetClassName(studentClassObject));
}
giving this output:
Student count: 2
student1 class object is of <StudentFactory> class
IMPLIED METACLASS
Given a new class for class objects, we can have any number of
classes associated with the new class object. For example, instead
of <StudentFactory> we might have used the metaclass name
<CountFactory> to indicate greater generality, and then used this
metaclass whenever we want to keep track of total instantiated
objects.
Often we do not need such generality and we only intend for a
given class object to be associated with one class. For example,
the only change to the metaclass might be the addition of new
constructor methods which are only applicable to a specific class.
In such cases we can use a slight short cut to redefine the
metaclass. This short cut is called implied metaclass. The main
advantage of implied metaclasses is that the class definition file
for the metaclass can be folded into the class definition file for
the class it controls, and similarly for the class implementation
file.
The student implementation file rewritten to use the implied
metaclass is shown in Figure 11. The student implementation file
rewritten to use the implied metaclass is shown in Figure 12.
.............................................................
Figure 11. Student Class Definition using implied Metaclass
(Changes from original <Student> shown in bold face.)
include <somobj.sc>
class:
Student, classprefix = StudentClass_; <--
parent:
SOMObject;
data:
char id[16]; /* student id */
char name[32]; /* student name */
int count, class; <--
methods:
override somInit;
void setUpStudent(char *id, char *name);
void printStudentInfo();
char *getStudentType();
char *getStudentId();
override somInit, class; <--
override somNew, class; <--
int countObjects(), class; <--
............................................................
Notice there are two new keywords used to define implied
metaclasses. The keyword "classprefix" indicates a prefix used to
identify methods associated with the class object. The keyword
"class" is used to identify data members or methods associated with
the class object.
.............................................................
Figure 12. Student Class Implementation using implied Metaclass
(Programmer added lines shown in bold face.)
#define Student_Class_Source
#include "student.ih"
SOM_Scope void SOMLINK somInit(Student *somSelf)
{
StudentData *somThis = StudentGetData(somSelf);
parent_somInit(somSelf);
_id[0] = _name[0] = '\0'; <--
}
/* Other Student Methods, as before, followed by... */
#undef SOM_CurrentClass
#define SOM_CurrentClass SOMMeta
SOM_Scope void SOMLINK StudentClass_somInit(
M_Student *somSelf)
{
M_StudentData *somThis = M_StudentGetData(somSelf);
parent_somInit(somSelf);
_count = 0; <--
}
SOM_Scope SOMAny *SOMLINK StudentClass_somNew(
M_Student *somSelf)
{
M_StudentData *somThis = M_StudentGetData(somSelf);
_count ++; <--
return (parent_somNew(somSelf));
}
SOM_Scope int SOMLINK StudentClass_countObjects(
M_Student *somSelf)
{
M_StudentData *somThis = M_StudentGetData(somSelf);
return (int) _count; <--
}
............................................................
Our client program, shown again here, needs no changes:
#include "student.h"
main()
{
Student *student1 = StudentNew();
Student *student2 = StudentNew();
SOMClass *studentClassObject;
_setUpStudent(student1,"120455","Joe Doe");
_setUpStudent(student2,"103606","Mary Smith");
studentClassObject = _somGetClass(student1);
printf("Student count: %d \n",
_countObjects(studentClassObject));
printf("student1 class object is of <%s> class\n",
_somGetClassName(studentClassObject));
}
The output, which had looked like:
Student count: 2
student1 class object is of <StudentFactory> class
now looks like:
Student count: 2
student1 class object is of <M_Student> class
The difference in output reflects the system assigned name of the
metaclass.
SUMMARY
The class object is an important source of runtime information in
SOM. Virtually anything that one might want to know about a class
can be found out through one of the class object's methods. The
class object is itself a fully functional object, like any other
object in SOM. It has its own class, by default <SOMClass> which
can be modified using any of the normal class modification
techniques including inheritance and polymorphism.
The class object is an important programmer resource. It is
the source of runtime information about classes and gives the
ability to make runtime decisions about how objects will be
manipulated. Because the class object is a standard SOM object,
instantiated from a standard SOM Class, you have considerable
flexibility to redefine its characteristics as needed.
REFERENCES
[Sessions] Class Construction in C and C++: Object-Oriented
Programming Fundamentals, by Roger Sessions. Prentice Hall,
Englewood Cliffs, New Jersey, 1992.
[Sessions and Coskun] Object-Oriented Programming in OS/2 2.0, by
Roger Sessions and Nurcan Coskun. IBM Personal Systems Developer,
Winter, 1992, 107-120.
[SOM] OS/2 2.0 Technical Library System Object Model Guide and
Reference, IBM Doc 10G6309
ACKNOWLEDGEMENTS
SOM is the work of many people. Mike Conner developed the initial
idea and implementation, and continues to lead the overall design
of SOM. Andy Martin designed the SOM Class Interface Language, and
designed and implemented the class Interface compiler. Larry Raper
implemented many features of the run time library and porteed SOM
to OS/2. Larry Loucks provided close technical tracking and was
instrumental in focusing the effort. Other SOM developers include
Nurcan Coskun, Scott Danforth, Hari Madduri, Roger Sessions, Simon
Nash, and John Wang. The project is managed by Gerry Ronga.
BIOGRAPHIES
Roger Sessions, IBM Corporation, 11400 Burnett Road, Austin, TX
78758. He is the author of two books, Reusable Data Structures for
C, and Class Construction in C and C++ - Object-Oriented
Programming Fundamentals, as well as several articles. He is
working on object-oriented programming environments and previously
worked with high performance relational databases and object-
oriented storage systems. Mr. Sessions can be contacted at
roger@vnet.ibm.com. Mr. Sessions has a B.A. from Bard College and
an M.E.S. in Database Systems from the University of Pennsylvania.
Nurcan Coskun, IBM Corporation, 11400 Burnet Road, Austin, TX
78758. Her expertise is in integrated programming environments,
code generators, incremental compilers, interpreters, language
based editors, symbolic debuggers, application frameworks, and
language design. She is now working on object-oriented programming
environments and previously worked on the OS/2 Database Manager.
Dr. Coskun can be contacted at nurcan@ausvm1.vnet.ibm.com. Dr.
Coskun has a B.S. in Industrial Engineering from Middle East
Technical University, an M.S. and a Ph.D. in Computer Science from
the University of Missouri, Rolla.