Table of Contents
The Object Engine

C++ Class Library and

Code Generator for

The Paradox [image] Engine

version 0.9

Dr. Mark Brittingham

Overview

Distribution Policy

Contacting BSDI

Using the Library

Understanding the Object Engine Class Library

The Persistent Class - An Introduction

Persistent Class Methods

Object List-Introduction

Object List Methods


Overview

The Object Engine is a C++ class library that encapsulates the core functionality of the Paradox Engine [image] database library.  This encapsulation provides a large degree of OODBMS functionality to engine users.  Rather than spending your time writing functions to copy data back and forth between data structures and record buffers, you need only call "Store", and "Link" methods of the Object Engine's Persistent classes.  Furthermore, these functions (and others) are written for you automatically.  You simply specify the fields that will populate your new class, and the Code Generator automatically generates the class and all of its methods for you!  The generated code will work under both Windows [image] and DOS [image] and can be compiled with Borland [image] C++ 3.1 (a sample is included to help guide you).  The library covers Alphanumeric, Short, Long, Double, Float (currency), Date and List types (see below).  Support for Blobs will be added in the version 2.0.  In addition to these features, the Object Engine provides a unique database list class that allows you to store a heterogeneous set of Persistent objects.  You can construct arbitrarily complex data relationships without the need for common (relational) fields by placing pointers to these lists within persistant classes.  You gain nearly all of the power of a full fledged OODBMS simply by adding the Object Engine library to your current Paradox [image] Engine application.  Plus, the library is very efficient: it adds less than 13K to your application (with speed optimization).


Distribution Policy

The files contained on this disk: objeng.cpp, objeng.hpp, manual.txt, and GEN2.EXE, are all copyrighted material. However, you may use, copy and distribute this software free of charge under the following conditions:

  1.  Never change any part of the Copyright statement.

  2.  All of the enclosed files must be distributed together.

  3.  You must not distribute changed versions of the program.  I do not want to be receiving correspondence about bugs that I have not produced.  If you have a suggestion, contact me at the address below and I will consider your suggestion.  If I incorporate your change, you will be credited in the documentation.

  4.  The author is not liable for any damage on your side caused by the use of this program.

  5.  The author has no duty to remedy for the deficiencies of the program.

 6.  If you wish to distribute this software with publications or a product, you must print the  copyright statement somewhere on the disk or on the package.

As long as those conditions are satisfied, you do not need to get the author's permission to use or to distribute the software.


Contacting Brittingham Software Design
How to contact Brittingham Software Design, Inc.

The best way to contact me is to send MAIL to me on CompuServe: 72740,2244.  If you do not have a CompuServe account, you may write to me at:

Dr. Mark Brittingham

15 Pheasant Ct.

Flanders, NJ  07836


Using the Library

Follow the next five steps to complete your database interface--

First, plan the database.  Have a clear idea of the classes needed for your application and their interrelationships.

Second, run the Gen2.exe application (it runs under [image] Microsoft Windows [image]) to create each of your classes.  Gen2 is quite simple.  A single button allows you to create a class or to edit whichever class is highlighted in a list.  It saves your class definitions in a format recognized by Gen2 and restores them later.  Thus, you need not complete the entire set of class definitions in one sitting.  When you are satisfied with the set of classes you have defined, you can generate the code to implement these classes as persistant objects with the push of a button. You specify the directory and filename of the header and C++ files.  Keep in mind that you can go back, change your classes, and regenerate your code at any time.  However, any changes you  make to the generated code will be overwritten (unless you use another file name).  Thus, it is best to either subclass the generated classes or keep your own code separate. No further documentation is included for the generator simply because it is so straightforward.  Please fire it up and see for yourself how quickly you can generate some real persistant classes.

Third, include the generated .h (header) file in your application.  When you generated code, this is the name you were prompted to type from the save file dialog.

Fourth, put the generated .cpp (C++ source code) file in your project or makefile.  You also named this file in response to the save file dialog box during code generation (step 2).

Fifth, add objeng.cpp to your project or makefile.  This file was delivered in the Object Engine zip file and is where all the hard work has been done for you!

Now you are ready to declare, manipulate, store, or retrieve instances of the classes you have defined! You never need to worry about initializing the engine, manipulating or creating tables, or reading or writing data.  Of course, you do have control over Paradox [image] Engine initialization as well as table manipulation if you need it.  But, for the vast majority of your work, you need only deal with the generated class interface in standard C++ syntax.


Understanding the OE Library
Understanding the Object Engine Class Library

Two core ideas will help you to understand how to use the library.  First, to store an object in the library, simply call its "Store" method. To retrieve a database record and place it in an object, you will "link" the object to the record.  This can be done either by the contents of a field (possibly a key field), or navigation (first, next, etc.). Thus "linking" is the act of making an in-memory data structure reflect an on-disk record. Of course, you needn't think of it in terms of database records at all!

The following link functions are defined:

LinkToKey         (one or more key fields)

LinkToRecord    (a record position)

LinkToField        (contents of a field)

LinkToID            (objectID - see below)

LinkToFirst

LinkToLast

LinkToNext

LinkToPrev

The second key idea is found in the "objectID".  Each object has an objectID associated with it.  When you link an object to a particular database record, your object is given the record's unique objectID.  You need to keep this in mind when working with Persistent classes.

For example, if you create a new object using the copy constructor or set one object to another (using '='), then both will have the same objectID.  If you change these two objects separately and save them both, then the record will reflect the last one stored.  That is, storage is mediated by the objectID.  An object without an objectID will be stored as new (assuming that there are no conflicting key fields).  An object whose objectID matches a record found in the database will update that record. If you simply keep in mind that an object has an identity that you must respect, you will have no problem using the library.


Persistent Class Functions
Persistent Class Methods - Introduction

When you generate code using gen2.exe, you will see that your class definition:

a) is derived from "PersistClass",

b) has one or more variable declarations,

c) has one or more matching "SetXXX" functions to set the variables,

d) has one or more "GetXXX" functions to get the value of variables, and

e) has only six other methods (2 Constructors, operator=, Store, Retrieve,                                                                 and LinkToKey).

If a class contains a list (an "OEList") of other Persistent classes, then there is no corresponding  "GetXXX" function and the "SetXXX" function returns a non-const pointer to the actual list.  All other returned values are const to ensure encapsulation.  You can change the "SetXXX" functions to reflect any validity checking or dependent variable manipulations that your class requires.  Since the OEList already features its own encapsulation (and since the code generator architecture made it a pain to make the list variables public), a non-const pointer to the list is returned from the "GetXXX" function.

In addition to the Set and Get functions and the six defined with the class, your class inherits many functions for linking to various records as well as functions for testing status, returning Paradox [image] Engine variables (table and record handles, etc.), and more.

Functions that return a value will return Paradox [image] Engine error codes (PXSUCCESS, PXERR_XXX).  In all cases (functions, operators, constructors), the variable "status" will be filled with the most recent Paradox [image] operation's return value (use GetStatus() to inspect). Thus, you will need to be aware of the Paradox [image] Engine's error codes.


Object List Methods-Introduction
Object List (OEList) Methods - Introduction

You will generally use OELists by declaring a field to be of type '^' in the code generator. This will generate all of the code you need to Store and Retrieve nested classes.  For example, if you have multiple phone numbers for a client, you may want to create a nested list of phone number objects and store it with a client record.  To pursue the example, assume that you have declared a class "Client" and a class "Phones."  You want to associate zero or more phones with a client but don't want to use a unique relational field in both tables since a given phone might be used by more than one client (a husband and wife, for example).

In this case, you can declare the Client class with an OEList called "phones."  When working with a client you can add a phone number in the following manner:

Client client;

Phone phone;

client.LinkToField("LNAME", (void *)"Brittingham", SEARCHFIRST);

phone.Setnumber("555-1212");

client.Getphone()->Insert((PersistClass *)&phone);

client.Store();

Note the following important points:  The phone object Inserted into the list and the link to the client object are both immediately Stored.  You do NOT need to call Store on an object that you place in an OEList (whether or not the list is owned by another PersistClass object).  This means that changes you make to a class's OEList variables cannot be undone simply by not storing the object.

You CAN have arbitrarily nested OELists.  For example, the Phone object could have a list of people that the phone number serves.  This list may include the client object defined above.  We avoid recursive infinite loops by NOT retrieving the data in a list until you take an action (such as inserting an object).  This also avoids retrieving a all of the data associated with a complex object with nested OELists if you just want to see a single field. If you want to make sure that all of the data associated with an OEList is in memory immediately, you can use the OEList::Init() function.  OELists have listIDs (just as objects have objectIDs).  When you place objects in an OEList, the objectID of the object and the listID of the list are associated and stored.  If you later call the OEList constructor with a listID (and call Init()), the system will search for all objects that were ever put on the list and pull them into memory.  This is how OELists are stored on PersistClasses: the listID is stored in the object's table as aLONG.  When the object is retrieved, the listID is used to construct a new OEList (see generated source code).

The OEList is both a container and an iterator. This is achieved by having the "()" (function) operator return the "current" object on the list.  The"++" operator moves to the next object on the list.  The Reset() function makes the first object on the list the current object. For example, you can do the following:

OEList objlist;

PersistClass* temp;

Foo* footemp;

objlist.Insert((PersistClass *)fooptr1);  //First position

objlist.Insert((PersistClass *)fooptr3);  //Second position

objlist.Insert((PersistClass *)fooptr4);  //Third position

// objlist() would now return a pointer to fooptr4.

// Move to first position before iterating.

objlist.Reset();

while ((temp = objlist++) != NULL)

((Foo *)temp).PrintSelf();  <- Some user-declared function

// OR use a for loop...

for (objlist.Reset(); footemp = (Foo *)objlist(); objlist++)

{

// Insert after fooptr object with ID of 1 (id is user-declared)

if (footemp->Getid() == 1)

{

objlist.Insert((PersistClass *)fooptr2);

break;

}

}


OEList Class Methods
OEList Class-Methods

OEList

~OEList

AddReference

Clear

Count

DeleteOK

GetStatus

GetListID

Init

Insert

Operator()

Operator++

Remove

Reset


def-OODBMS
Object-Oriented DataBase Management System.


def-OEList
A unique database list class that allows storage of a heterogeneous set of Persistent objects.


def-Persistent
The name of the Object Engine class that is the basis for defined database objects.  The Gen.exe program creates the database object for you by inheriting from this object so you don't have to write any code!


def-Heterogeneous
Consisting of or involving dissimilar elements or parts.


def-BLOBs
Binary Large Objects.  Can be used to store non standard items such as bitmaps, custom structures, sounds etc.


def-Scalar
A quantity, such as mass, length, time, or temperature, completely specified by a number on an appropriate scale.


PersistClass - methods

PersistClass

Store

LinkToKey

LinkToRecord

LinkToField

LinkToID

LinkToFirst

LinkToLast

LinkToNext

LinkToPrev

Unlink

Destroy

NRecs

OEPutDate

OEGetDate

GetStatus

GetTblHdl

GetRecHdl

GetTblName

GetObjectID

GetDeleteFlag

SetDeleteFlag


PersistClass.PersistClass
PersistClass

Constructor:  PersistClass()

Creates an instance of Client.  If the client database does not exist, it is created.  If it is not open, it is automatically opened.  The object does not reflect any record in the database when first created.  All strings and lists are empty, all scalar values are set to zero.

Constructor: PersistClass(Client& val)   [the copy constructor]

Creates a new, identical instance of Client.  The client database will be created and open since you must have already created a client to pass in! The new object reflects all of the values of the passed in object.  OELists are not copied!  The same OEList pointer is now used in both objects.  If you make a change to the list using the first object, you are also changing the list used by the second. Since the list is meant to reflect the database, this is exactly as it should be!

Operator: PersistClass &operator=(Client &)    [the assignment operator]

See the copy constructor.  Best used during the declaration of a reference.


PersistClass.Store
Store

Function: int Store()  [see PXRecInsert, PXRecUpdate]

Moves the contents of the object into the database. If the object is already

linked to a database record (either from a link function or because it was

copied from another object) then this will UPDATE the database record. Any object possessing a non-zero objectID is assumed to be linked to the matching db record.  If the object is not yet linked to a database record (objectID is equal to 0), then calling Store will add it to the database (PXRecInsert). Note that this function reflects the documentation for PXRecUpdate and PXRecInsert: an attempt to enter a duplicate key is an error, a non-keyed table insert will place the record before the current record, etc.

Return Value: PXSUCCESS or other engine error code.


PersistClass.LinkToKey
LinkToKey

Function: int LinkToKey(int numkeys, int mode)  [See also, PXSrchKey]

When you define a class, you can specify one or more of its variables(starting with the first), as keys.  A key variable corresponds to key fields in the database.  To search using key values, you place these values in the object's key variables and call this function.  For example, if youhave three key fields and want to search on the first two, you should placevalues for these first two key fields in the corresponding variables andcall this function with numkeys = 2.  Set Mode equal to any mode legal under the PXSrchKey documentation (SEARCHFIRST, etc.) according to your search requirements. For example:

Client.ID = 1000;

if (Client.LinkToKey(1, SEARCHFIRST) == PXSUCCESS)

 <do great things...>

Note: if the field is not keyed, this function does nothing but return an error.

Return Value: PXSUCCESS or other engine error code.


PersistClass.LinkToRecord
LinkToRecord

Function: int LinkToRecord(int position) [See PXRecGoto]

Make object reflect contents of the nth record where n = position.

Return Value: PXSUCCESS or other engine error code.


PersistClass.LinkToField
LinkToField

Function: int LinkToField(char *fieldname, void *value, int mode) [See PXSrchFld]

To search for a specific value in a specific field, pass in the fieldname,the value (cast to a void pointer), and the search mode.  For example:

client.LinkToField("LNAME", (void *)"Brittingham", SEARCHFIRST);

client.LinkToField("ID", (void *)&i, SEARCHFIRST);

Note that the value must be a void pointer.  If you really want overloaded functions, let me know.  It takes up more space but might be worth doing.  Note also that searches using compound keys are on the way...

Return Value: PXSUCCESS or other engine error code.


PersistClass.LinkToID
LinkToID

Function: int LinkToID(const long &ID)   [See PXSrchFld]

Explicitly set object to database record with objectID = ID.  Should not generally be needed.  Note, no mode argument since objectIDs are unique.

Return Value: PXSUCCESS or other engine error code.


PersistClass.LinkToFirst
LinkToFirst

Function: int LinkToFirst()

Make object reflect contents of first database record.  If database is empty, contents are not changed and error code is returned.

Return Value: PXSUCCESS or other engine error code.


PersistClass.LinkToLast
LinkToLast

Function: int LinkToLast()

Make object reflect contents of last database record.  If database is empty, contents are not changed and error code is returned.

Return Value: PXSUCCESS or other engine error code.


PersistClass.LinkToNext
LinkToNext

Function: int LinkToNext()

Make object reflect contents of next database record.  If there is no next record, contents are not changed and error code is returned.

Return Value: PXSUCCESS or other engine error code.


PersistClass.LinkToPrev
LinkToPrev

Function: int LinkToPrev()

Make object reflect contents of previous database record. If there isno previous record, contents are not changed and error code is returned.

Return Value: PXSUCCESS or other engine error code.


PersistClass.Unlink
Unlink

Function: void Unlink()

Removes the objectID from the object.  This allows you to copy an object and then unlink it from the database record.  If you then Store the object it will go in under a new objectID (assuming there is no key conflict).

Return Value: None


PersistClass.Destroy
Destroy

virtual int Destroy()  [See PXRecDelete]

Deletes the database record to which this object corresponds.  If theobject has not been linked, it returns PXERR_RECNOTFOUND.  Does not deletethe object or change any of its values.  Note that deleting an object doesnot remove it from the database.

Return Value: PXSUCCESS or other engine error code.


PersistClass.NRecs
NRecs

function: int NRecs(RECORDNUMBER & nrecs)  [See PXTblNRecs]

Returns the numbers of records in the table that the object works with.

Return Value: PXSUCCESS or other engine error code.


PersistClass.OEGetDate
OEGetDate

function: int OEGetDate(const int &, struct tm &)

Used in the classes derived from PersistClass to place dates into specific record buffer fields.  Used to simplify the process of generating code.  You should never need to use this function since you will not work directly with record buffers.

Return Value: PXSUCCESS or other engine error code.


PersistClass.GetStatus
GetStatus

function: const int GetStatus()

Returns the status value. Status is set by the most recent Paradoxc:\dev\dl\objeng\tm.bmp Engine function.  This function is useful for testing the success of functionsthat don't return a status value (constructors and operators) although itcan be used after any Object Engine call.

Return Value: PXSUCCESS or other engine error code.


PersistClass.GetTblHdl
GetTblHdl

function: TABLEHANDLE GetTblHdl()

Returns the Paradoxc:\dev\dl\objeng\tm.bmp Engine table handle.  Can be used if you want to bypass the library and work directly with the table.  Since the Object Engine doesnot provide direct table manipulation (copy, delete, etc.), you may findthis function useful.

Return Value: PXSUCCESS or other engine error code.


PersistClass.GetRecHdl
GetRecHdl

function: RECORDHANDLE GetRecHdl()

Returns the Paradoxc:\dev\dl\objeng\tm.bmp Engine record handle.  Can be used to bypass the library and work directly with the record buffer.  Should not be needed unless youwant to work with multi-field secondary indices (which will be available in the next release anyhow).

Return Value: PXSUCCESS or other engine error code.


PersistClass.GetTblName
GetTblName

function: const char *GetTblName()

Returns the name of the table to which this object is attached.

Return Value: PXSUCCESS or other engine error code.


PersistClass.GetObjectID
GetObjectID

function: long GetObjectID()

Returns the objectID.  Should not be needed.

Return Value: PXSUCCESS or other engine error code.


PersistClass.GetDeleteFlag
GetDeleteFlag

SetDeleteFlag

function: char GetDeleteFlag() &

function: char SetDeleteFlag(char flag)

GetDeleteFlag and SetDeleteFlag are used in conjunction with OELists.  When an OEList is deleted or cleared, it will check the deleteFlag for each ofthe elements that is points to.  If the deleteFlag is TRUE, the object will be deleted.  Otherwise it will not.  In both cases, any reference to the object is removed from the OEList.  Thus, if you create a pointer to an object that you get from an OEList, you should set its deleteFlag to FALSE if you want to delete the list but keep the pointer.

Return Value: PXSUCCESS or other engine error code.


PersistClass.OEPutDate
OEPutDate

function: int OEPutDate(const int &fieldno, const struct tm &date)

Used in the classes derived from PersistClass to place dates into specific record buffer fields.  Used to simplify the process of generating code.  You should never need to use this functions since you will not work directly with record buffers.

Return Value: PXSUCCESS or other engine error code.


PersistClass.SetDeleteFlag
GetDeleteFlag

SetDeleteFlag

function: char GetDeleteFlag()

function: char SetDeleteFlag(char flag)

GetDeleteFlag and SetDeleteFlag are used in conjunction with OELists.  When an OEList is deleted or cleared, it will check the deleteFlag for each ofthe elements that is points to.  If the deleteFlag is TRUE, the object will be deleted.  Otherwise it will not.  In both cases, any reference to the object is removed from the OEList.  Thus, if you create a pointer to an object that you get from an OEList, you should set its deleteFlag to FALSE if you want to delete the list but keep the pointer.

Return Value: PXSUCCESS or other engine error code.


OEList.OEList
OEList

Constructor: OEList()

Create a new OEList.  This object will be a simple shell without ID or any objects inside.

Constructor: OEList(long inID)

Create a new OEList.  This object will be a simple shell but will have the given ID.  When Init() is called, all objects associated with the given listID will be pulled into memory.


OEList.Init
Init

function: int Init()

Instantiate the given OEList.  If no listID has been assigned, generate a new (unique) listID.  If a listID exists, pull out all associated objects from the database and store them in memory.

Return Value: TRUE or FALSE.


OEList.~OEList
~OEList

Destructor: ~OEList()

Deletes all of the objects pointed to by the OEList EXCEPT those whose deleteFlag is FALSE.

Also, this function WILL CAUSE AN ERROR WINDOW OR MESSAGE to pop up if you attempt to delete a list that has multiple pointers to it.  That is, whenever an object is copied, it will copy any OEList pointers.  In order to be a good citizen, it will also inform each OEList (via the AddReference function, see below) that a new pointer exists.  When you want to delete a list, you should make sure that you check this pointer count. The RIGHT way to delete a list is:

 if ((oelist != NULL) && (oelist->DeleteOK() == TRUE))

delete oelist;

The code generator automatically puts this line into the destructors of all classes that have OEList elements.  In general, you do not need to worry about this at all unless you are declaring your own lists and creating multiple pointers.  If you just use the code generated by Gen2, you are guaranteed never to run into any problems.


OEList.Insert
Insert

function: void Insert(PersistClass *curelem)

Inserts a Persistent class into an OEList.  If you put an element into the OEList and want to keep it around even if the list goes away, call the object's SetDeleteFlag function with an argument of FALSE.  If you delete an object pointed to by a list you may crash your program if you work with the list later.


OEList.Remove
Remove

function: void Remove()

Removes the current object from the list (you change the "current object"by iterating through a list- see above).  If the deleteFlag is TRUE, this function deletes the object from memory.  Does NOT destroy the object's corresponding database record.  The on-disk representation of the list is immediately updated to reflect this removal.


OEList.operator()
operator()

function: PersistClass *operator()()

Returns the current object.  Does not increment or otherwise change the state of the list.


OEList.operator++
operator++

function: PersistClass *operator++()

Returns the current object and then increments the "current object" pointer.

void Reset()

Moves the "current object" pointer to the first element of the list.


OEList.Count
Count

function: int Count()

Returns the number of elements in the OEList.  Do not use this for iterating unless you are SURE that you will not be changing the list in your iteration.


OEList.Clear
Clear

function: void Clear()

Removes all of the elements on the list.  See the Remove() function.


OEList.AddReference
AddReference

function: void AddReference()

If you will be creating your own pointers to a list, this function is used to inform the list of your pointer.


OEList.DeleteOK
DeleteOK

function: char DeleteOK()

Decrements the reference count and returns it.  This is the opposite of AddReference.  You should not call this function unless you are removing your pointer.  Also, you should delete the list if this function returns 1.(See the documentation for the destructor).


OEList.GetStatus
GetStatus

function: const int GetStatus()

Returns the status of the list.  Can be any of: NOTINIT, READY, or FAIL.


OEList.GetListID
GetListID

function: const long GetListID()

Returns the listID.  This number is used in a <listID, objectID, TableName> table to keep track of the associations between lists and objects.


OEList.Reset
Reset

function: void Reset()

Moves the "current object" pointer to the first element of the list.