SOM and Object REXX

by Dr. Willis Boughton

SOM and Object REXX
The System Object Model (SOM) probably is best known as the object framework for OS/2's Workplace Shell (WPS). OS/2 desktop objects are instances of WPS classes, which are subclasses of SOM classes. IBM stopped development of SOM in 1997, but it remains a fundamental programming framework for OS/2 (or eCS). Object REXX is the object-oriented version of REXX, OS/2's integrated scripting language. Object REXX is also available on Linux and Microsoft Windows and is a portable scripting language with many capabilities.

It is on OS/2, though, that Object REXX becomes most useful, because it is integrated with SOM. Specifically, a SOM class can be used directly by an Object REXX program. This capability is often used in Object REXX scripts that manipulate WPS objects. The integration of SOM and Object REXX is more that just a tool for manipulating the WPS, though. It has important consequences for system design and programmability. It means, essentially, that if a program is written as SOM classes, it is reprogrammable with Object REXX. The original program is just one way of using these classes; through Object REXX programming, you can make another program with these same classes. The concept of a "programming API" disappears. There are no custom "commands", there is only Object REXX programming. Furthermore, you can develop new functionality by subclassing the SOM classes in Object REXX; you can reuse the SOM classes and add functionality, in Object REXX. In practice there are limits to this approach, but the concept is elegant and powerful.

Scope of this Article
This article demonstrates use of SOM with Object REXX in a situation not involving the WPS. This article does not address the big issue of designing an entire program using SOM classes, to make it reprogrammable using Object REXX. Addressed is a much smaller but related issue: how to make C or C code usable from Object REXX. This article demonstrates development of a SOM class, Sdb, that "wraps" a commercial C class, so that it can be used by Object REXX programs. The C class is from the C/Database Toolchest (CDT) sold by Mix Software, Inc. CDT is a library for indexed databases and includes many features such as variable field types, variable record length, multiple indexes, and record locking. CDT was selected simply because I was using it; I have no association with Mix Software, Inc. If you want to get runnable code for the Sdb class in this article, you will need to purchase CDT. This article describes the development of the Sdb SOM class but is not a tutorial on SOM or Object REXX. For SOM, refer to the documentation in the Warp Developer's Toolkit, which you need installed to do SOM development. Another reference is the book, which has an outstanding presentation of SOM and C object orientation. Since the Sdb listing is lengthy, this article includes only samples; the complete source is available on Hobbes. [Editor: The source doesn't seem to be uploaded yet, but check shortly].

Before going into the development of the SOM wrapper class, it may be helpful to show what you can do with it. The following is an Object REXX program that uses Sdb: This program does the following:
 * Creates empty database "Example". Each database record consists of a string field named "Name" and an unsigned short integer field named "Number".
 * Creates database index "Index1" consisting of the Name field only.
 * Opens the database for shared read and write access.
 * Adds two records with different field values.
 * Retrieves each record using the index and for each record writes the Name and Number fields to the screen.
 * Closes the database.

CDT Summary
The CDT manual fully explains design and use of a CDT database. A quick summary is necessary for understanding the SOM wrapper class. A CDT database consists of a datafile and index file. Initially an index file has only a physical index, but you can add any number of field-based indexes, and each index can involve multiple fields. Your program selects what index is in use. The field types supported are string, case-sensitive string, short int, unsigned short int</tt>, long int</tt>, float</tt>, double</tt>, and fixed-length binary</tt>. A field is defined by a string that includes the field name and type, e.g., "cost,u" defines a unsigned integer field named "cost". Records are variable length. CDT provides C and C APIs. In C, a CDT database is an instance of the ISAM class (isam.hpp header file). This class has many methods; the CDT ISAM Method Summary Table summarizes a few. Basically, to use CDT you identify fields and indexes by name and manipulate fields by type, e.g., string</tt>, int</tt>, or float</tt>. To add, delete, and retrieve records, you work with a memory buffer and the "current record". Each index has its own current record. To add a record, for example, you set the field values in the buffer, then add the buffer, as a record, to the database. This makes that record the current one. To retrieve a record, you select the index, set the field values, find the matching record, which makes it the current record, then get it into the buffer. Error handling is by status return, not exceptions.

SOM Development Process Summary
To develop a SOM class, you need the Warp Developer's Toolkit and a C or C compiler. I use VisualAge C V3. I do not know if there any SOM or CDT issues with other compilers. If you use VisualAge C V3, make sure you do not use the SOM toolkit that comes with it, since it is out of date. The easiest approach is to install the compiler first. The SOM development process involves the following steps (see the Toolkit SOM Programming Guide):
 * 1) Define each SOM class in Interface Definition Language (IDL).
 * 2) With the SOM compiler, compile each IDL file to generate a C or C stub for each class and to add the class to your SOM Interface Repository (IR).
 * 3) Fill in the class stubs with your code and compile your C or C classes.
 * 4) Link your classes into a DLL.
 * 5) Repeat these steps as necessary. When you rerun the SOM compiler, it updates the output file only if necessary. For example, if you add a new method to the IDL, the stub for that method will be added to the C or C source file but the rest of the file will be unchanged.

The main SOM class design issue related to Object REXX is passing of method parameters and returns. A SOM method parameter is in</tt> (input only), out</tt> (output only), or inout</tt> (input and output). As long as you use only in</tt> parameters, which is the "pure" object orientation approach, many IDL types can be accessed directly in Object REXX (see the Object REXX Programming Guide). Probably the most commonly used are void</tt>, short</tt>, ushort</tt>, long</tt>, <tt>ulong</tt>, <tt>float</tt>, <tt>double</tt>, <tt>boolean</tt>, <tt>char</tt>, <tt>string</tt>, <tt>octet</tt>. An IDL <tt>struct</tt> can be passed to and from Object REXX, but the <tt>struct</tt> elements cannot be accessed in Object REXX. So, when designing a SOM class for use with Object REXX, you must use <tt>void</tt>, <tt>short</tt>, etc. parameters and returns if you want to directly use the value in Object REXX. If the parameter is just a passthru, you can use a <tt>struct</tt>. In the case of a SOM wrapper class, e.g., <tt>Sdb</tt>, your SOM methods must do conversions as necessary between the IDL parameters and those required by the code being wrapped. For example, the ISAM <tt>create</tt> method has a parameter that is an array of <tt>char*</tt> pointers (see the CDT ISAM Method Summary Table). The array strings are not accessible in Object REXX directly. The Sdb create method therefore replaces the <tt>char*</tt> array parameter with a single <tt>string</tt>, which must contain the individual strings separated by delimiters. The <tt>Sdb create</tt> method converts this <tt>string</tt> parameter into the <tt>char*</tt> array required by the ISAM class.

The following sections summarize SOM development for the CDT wrapper.

CDT Wrapper IDL
Shown below is the IDL for the <tt>Sdb</tt> class. Most but not all CDT ISAM methods are wrapped, e.g., ones to display error messages are not. A couple of methods are added, e.g., a method to import records from a delimited ASCII file. There is an <tt>enum</tt> for ISAM status. <tt>static</tt> CDT ISAM methods are wrapped using the SOM Metaclass <tt>SdbMeta</tt>. Its IDL is shown after the <tt>Sdb</tt> IDL. The CDT SOM wrapper therefore consists of two SOM classes, <tt>Sdb</tt> and <tt>SdbMeta</tt>. I put each class in a separate IDL file, but they could be combined in one.

SOM Compilation
SOM compilation involves two actions: generating the stubs and generating the Interface Repository. You can do either first; here I will start with the stubs. For each IDL file, you can generate C (<tt>.c</tt>) or C (<tt>.cpp</tt>) stubs by selecting the compiler emitter. Do not mix C and C stubs. I used C, for which the corresponding emitter is <tt>xh;xih;xc</tt>. You can specify the emitter in more than one way; probably the easiest is with <tt>SMEMIT=xh;xih;xc</tt> in <tt>config.sys</tt> (see the SOM Programming Guide). For each class the stub will contain an empty C method for each IDL method. If you have specified the emitters with <tt>SMEMIT</tt>, the compilation command is: sc -u nnn.idl

where <tt>nnn</tt> is the class name. The -u parameter specifies updating of the existing stub, if any. You can specify more than one IDL file. By default the stubs are output to the current directory. To generate the C stubs for <tt>Sdb</tt> and <tt>SdbMeta</tt>, the command is: sc -u sdb.idl sdbmeta.idl

Absence of an error message indicates successful compilation. The following is the start of the <tt>sdb.cpp</tt> stub: Each SOM class must also be compiled into an Interface Repository, using the compiler's <tt>ir</tt> emitter. To specify your IR, first add the full pathname for file <tt>nnn.ir</tt> to the end of the <tt>SOMIR</tt> variable in <tt>config.sys</tt>, where <tt>nnn</tt> is the name of one of your SOM classes. So for <tt>Sdb</tt>, add <tt>sdb.ir</tt> (full pathname) to the end of <tt>SOMIR</tt>. The SOM compiler updates only the last IR in <tt>SOMIR</tt>, so your IR file must be the last one. Probably the easiest way to run the <tt>ir</tt> emitter is with the command: sc -sir nnn.idl

where <tt>nnn</tt> again is the SOM class name. So for the CDT wrapper classes the command would be: sc -sir sdb.idl sdbmeta.idl

Completing the SOM Stubs
There is nothing specific to Object REXX in completing the SOM stubs. You code what needs to be done. The completed <tt>Sdb create</tt> method is shown below. The completed <tt>sdb.cpp</tt> file has about 1000 lines of code, excluding comments. <tt>sdb.cpp</tt> uses the IBM Open Class Library, so it would have to be modified if Open Class were not used. SOM_Scope boolean SOMLINK create(Sdb *somSelf, Environment *ev,           string dbName, string fieldSpecs, short blockSize) { SdbData *somThis = SdbGetData(somSelf); SdbMethodDebug("Sdb","create"); IString specsCopy(fieldSpecs); uint numFields = unpackTokens(specsCopy.operator char*,                     FIELDDELIM, SdbFields); if (numFields > MAXFIELDS) { somThis->iResult = Sdb_TOOMANYFIELDS  ERROROFFSET; } else if (numFields == 0) { somThis->iResult = Sdb_NOFIELDS  ERROROFFSET; } else { ISAM* db = (ISAM*) somThis->iDb; if (blockSize == 0) blockSize = DEFAULT_BLKSIZE; somThis->iReadOnly = false; somThis->iShare = false; strcpy(somThis->iName, dbName); somThis->iResult = db->create(dbName, SdbFields, blockSize); if (somThis->iResult == I_OK) { somThis->iResult = somSelf->selectIndex(ev,                          PHYSICALINDEXNAME); }  }   return (somThis->iResult == I_OK); }

C Compiling and Linking
There is nothing specific to SOM in compiling the wrapper classes. Make sure all Toolkit include directories are in the include path. The Toolkit installation does this automatically. You must link a SOM class into a DLL, and you can have any number of classes in a DLL. You can use the SOM compiler's <tt>def</tt> emitter to generate the export list, e.g.: sc -sdef sdb.idl

This command outputs the sdb.def file, containing the following: LIBRARY sdb INITINSTANCE DESCRIPTION 'Sdb Class Library' PROTMODE DATA MULTIPLE NONSHARED LOADONCALL EXPORTS SdbCClassData SdbClassData SdbNewClass

To put multiple classes in a DLL, run the <tt>def</tt> emitter multiple times and use a text editor to combine the def files into one. You also need to add the <tt>SOMInitModule</tt> export by hand. The full def file for the <tt>Sdb</tt> DLL is: LIBRARY sdb INITINSTANCE DESCRIPTION 'Sdb Class Library' PROTMODE DATA MULTIPLE NONSHARED LOADONCALL EXPORTS SOMInitModule SdbCClassData SdbClassData SdbNewClass SdbMetaCClassData SdbMetaClassData SdbMetaNewClass

When linking, you must include somtk.lib and os2386.lib as well the libraries are required by your code specifically. For the CDT wrapper, these libraries are isam.cpp, isamcpp.lib and cdt.lib. If all files are in the link path, the following command links the <tt>Sdb</tt> DLL with VisualAge C V3: ilink /packd /packc /exepack /align:16 /noi /out:sdb.dll sdb.obj sdbmeta.obj isam.lib cbt.lib isamcpp.lib somtk.lib os2386.lib sdb.def

Object REXX Programming
You must do two things to use a SOM class in Object REXX: <tt> ::class Sdb external "SOM nnn" </tt> where <tt>nnn</tt> is the SOM class name.
 * Put the SOM DLL in the <tt>LIBPATH</tt>
 * In your Object REXX program, import the SOM class with the statement

See the example program at the start of this article. This program creates in the current directory the file <tt>example.db</tt>, which is the CDT database file, and file <tt>example.idx</tt>, the database index file. The program output is: Name1 1 Name2 2

Another example Object REXX program using <tt>Sdb</tt> is:

This program reads the text file "data", adds its contents to a CDT database as a binary field, retrieves the record and displays the binary field, and displays database information. If the data file contains Data 1 Data 2 Data 3 Data 4 Data 5

the program output is 40 Data 1 Data 2 Data 3 Data 4 Data 5 name1 2 2 name,s;binary,b100; name,s 100 1 name,s

In Object REXX you can subclass a SOM class, i.e., you can subclass <tt>Sdb</tt> to create a custom database class. For example, say you want a CDT database with the following capabilities:
 * Whenever a database is created, a long int field named "id" is automatically included.
 * There is a method that returns the number of records.

An Object REXX class with these capabilities is:

C Programming
The SOM wrapper class can be used in C as well as Object REXX. You must use SOM, however, not just C. Listed below is a C program that does exactly what the Object REXX program at the start of this article does, with one exception: the C code accesses the <tt>SdbMeta class</tt>, to demonstrate how it is done in SOM.

Conclusion
SOM code is in some ways more complex than C code. Using SOM, though, makes C classes directly usable by Object REXX on OS/2, which is an advantage for system programmability.