The use of SOM to mirror the C++ object
Written by Gordon Zeglinski
Contents
Introduction
A few issues back, we looked at encapsulating the profile file API in C++. In this issue, we will use SOM to mirror the C++ object.
Recall the C++ call definition:
class ProfileFile{ APIRET Error; HINI hini; HAB hab; PSZ FileName; char Close; public: ProfileFile(PSZ name,HAB hb); ProfileFile(HINI H); ~ProfileFile(); APIRET GetError(){return Error;} HINI GetHandle(){return hini;} void WriteString(PSZ App,PSZ Key, PSZ String); void WriteData(PSZ App, PSZ Key, void * Buff, ULONG BLen); ULONG GetDataSize(PSZ App,PSZ Key); void GetData(PSZ App,PSZ Key,void * Buff, ULONG & BufMax); ULONG GetString(PSZ App,PSZ Key,PSZ Buff,ULONG BufMax,PSZ DefArg=NULL); LONG GetInt(PSZ App,PSZ Key,LONG DefArg=0); void WriteInt(PSZ App,PSZ Key,LONG Num); PRFPROFILE Querry(); };
The above definition will be mirrored using SOM.
The SOM Profile Class Definition
Following is the SOM IDL definition for the profile class and its metaclass. This listing is from the file INIObjs.idl. We'll look at this in detail later.
#ifndef __INIObj_h__ #define __INIObj_h__ #include <somcls.idl> interface M_INIObj; interface INIObj : SOMObject { typedef unsigned long ULONG; typedef sequence<char> buffType; attribute long Error; // Stores the Error Data attribute long DidCreate; // Flag used to indicate whether we should close the ini file unsigned long GetHandle(); void SetHandle(in unsigned long H); void WriteString(in string App,in string Key, in string String); // write a string void WriteData(in string App, in string Key,in buffType Buff,in ULONG BLen); // write the Buffer void WriteInt(in string App,in string Key,in long Num); // write an "int" ULONG GetDataSize(in string App,in string Key); // return the length of the data void GetData(in string App,in string Key,in buffType Buff, inout ULONG BufMax); // return the data in Buff ULONG GetString(in string App,in string Key,in buffType Buff,in ULONG BufMax, \ in string DefArg); // return the String in Buff, defaults to DefArg if App/Key not present ULONG GetStringNoDef(in string App,in string Key,in buffType Buff,in ULONG BufMax); // return the String in Buff long GetInt(in string App,in string Key,in long DefArg); // return the "int" in App/Key or the DefArg if App/Key not present long GetIntNoDef(in string App,in string Key); // return the "int" in App/Key #ifdef __SOMIDL__ implementation { //we use an unsigned long because that's what OS/2 uses for a HIHI unsigned long INIHandle; releaseorder: GetHandle,SetHandle,_get_Error,_set_Error,_set_DidCreate,_get_DidCreate, \ WriteString,WriteData,WriteInt,GetDataSize,GetData,GetString,GetStringNoDef, \ GetInt,GetIntNoDef; callstyle=oidl; filestem = iniobjs; metaclass = M_INIObj; somInit: override; // initialize instance variables to 0 somUninit:override; // close INI file if we opened it passthru C_xh_after= "#define INCL_WINSHELLDATA" \ "#include <os2.h>"; }; #endif /* __SOMIDL__ */ }; interface M_INIObj : SOMClass { INIObj INICreate(in unsigned long hab,in string FileName); // create an INI file with the given Name INIObj CreateFromOpen(in unsigned long HINI); INIObj GetUserINI(); INIObj GetSystemINI(); #ifdef __SOMIDL__ implementation { INIObj UserINI; //the user INI file INIObj SystemINI; //the System INI file releaseorder: INICreate,CreateFromOpen,GetUserINI,GetSystemINI; callstyle=oidl; filestem = iniobjs; functionprefix=M_; somInitMIClass: override; somInit: override; // just a test for parent call macros somUninit:override; // clean up any instances we make }; #endif /* __SOMIDL__ */ }; #endif /* __INIObj_h__ */
Pretty impressive stuff huh?
The M_INIObj Metaclass
As mentioned in the last issue, the metaclass provides the constructor and class methods. We provide 2 constructors, CreateFromOpen and INICreate. INICreate takes the HAB and the name of the profile file; its implementation follows.
SOM_Scope INIObj* SOMLINK M_INICreate(M_INIObj *somSelf, unsigned long hab, string FileName) { /* M_INIObjData *somThis = M_INIObjGetData(somSelf); */ M_INIObjMethodDebug("M_INIObj","M_INICreate"); /* Return statement to be customized: */ { INIObj* retVal; return (retVal); } // call the OS/2 API function to open the profile function HINI hini=PrfOpenProfile(hab,FileName); //create the INIObj instance INIObj* INI=somSelf->somNew(); // if hini is non zero, then we opened the file successfully if(hini){ INI->_set_Error(0); //clear the instance variable Error INI->SetHandle(hini); //set the profile handle INI->_set_DidCreate(1); //set the flag indicating that the file should } //be closed when the instance is destroyed // otherwise an error occurred else{ INI->_set_Error(1); INI->SetHandle(hini); INI->_set_DidCreate(0); } //return the instance return INI;</font> }
In the above snippet, the bold text is the code added to the stubs created by the SOM compiler. The constructor CreateFromOpen is provided to create an instance of an INIObj give a handle of an already open profile file.
The implementation section of the M_INIObj metaclass contains the declaration of two class variables, UserINI and SystemINI. These variables will be used to store an instance of INIObj for the System and User INI files. They are accessed by the class methods GetUserINI and GetUserINI respectively. Below is the implementation for the GetUserINI method.
SOM_Scope INIObj* SOMLINK M_GetUserINI(M_INIObj *somSelf) { M_INIObjData *somThis = M_INIObjGetData(somSelf); M_INIObjMethodDebug("M_INIObj","M_GetUserINI"); /* Return statement to be customized: */ { INIObj* retVal; return (retVal); } //check to see if the UserINI variable is NULL if(!somThis->UserINI){ //if it is, then create an instance of the INIObj class somThis->UserINI=somSelf->somNew(); //and set it handle to that of the user profile file somThis->UserINI->SetHandle(HINI_USERPROFILE); //we didn't create the file so we won't close it somThis->UserINI->_set_DidCreate(0); } //return instance of the user ini file. return somThis->UserINI;</font> }
The above snippet illustrates how to access class data using somThis. Now to take care of a few details. Notice that we tested UserINI against 0 to see if we created an instance before. How do we know it will be zero if we previously didn't create an instance of INIObj for it? Also note that we create a new instance of INIObj. Where does it get deleted? For the answer to these two questions, we look at the implementation section of the metaclass again. We see:
somInit:override; // just a test for parent call macros somUninit:override; // clean up any instances we make
These methods take care of the initialization and destruction of the metaclass data members. Their implementation follows.
SOM_Scope void SOMLINK M_somInit(M_INIObj *somSelf) { M_INIObjData *somThis = M_INIObjGetData(somSelf); M_INIObjMethodDebug("M_INIObj","M_somInit"); M_INIObj_parent_SOMClass_somInit(somSelf); // initialize the variables to 0 somThis->UserINI=0; somThis->SystemINI=0; } SOM_Scope void SOMLINK M_somUninit(M_INIObj *somSelf) { M_INIObjData *somThis = M_INIObjGetData(somSelf); M_INIObjMethodDebug("M_INIObj","M_somUninit"); M_INIObj_parent_SOMClass_somUninit(somSelf); if(!somThis->UserINI) //if we created it delete it delete somThis->UserINI; if(!somThis->SystemINI) //if we created it delete it delete somThis->SystemINI;</font> }
That's it for the metaclass. As an exercise for the reader, modify the constructor CreateFromOpen so that it fails to create an instance of INIObj if it is passed a handle to either the system or user INI.
The INIObj Class
Now that we've seen the metaclass, we can start looking at the INIObj class. Going back to the SOM interface definition listing at the start of this section and comparing it to the C++ code, we see that there is nearly an 1 to 1 correlation between the functions. We won't cover what each function does, but merely explore the "highlights". The SOM version includes two additional functions GetStringNoDef and GetIntNoDef. These methods are necessary because in SOM there's no way to set default arguments and each function must be uniquely named. It's not CORBA compliant to use pointers as parameters. Thus, we define the type buffType, typedef sequence<char> buffType;. A sequence is similar to an "array/pointer" in C except it can have a maximum bound. Again, we override the somInit and somUninit to zero the instance variables and to provide clean up. Following is the code for somUninit.
SOM_Scope void SOMLINK somUninit(INIObj *somSelf) { INIObjData *somThis = INIObjGetData(somSelf); INIObjMethodDebug("INIObj","somUninit"); INIObj_parent_SOMObject_somUninit(somSelf); if(somThis->DidCreate) PrfCloseProfile(somThis->INIHandle);</font> }
The above routine calls the parent somUninit methods and then performs it's clean up. If DidCreate variable is non-zero, then the instance opened the profile file and should close it when destroyed.
Testing The Code
Now that we've seen how to implement the SOM object, let's see how to use it. The following code is the main routine taken from the file MAIN.CPP.
int main(int argc, char *argv[]){ // create an the INIObj class by creating an instance of M_INIObj M_INIObj *INIObjclass=INIObjNewClass(INIObj_MajorVersion ,INIObj_MinorVersion); //now create an instance of INIObj INIObj *INIFile=INIObjclass->INICreate(0,"test.ini"); //Write some strings to the ini file // application "TestApp", key "TestKey1" INIFile->WriteString("TestApp","TestKey1","This is the First Test"); // application "TestApp", key "TestKey2" INIFile->WriteString("TestApp","TestKey2","This is the Second Test"); char Junk[300]; // some space to the results from the GetString calls // read in and display the two app/key pairs INIFile->GetStringNoDef("TestApp","TestKey1",(INIObj_buffType*)Junk,300); cout<<Junk<<endl; INIFile->GetStringNoDef("TestApp","TestKey2",(INIObj_buffType*)Junk,300); cout<<Junk<<endl; // read in a app/key pair that doesn't exist ... do we get the default value ? INIFile->GetString("TestApp","TestKey3",(INIObj_buffType*)Junk,300,"No Such Key"); cout<<Junk<<endl; delete INIFile; // delete the instance of INIObj delete INIObjclass; // delete the instance of M_INIObj return 0; }
Using our C++ based wrapper previously defined, the code would look like:
int main(int argc, char *argv[]){ //now create an instance of Profile File INIObj *INIFile=new ProfileFile("test.ini",0); //Write some strings to the ini file // application "TestApp", key "TestKey1" INIFile->WriteString("TestApp","TestKey1","This is the First Test"); // application "TestApp", key "TestKey2" INIFile->WriteString("TestApp","TestKey2","This is the Second Test"); char Junk[300]; // some space to the results from the GetString calls // read in and display the two app/key pairs INIFile->GetString("TestApp","TestKey1",(INIObj_buffType*)Junk,300); cout<<Junk<<endl; INIFile->GetString("TestApp","TestKey2",(INIObj_buffType*)Junk,300); cout<<Junk<<endl; // read in a app/key pair that doesn't exist ... do we get the default value ? INIFile->GetString("TestApp","TestKey3",(INIObj_buffType*)Junk,300,"No Such Key"); cout<<Junk<<endl; delete INIFile; // delete the instance of ProfileFile return 0; }
There are only two major differences between the C++ and SOM version (at this level). First, the SOM version uses a metaclass. Therefore, the SOM version needs code to create and delete the metaclass. Second, C++ allows default parameters so the GetString() function can be used in several locations.
Wrapping Things Up
This concludes another thrilling episode of SOM exploration. The reader is encouraged to see how the implementation of the C++ members and SOM members differ. In particular, see how the inout parameters in SOM differ from the reference parameters in C++.