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.
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:
/* REXX */ db = .Sdb~new if (db~create("Example", "name,s;number,u", 512)) then do db~createIndex("Index1", "name,s") db~setStrField("Name", "Name1") db~setUShortField("Number", 1) db~addrec db~setStrField("Name", "Name2") db~setUShortField("Number", 2) db~addrec db~setStrField("name", "Name1") db~findRec db~getrec(0) say db~strField("name") db~UShortField("number") db~setStrField("name", "Name2") db~findRec db~getrec(0) say db~strField("name") db~UShortField("number") db~close end exit ::class Sdb external "SOM Sdb"This program does the following:
Method | Description |
int addRec() | Adds a record from the memory buffer. |
int create(Str name, StrList fieldDescriptions) | Creates a database. |
int delRec() | Deletes the current record for the selected index. |
int findRec() | Sets the current record for the selected index to match the memory buffer fields. |
int getField(Str name, float& value) | Gets a float field value from the memory buffer. There are many get methods, for the various field types. |
int getRec(int lock) | Gets the current record for the selected index into the memory buffer. lock specifies the record locking. |
int index(Str name, StrList fields) | Creates a named index involving the specified fields. |
int modRec() | Modifies the current record for the selected index to match the memory buffer fields. |
open(Str name, int mode) | Opens the specified database. mode is shared or read only. |
int selectIndex(Str name) | Selects an index. |
int setField(Str name, int i) | Sets an int field value in the memory buffer. There are many set methods, for the various field types. |
* typedef const char* Str; typedef const Str* StrList; |
The following sections summarize SOM development for the CDT wrapper.
// IDL for Sdb class #ifndef _Sdb_ #define _Sdb_ #include#include #include "sdbmeta.idl" interface Sdb : SOMObject { enum Status {OK, ERROR, NOLOCK, ATINDEXSTART, ATINDEXEND, FOUND, NOTFOUND, TOOMANYFIELDS, TYPEMISMATCH, NOFIELDS}; boolean create(in string dbName, in string fieldSpecs, in short blockSize); boolean open(in string dbName, in boolean readOnly, in boolean share); boolean close(); boolean destroy(in string dbName); boolean flush(); boolean lock(in boolean readPermitted); boolean unlock(); boolean copy(in string curName, in string copyName, in short blockSize); string fileName(); Status lastStatus(); boolean addRec(); boolean getRec(in boolean lock); boolean delRec(); boolean findHeadRec(); boolean findNextRec(); boolean findPrevRec(); boolean findTailRec(); boolean findRec(); boolean modRec(); boolean importRecs(in string fileName, in char delimiter); boolean createIndex(in string indexName, in string indexFields); boolean selectIndex(in string indexName); boolean deleteIndex(in string indexName); short numFields(); string fieldSpec(in string fieldName); string fieldSpecs(); short binaryFieldLen(in string fieldName); short numIndexes(); short numIndexFields(); string indexSpec(in string indexName); boolean clearFields(); boolean setStrField(in string fieldName, in string value); boolean setShortField(in string fieldName, in short value); boolean setUShortField(in string fieldName, in unsigned short value); boolean setLongField(in string fieldName, in long value); boolean setFloatField(in string fieldName, in float value); boolean setDoubleField(in string fieldName, in double value); boolean setBinaryField(in string fieldName, in string value); string strField(in string fieldName); short shortField(in string fieldName); unsigned short ushortField(in string fieldName); long longField(in string fieldName); float floatField(in string fieldName); double doubleField(in string fieldName); string binaryField(in string fieldName); #ifdef __SOMIDL__ implementation { void* iDb; short iResult; short iCurIndex; short iLastLockType; boolean iReadOnly; boolean iShare; char iName[80]; char iCurIndexName[80]; releaseorder: create, open, close, destroy, lastStatus, fieldNum, setStrField, setShortField, setUShortField, setLongField, addRec, getRec, setFloatField,, strField, shortField, delRec, ushortField, longField, floatField, doubleField, findHeadRec, findNextRec, findPrevRec, createIndex, selectIndex, deleteIndex, findRec, findTailRec, flush, numFields, fieldSpec, fieldSpecs, fileName, numIndexes, numIndexFields, indexSpec, setDoubleField, lock, unlock, clearFields, modRec, copy, setBinaryField, binaryField, binaryFieldLen, importRecs; metaclass = SdbMeta; majorversion = 0; minorversion = 0; dllname = "sdb.dll"; somDefaultInit: override, init; somDestruct: override; }; #endif }; #endif // IDL for SdbMeta class #include #include interface Sdb; interface SdbMeta : SOMClass { void setLocking(in short tries, in long delay, in long timeout); short lockTries(); long lockDelay(); long lockTimeout(); boolean setBuffers(in short numBufs, in short bufSize); boolean renameDb(in string oldName, in string newName); #ifdef __SOMIDL__ implementation { short iBufferCount; short iBufferSize; short iLockTryCount; long iLockDelayMs; long iLockTimeoutMs; releaseorder: lockTries, lockDelay, lockTimeout, setLocking, setBuffers, renameDb; majorversion = 0; minorversion = 0; dllname = "sdb.dll"; somDefaultInit: override, init; somDestruct: override; }; #endif };
sc -u nnn.idlwhere nnn 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 Sdb and SdbMeta, the command is:
sc -u sdb.idl sdbmeta.idlAbsence of an error message indicates successful compilation. The following is the start of the sdb.cpp stub:
/** This file was generated by the SOM Compiler and Emitter Framework. * Generated using template emitter: * SOM Emitter emitxtm: 2.23.1.9 */ #ifndef SOM_Module_sdb_Source #define SOM_Module_sdb_Source #endif #define Sdb_Class_Source #define SdbMeta_Class_Source #include "sdb.xih" SOM_Scope boolean SOMLINK create(Sdb *somSelf, Environment *ev, string dbName, string fieldSpecs, short blockSize) { SdbData *somThis = SdbGetData(somSelf); SdbMethodDebug("Sdb","create"); /* Return statement to be customized: */ { boolean retVal; return (retVal); } } ... more stubs followEach SOM class must also be compiled into an Interface Repository, using the compiler's ir emitter. To specify your IR, first add the full pathname for file nnn.ir to the end of the SOMIR variable in config.sys, where nnn is the name of one of your SOM classes. So for Sdb, add sdb.ir (full pathname) to the end of SOMIR. The SOM compiler updates only the last IR in SOMIR, so your IR file must be the last one. Probably the easiest way to run the ir emitter is with the command
sc -sir nnn.idlwhere nnn again is the SOM class name. So for the CDT wrapper classes the command would be:
sc -sir sdb.idl sdbmeta.idl
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); }
sc -sdef sdb.idlThis command outputs the sdb.def file, containing the following:
LIBRARY sdb INITINSTANCE DESCRIPTION 'Sdb Class Library' PROTMODE DATA MULTIPLE NONSHARED LOADONCALL EXPORTS SdbCClassData SdbClassData SdbNewClassTo put multiple classes in a DLL, run the def emitter multiple times and use a text editor to combine the def files into one. You also need to add the SOMInitModule export by hand. The full def file for the Sdb DLL is:
LIBRARY sdb INITINSTANCE DESCRIPTION 'Sdb Class Library' PROTMODE DATA MULTIPLE NONSHARED LOADONCALL EXPORTS SOMInitModule SdbCClassData SdbClassData SdbNewClass SdbMetaCClassData SdbMetaClassData SdbMetaNewClassWhen 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 Sdb 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
Name1 1 Name2 2Another example Object REXX program using Sdb is:
/* REXX */ db = .Sdb~new if (db~create("example", "name,s;binary,b100", 512) > 0) then do BinaryFile = .stream~new("data") BinaryData = BinaryFile~charin(1, BinaryFile~chars) say BinaryData~length db~createIndex("index1", "name,s") db~selectIndex("index1") db~setStrField("name", "name1") db~setBinaryField("binary", BinaryData) db~addRec db~findRec db~getRec(0) ABinary = db~binaryField("binary") say ABinary say db~strField("Name") say db~numFields say db~numIndexes say db~fieldSpecs say db~fieldSpec("name") say db~binaryFieldLen("binary") say db~numIndexFields say db~indexSpec('index1') db~close end exit ::class Sdb external "SOM Sdb"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 5the 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,sIn Object REXX you can subclass a SOM class, i.e., you can subclass Sdb to create a custom database class. For example, say you want a CDT database with the following capabilities:
class MyDb public subclass Sdb ::method create use arg dbName, fieldSpecs, blocksize fieldSpecs = fieldSpecs||';id,l' return self~create:super(dbName, fieldSpecs, blockSize) ::method numRecs count = 0 if (self~findHeadRec) then do count = count + 1 do while (self~findNextRec) count = count + 1 end end return count class Sdb external "SOM Sdb"
#include#include int main(int argc, char ** argv) { Environment *ev = somGetGlobalEnvironment(); Sdb* db = new Sdb; Sdb_Status status = db->create(ev, "example", "name,s;number,u", 0); if (status == Sdb_OK) { db->createIndex(ev, "Index1", "name,s"); db->setStrField(ev, "name", "Name1"); db->setUShortField(ev, "number", 1); db->addRec(ev); db->setStrField(ev, "name", "Name2"); db->setUShortField(ev, "number", 2); db->addRec(ev); db->setStrField(ev, "name", "Name1"); db->findRec(ev); db->getRec(ev, 0); somPrintf("name=%s number=%u\n", db->strField(ev, "name"), db->ushortField(ev, "number")); db->setStrField(ev, "name", "Name2"); db->findRec(ev); db->getRec(ev, 0); somPrintf("name=%s number=%u\n", db->strField(ev, "name"), db->ushortField(ev, "number")); /* Access metaclass object, for demonstration only */ somPrintf("lockTries=%i\n", SdbClassData.classObject->lockTries(ev)); db->close(ev); } exit(0); }
This article is courtesy of www.os2ezine.com. You can view it online at http://www.os2ezine.com/20040816/page_8.html.