| Front Page | News Headlines | Technical Headlines | Planning Features | Advanced Search |

February 2002

Using C++ To Develop Posix-Compliant Apps

Free GNU C++ compiler, IMAGE classes make code easily ported to other platforms

By Curtis Stordahl

Second of two parts

Last month I recommended Posix-compliant revision of HP 3000 applications for a more gradual and safer migration path. There are numerous options available when choosing how to go about developing Posix-compliant applications on the HP 3000. A lot of attention, for example, has been focused on Java. But native Java isn’t going to support your legacy IMAGE database. You must attach your IMAGE database to an SQL front end (IMAGE/SQL) or go through a Java Native Interface (JNI).

One hidden asset in this quest is the port of the GNU compilers done by Mark Klein. One of the things that makes C++ attractive is that it links directly with the IMAGE intrinsics. And code developed using the GNU compilers is easily ported to another Posix-compliant platform.

Object-Oriented Development

Most readers are aware that C++ is an object-oriented extension of the C programming language. Many readers may not be aware of object-oriented database design. We have an increasing need to provide the same functionality to all possible interfaces (CGI, XML, etc.). It is impractical to expect our interface developers to incorporate and maintain business rules in every conceivable interface as we did with our COBOL/IMAGE/VIEW applications. This is where object-oriented database management comes into play. We encapsulate the business rules into objects separate from the interface. The interface developers then need only be given an object, and that object in turn inherits all necessary business rules.

C++ by itself isn’t, of course, a database management system. So its objects don’t exist beyond the instance of execution. We can, however, use our existing IMAGE database as a storage mechanism to allow our C++ objects to persist beyond the instance of execution.

This interaction between C++ and IMAGE is maintained through an IMAGE class library. It is important that all interaction between the application and IMAGE be done exclusively through this class library so that only this class library need be modified to complete the port.

The IMAGE C++ Class Library

Let’s now get an overview of the IMAGE class library and how it works.

We have a Base class. This contains methods for manipulation at the database level. This includes an openModify (DBOPEN mode 1), openRead (DBOPEN mode 5), lockBase (DBLOCK), unlockBase (DBUNLOCK), beginWork (DBXBEGIN), commitWork (DBXEND), rollbackWork (DBXNDO), and closeBase (DBCLOSE).

We have a Set class. This contains methods for manipulation at the set level. This includes methods such as find (DBFIND), getChainedForward (DBGET mode 5), getCalculated (DBGET mode 7), setUpdate (DBUPDATE), and setPut (DBPUT) just to name a few. We would not use the Set class by itself because it has no knowledge of any data items. It would, instead, be used as a Base class to well-defined Set classes.

We have a series of Item classes used to move data to and from the buffer contained within the Set class. We have an AlphaItem class for type X items. The length in characters must be specified when the AlphaItem class is instantiated. We have a ShortItem class for type I1 items. We have an IntegerItem class for type I2 items. We also have some custom items. We have a DateItem class which stores the date in the buffer in an arbitrary YYYYMMDD format in a type I2 field. We have a TimeItem class which stores the time in the buffer in an arbitrary HHMMSS format in a type I2 field. Item classes are attached to a Set class when they are instantiated.

The listing below shows an example of a fully formed Set class specifically for the ISSUES data set. You can think of the class definition for a specific data set to be a container class because it contains Item classes.

class ICIssuesSet : public Set {
public:
AlphaItem *partNumber;
IntegerItem *referenceNo;
DateItem *dateIssued;
TimeItem *timeItem;
IntegerItem *qtyIssued;
ICIssuesSet(char *bName, Key *kClass);
};
ICIssuesSet::ICIssuesSet(char *bName, Key *kClass)
: Set(bName,”ISSUES;”,kClass) {
partNumber = new AlphaItem(this,”PART-NUMBER”,PART_NUMBER);
referenceNo = new IntegerItem(this,”REFERENCE-NO”);
dateIssued = new DateItem(this,”DATE-ISSUED”);
timeIssued = new TimeItem(this,”TIME-ISSUED”);
qtyIssued = new IntegerItem(this,”QTY-ISSUED”);
}

We have a series of Key classes used to define the path to the data set. These are similar to the Item classes since the path is also an item. They do in fact derive the same attribute properties that the Item classes use when moving data in and out of the key buffer. There is an AlphaKey, a ShortKey, an IntegerKey, a DateKey, and a TimeKey. The Key class, like the Item class, must have it’s item name specified when it is instantiated.

We did not specify the key path when we defined the ICIssuesSet class. In this particular example we have a path via either the Part Number or the Reference Number. The Key class to be used must be specified when the Set class, which ICIssuesSet is derived from, has been instantiated.

Creating Business Objects

The listing that follows is the ICIssuesAdd class responsible for the actual update if the Issues data set. This is a transaction level class that the database developers would make available to the interface developers.

1 class ICIssuesAdd
2 : public ICRequirementsIssues, ICPartMasterIssues {
3 private:
4 AlphaKey *partNumberKey;
5 ICIssuesSet *issuesSet;
6 public:
7 ICIssuesAdd(char *bName);
8 void doIt(char *part_number,
9 int *reference_no,
10 iDateT *date_issued,
11 iTimeT *time_issued,
12 int *qyt_issued);
13 };
14 ICIssuesAdd::ICIssuesAdd(char *bName)
15 : ICRequirementsIssues(bName), ICPartMasterIssues(bName) {
16 partNumberKey = new AlphaKey(“PART-NUMBER;”,PART_NUMBER);
17 issuesSet = new ICIssuesSet(bName,partNumberKey);
18 }
19 void ICIssuesAdd::doIt(char *part_number,
20 int *reference_no,
21 iDateT *date_issued,
22 iTimeT *time_issued,
23 int *qty_issued) {
24 isPartNumberValid(part_number);
25 isRequirementValid(part_number,reference_no);
26 issuesSet->partNumber->setString(part_number);
27 issuesSet->referenceNo->setInteger(reference_no);
28 issuesSet->dateIssued->setDate(date_issued->Year,
29 date_issued->Month,
30 date_issued->Day);
31 issuesSet->timeIssued->setTime(time_issued->Hours,
32 time_issued->Minutes,
33 time_issued->Seconds);
34 issuesSet->qtyIssued->setInteger(qty_issued);
35 issuesSet->setPut();
36 adjustRequirement(qty_issued);
37 adjustPartMaster(qty_issued);
38 }

Lines 1 thru 13 define the class header. The class header essentially defines pointers to our Key class and Set class as well as methods for the constructor and execution of the transaction. Lines 14 thru 18 make up the constructor. Line 16 instantiates the Key class. Line 17 instantiates the Set class. Lines 19 thru 38 define the doIt method, which performs the actual transaction. Lines 26 thru 34 execute the Item methods that loads the buffer. Line 35 writes the buffer to the Issues data set.

Once again, the database developers need to provide the interface developers with a single method. That includes referential integrity between datasets. Note that line 2 specifies that the ICIssuesAdd class shall inherit the properties of the ICRequirementsIssues and ICPartMasterIssues classes.

The ICRequirementsIssues class (not illustrated) defines the referential integrity between the Requirements data set and the Issues data set. Line 25 executes a method that validates the existence of a requirements record for the specified Part Number and Reference Number. Line 36 executes a method that will adjust the quantity issued and quantity required of the requirement record in accordance with the issue.

The ICPartMasterIssues class (not illustrated) defines the referential integrity between the Part Master dataset and the Issues dataset. Line 24 executes a method that validates the Part Number on the Part Master. Line 36 executes a method that will adjust the total quantity on-hand and the total quantity in demand on the part master record in accordance with the issue.

Figure 1 illustrates the relationship between these datasets.

Developing the User Interface

Our sample user interface is a CGI program. One of the drawbacks to CGI programming is that it is stateless and every transaction requires that a new process be launched. Heavy transaction processing can easily bring a system to its knees. But if high-volume transaction processing is not an issue, then CGI programming is an attractive option. The use of C++ class libraries simplifies the CGI application interface just like Transact simplified the VIEW application interface.

The listing below is a complete CGI program written to process issues.

1 // issues.C
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include “image.h”
5 #include “itemsize.h”
6 #include “ic.h”
7 #include “icExceptions.h”
8 #include “icIssues.h”
9 #include “util.h”
10 Base *icBase;
11 ICIssuesAdd *icIssuesAdd;
12 main (int argc, char **argv) {
13 char *PartNumber;
14 int ReferenceNo;
15 iDateT DateIssued;
16 iTimeT TimeIssued;
17 int QtyIssued;
18 char icName[]=” IC;”;
19 char pass[]=”dba;”;
20 icBase = new Base(icName,pass);
21 icIssuesAdd = new ICIssuesAdd(icName);
22 Form *f = new Form(“Issues”);
23 try {
24 icBase->openModify();
25 icBase->lockBase();
26 icBase->beginWork();
27 try {
28 PartNumber=f->getParameter(“PartNumber”);
29 f->getInteger(“ReferenceNo”,ReferenceNo);
30 f->getDate(“DateIssued”,DateIssued.Year,
31 DateIssued.Month,
32 DateIssued.Day);
33 f->getTime(“TimeIssued”,TimeIssued.Hours,
34 TimeIssued.Minutes,
35 TimeIssued.Seconds);
36 f->getInteger(“QtyIssued”,QtyIssued);
37 icIssuesAdd->doIt(PartNumber,
38 &ReferenceNo,
39 &DateIssued,
40 &TimeIssued,
41 &QtyIssued);
42 printf(“Issue Successfully Processed”);
43 icBase->commitWork();
44 } catch (DataException &dataException) {
45 icBase->rollbackWork();
46 dataException.printError();
47 } catch (DataBaseException &dataBaseException) {
48 icBase->rollbackWork();
49 dataBaseException.printError();
50 } catch (ImageException) {
51 throw;
52 }
53 icBase->closeBase();
54 } catch (ImageException &imageException) {
55 imageException.printError();
56 exit(1);
57 }
58 delete f;
59 }

This program uses the ICIssuesAdd class as a single point of entry to the database. The pointer to the class is defined on line 11. The class is instantiated on line 21. The doIt method of the ICIssuesAdd class is executed on lines 37 thru 41 after all parameters have been obtained. Line 47 will catch any exceptions thrown by the ICIssuesAdd class, and roll back the transaction if necessary.

Summary

The GNU C++ compiler is a powerful tool that is worthy of serious consideration. It can be compiled down to fully optimized native code that links directly with your legacy IMAGE databases. It allows us to modernize our applications through object-oriented design. And it is fully portable to other Posix-compliant platforms.

Whether or not you choose the GNU C++ compiler for development of portable applications, the concept of developing Posix-compliant applications that gradually displace your legacy applications should be given serious consideration — because it mitigates the risks inherent in migrations.

Curtis Stordahl has been a developer of HP 3000 applications since 1979. His latest product is iJobSched, a Web-based job scheduler that he has recently ported to HP-UX using Posix and Eloquence. An Open Source copy of the “C++ IMAGE Class Library” will be made available at his Web site at www.stordahl-inc.net. 


Copyright The 3000 NewsWire. All rights reserved.