The use of SOM to mirror the C++ object

From EDM2
Jump to: navigation, search

Written by Gordon Zeglinski

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 wether 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 lenght 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 instace is destroyed
   // otherwise an error occured
   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 a 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 a 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++.