NETSIM C++


User's Manual




Table of Contents


1.0 Introduction

2.0 Using NETSIM C++

3.0 Data Definition Language Files

4.0 Data Manipulation Language Syntax

5.0 Utility Variables and Methods

6.0 Interactive Debugger

7.0 Error Messages

1.0 Introduction

1.1 Overview of NETSIM C++

The Network Database Simulator system in C++ (NETSIM C++) simulates selected features of a network database management system. Using NETSIM C++, you can create record types, set types, and keeplists for a network database, and then manipulate this database in the context of a C++ program. The system will translate your type declarations and data manipulation commands into C++ code, which can then be compiled by a C++ compiler.

NETSIM C++ includes several utility methods (in C++) which you can include in your database programs. The system also incorporates a debugging facility to assist you in tracking down errors in the structure of your databases.

Note: It is assumed throughout this manual that the user has a working knowledge of C++ and of object orientation.

1.2 Network Database Concepts

A network database consists of a collection of records connected to one another by links. Each link associates two records together in some way. Record types can also be linked to one another. When this occurs, they form a set. In every set, one record type is designated as the owner of the set, and the other is designated as the member of the set. Sets can only exhibit one-to-many relationships in a network database (with the set owner being the "one"). Each owner record along with its associated member records form an occurrence of a given set type. Each member type record can be in at most one set occurrence, though a set type itself can have any number of occurrences.

When a set is formed, its method of insertion must be specified. This insertion method dictates properties which must hold on records which are inserted into an occurrence of the set. Insertion methods may be one of the following:

Manual: the record is inserted into the set explicitly via a data
manipulation command

Automatic: the record is inserted into the set implicitly upon its creation

Similarly, a set must have a rule concerning the removal of its member records. This rule is called a retention method and can be one of the following:

Fixed: the member record may not be disconnected from its set occurrence

Mandatory: the member record must be reconnected to another set
occurrence of the same set upon its removal from the original
set occurrence; it cannot just be simply disconnected

Optional: the member record may be disconnected without restriction
from the set occurrence

Record and set types and variables are declared using a data definition language (DDL). The data manipulation language (DML) provides facilities for creating, manipulating, and deleting individual instances of these. DML commands are embedded in a host language (C++ for this system), and can access database variables (created via the DDL) as well as locally defined program variables. (DDL and DML statements for NETSIM C++ are discussed below.)

For each database application program or run unit, the network database system maintains a user work-area. This is a buffer storage area which contains templates for each record type defined for the run unit. It also contains a set of pointers to the most recently accessed entities in the application program. These currency pointers include the following:

Current of record type: a pointer to the address of the most recently accessed
record for each record type

Current of set type: a pointer to the address of the most recently accessed
record in the set for each set type; this may point to the set owner or one of
its members

Current of run unit: a pointer to the most recently accessed record, regardless
of its type

Note that an application program may not directly access these currency pointers, but may manipulate them indirectly through DML statements. Additionally, the user work-area contains a set of status flag variables used to communicate the outcome of the last operation performed to the run unit. (Some of these can be accessed in NETSIM C++ and are outlined below.)

Sometimes the currency pointers in the user work-area are insufficient for keeping track of things. A keeplist is a useful structure which can hold currency pointers so that they can be reactivated later. Keeplists behaves like queues as far as insertion and removal go.

NETSIM C++ is based upon the report issued by the Database Task Group (DBTG. However, only a subset of the DDL, DML, and other facilities outlined in this report are implemented in this system.

2.0 Using NETSIM C++

2.1 Understanding the System

In and of itself, NETSIM C++ is not an application program with a standard user interface, but rather a tool for building small scale network databases. Because of this, you will need several building blocks to effectively make use of the system. These include the following:

DDL file(s): These files will contain declarations which describe the database
you wish to use. Specifics of the NETSIM C++ DDL are outlined below. All
DDL files must have a ".ddl" extension.

Application program(s): These programs will contain a mixture of C++ code
and comments, NETSIM C++ DML commands, and a #ddl directive
referencing the DDL files to be used with the program. The last two of these
articles are described in detail below. Each application program must have a
".dml" extension.

The NETSIM C++ system consists of two major parts:

2.2 Preprocessor Program (netpp)

This program takes as input the application program(s) and DDL file(s) you have written for a network database system. It transforms the declarations for records, sets, and keeplists in the DDL file(s) into appropriate C++ class declarations. It also translates the DML commands in the application program(s) into calls to methods defined in the NETSIM C++ library.

For each application program submitted to it, the preprocessor outputs a program written in "pure C++" which contains the class declarations from the DDL file(s), the C++ code and comments which were in the original application program, and the method calls corresponding to the translated DML statements. (Note that these will be in the same relative positions in the output file as the DML statements in the application program.) This file will have a ".cc" extension and will be immediately ready for compilation by a C++ compiler.

For each syntax error the preprocessor detects while translating the DDL and application program files, it outputs an appropriate error message to the screen. (These error messages are described below.) If this happens, you should correct the error(s), and then re-submit the application program.

To submit an application program to the preprocessor, run the netpp program. It will prompt for the name of the application file. The file it outputs (provided no errors are found) will have the same name as the application program, except that its extension will be ".cc" rather than ".dml". (The preprocessor can deduce which DDL files are associated with the application program based upon the contents of its #ddl directive.) The output file can then be compiled and linked as any normal C++ program.

Note that every NETSIM C++ application program you intend to use must be successfully parsed and translated by the preprocessor before being processed by a C++ compiler.

2.3 NETSIM C++ Library (netlib)

The NETSIM C++ library contains C++ methods for carrying out DML commands as well as several utility methods and variables which your programs can access. (These are listed and explained below.) Additionally, this library contains error messages to be printed if a run time error occurs with your database. Declarations necessary for maintaining the structure of a network database are also kept here.

When you submit an application program to the preprocessor, it will append the following C++ directives to the beginning of the file it outputs. These directives instruct the C++ preprocessor to include the NETSIM C++ library.

#include "lolevel.h"
#include "netsim.h"
#include "record.h"
#include "set.h"
#include "keep.h
#include "debug.h"

In addition, you must remember to include the netlib library in the link command for your program. Exactly how to do this is system specific.

2.4 Other System Pieces

NETSIM C++ also includes a Date class which is used to model calendar dates and display them with the form dd-mm-yyyy This class includes facilities for manipulating dates in various ways through overloaded operators. The Date class is declared in the file date.h.

A help text file entitled netdbg.hlp is also included. This file is printed when the help command is issued from within the debugger, discussed below.

3.0 Data Definition Language Files

3.1 Creating and Referencing DDL Files

Each NETSIM C++ application program will use one or more DDL files describing the database. You should not alter a DDL file once this program is compiled and linked, and once data has been loaded into the corresponding database, since interpretation of this data hinges on the declarations in the DDL file. Doing so will require you to recreate the database as the information stored in it will be unreliable.

The names of your DDL files should end with a ".ddl" extension. They should only contain NETSIM C++ DDL constructs as described below and C++ comments.

Each application program using objects declared in a given DDL file must list that file in a #ddl directive. An application program using DML statements must contain at least one such directive (one for each DDL file referenced), and this should appear before any DML commands in the program (preferably along with other C/C++ preprocessor directives). A #ddl directive stands by itself on one line of the source file. It consists of the directive (#ddl) followed by a DDL file name within quotation marks.

Example:

#ddl "myddl.ddl"

3.2 DDL Syntax

The NETSIM C++ preprocessor requires that you use the following constructs in declaring records, sets, and keeplists in your DDL files. Each of these constructs begins with a # as the first non-blank character on the line. The declarations may continue over any number of lines in the DDL file, but nothing else (including comments) should appear on these lines. All sets must be declared after the records which are their members and owners.

In the syntax described below (both here and in the following sections), reserved words and characters in statements are written in bold face Names you supply are italicized, and fixed identifiers (in the examples) are in plain text. Furthermore, optional segments are enclosed in square brackets, and angle brackets surround parts which may be repeated any number of times. If several terms are possible at a point in a statement, these are listed in a column. Note that the NETSIM C++ system (like the C++ programming language) is case sensitive; all lexical conventions outlined in this section and those that follow must be expressly adhered to. 3.2.1 Record Declaration

#record recordName on fileName using
{ < fieldType fieldName ; > } ;

The value of fileName is the name of a file in which all records of the type being declared are stored. The name of this file must be enclosed in quotation marks.

The value of fieldType declares its corresponding fieldName to be of some type, which may be one of the following:

int an integer
float a floating point number
date a date (defined in date.h)
string length a string of length characters

Example:

#record Student on "student.dat" using
  {
    string 7 studentId ;
    string 20 lastName ;
    string 20 firstName ;
    int yearAdmitted ;
    float gpa ;
    date birthday ;
  } ;

For each field of each record you declare, you may access a template of the form recordName.fieldName (e.g. given the student record declaration above, you could reference Student.studentId and Student.gpa, to name a couple possibilities). You may use the name of a record by itself, or the field names by themselves within a DML command, but not in any other context.

Whenever you copy character arrays to fields of type string, be sure to use the C library function strncpy( string1, string2, len ) in your application programs, passing the size of the character array to be copied as the final parameter. (This can be done using the C sizeof operator.) This function ensures that all character slots in the string field are copied into (either by characters from the copied character array or by NULL characters), andwill keep some obnoxious errors out of your program.

3.2.2 Set Declaration

#set setName owner recordName member recordName
[ insertion insertMode ] [ retention retainMode ] ;

The value of insertMode may be either automatic or manual. The value of retainMode may be one of fixed, mandatory, or optional.

Examples:

#set StudentRoom owner Room member Student ;
#set StudentRoom owner Room member Student retention optional ;

You may use set names within DML statements, but nowhere else.

3.2.3 Keeplist Declaration

#keeplist keeplistName ;

Example:

#keeplist TempKeep ;

You may use keeplist names inside DML commands as well, but nowhere else.

Certain identifiers in a given DDL file need to be unique. Records, sets, and keeplists cannot share names. However, different record types may have fields with the same name (but obviously all fields declared in a given record type should be distinct). It is probably a good idea to choose unique names for identifiers in the application, so that they are different from others in the program and in relevant DDL files. This may help to avoid some translation and compilation errors.

Note that all record, field, set, and keeplist names may not exceed 16 characters in length.

4.0 Data Manipulation Language Syntax

The following DML statements may be used from within your application programs, interspersed with C++ code and comments. Each must have the # as the first non-blank character on its initial line and can extend over multiple lines in the source code file. Normally, nothing else (including comments) should appear with a DDL command on the lines it occupies. (The conditional statements described below are exceptions to this, since they require additional code for completeness.)

4.1 Find Statements

One of the most basic network database command is find. The find statement attempts to locate a record in the database which matches the criteria you specify.

Although there are several variations on the find statement, there are some properties common to all of them. When a record is successfully found, several currency pointers converge upon it. These include the current of run unit, the current of record for its type, and the current of set for all sets in which it participates (either as an owner or a member). If the record found is of the member type of a certain set, but itself is not currently the member of an occurrence of that set, then the current of set pointer for that set is not affected. If you wish to selectively repress updating some of these currency pointers, you should use a retaining clause, described below. If no record is found which matches the specified criteria, none of the currency pointers are altered. Instead, the status Netsim::status is set equal to Netsim::NOT_FOUND. (This variable and others like it are discussed below.)

All named values in a find statement must be defined in some DDL file. That is, recordName must be a declared #record, setName a #set, and keeplistName a #keeplist.

NETSIM C++ does not provide a find update statement. Update is allowed after any successful find.

In the following syntactic descriptions, first and any have exactly the same meaning, as do next and duplicate. The first and any keywords specify that record to be found is the first one of the specified type. The next and duplicate keywords denote that the record found should be the next record meeting the criteria given following the current of record for the specified record type.

4.1.1 Record Oriented Find Statement

first
#find  next    recordName [ using fieldName < , fieldName > ] [ retaining clause ] ;
any
duplicate


This is the simplest of the find commands. It attempts to locate the first/next record of the type specified by recordName. If you include a using clause in the statement, it looks for one that matches the record template (in the user work-area) on all of the specified fields.

Examples:

#find first Student ;
#find next Student using lastName ;
#find first Room using dorm, number ;

4.1.2 Set Oriented Find Statements

first
#find  next    recordName within setName [ retaining clause ] ;
any
duplicate

This form of the find command tries to locate the first/next member of the set occurrence associated with the current of set for the specified set. The value in recordName must refer to the member record type of setName. If the set has no current record then Netsim::status is set to Netsim::NO_CURRENT.

Example:

#find first Student within OccupiedBy ;

#find owner within setName [ retaining clause ] ;

The find owner statement looks up the owner of the set occurrence associated with the current of set for the specified set. Again, if there is no current record for the set, Netsim::status is set to Netsim::NO_CURRENT.

Example:

#find owner within OccupiedBy ;

4.1.3 Currency Oriented Find Statement

recordName
#find  setName    [ retaining clause ] ;
keeplistName

This form of find has different effects depending upon its argument. If the parameter is a recordName, then the current of record for that type is selected. If it is a setName, then the current of set for that type is selected. If it is a keeplistName, then the front entry of the specified keeplist is selected and removed.

Examples:

#find Student ;
#find OccupiedBy ;
#find TempKeep ;

4.2 Store Statement

#store recordName [ retaining clause ] ;

The store statement adds a new record to the specified record type, and data is copied from the user work-area template for that record type to it. You need to be sure that the data to be stored is first placed into the template before the store command is carried out.

The newly-created record is also added to current occurrence of any sets with insertion automatic of which it is the member type. If the record being created is the member type of such a set, you also need to ensure that the currency pointer(s) for each such set is pointing to the current occurrence before the store statement is executed.

The new record becomes the current of run unit, current of record for its record type, and current of set for all sets of which it is the owner type or an automatic member. Updating of currency pointers may be selectively repressed via use of a retaining clause (discussed below).

Example:

#store Student ;

4.3 Record Fetch/Update Statements

The following commands are used to access and alter records in the database. Each of these statements operates on the record pointed to by the current of run unit, and the recordName argument is optional, and only included for the sake of documentation and clarity for the reader. If it is specified, it must be a declared #record, and it must match the record type that is the current of run unit when the statement is executed. Otherwise, the statement will fail.

4.3.1 Get Statement

#get [ recordName ] ;

The get command copies data from the current of run unit record on disk to the template for its record type in the user work-area.

Many users run into problems by forgetting this statement after they have
found some record and before they try to test some attribute of that record. Remember, you cannot access a record on disk via a currency pointer; you must first bring it into main memory. Don't forget the get!

Example:

#get Student ;

4.3.2 Modify Statement

#modify [ recordName ] ;

The modify command copies data from a user work-area template in memory to the current of run unit record on disk.

This statement is not analogous to store. The modify command alters records which already exist in the database. The store command creates new records. If you try to use the two interchangeably, you'll have problems.

Example:

#modify Room ;

4.3.3 Erase Statement

#erase [ all ] [ recordName ] ;

The erase command deletes the current of run unit record. If the all keyword is omitted, erase does away with any member records of set occurrences the record being deleted owns, but only if the set has retention fixed. If the all keyword is included, erase deletes all member records of set occurrences the record being deleted owns, regardless of the retention rule of the set.

In either case, the erase statement recursively deletes member records of sets owned by set members according to the same rules. An erase statement without the all keyword will fail if any record being deleted owns a non-empty set occurrence of a set with retention mandatory.

Since the erase command deletes the current of run unit, the current of run unit pointer and all other currency pointers referring to that record will point to an empty slot in the database after the statement is executed. Thus, subsequent attempts to access that record directly will fail. However, statements like find next will still work properly. (It will look up the record after the one just erased.)

Example:

#erase all Room ;

4.4 Set Modification Statements

The following statements allow you to alter set memberships. In all cases, recordName must be a declared #record, and setName a #set. The value in recordName must also refer to the member record type of setName. The record affected is the current of run unit, which must be of the type specified by recordName, or the statement will fail.

4.4.1 Connect Statement

#connect recordName to setName [ retaining clause ] ;

The connect command makes the specified record a member of the specified set type, connecting it to the set occurrence which is its current of set. Obviously, the record to be connected to the set should not be a member of that set prior to the connect statement. The connected record becomes the current of set for the specified set type, unless updating of set currency is suspended by a retaining clause.

Example:

#connect Student to OccupiedBy ;

4.4.2 Disconnect Statement

#disconnect recordName from setName [ retaining clause ] ;

The disconnect statement removes the specified record from the set occurrence of the specified set of which it is a member. Of course, the record must be a member of the set in order to be disconnected from it. The disconnect command is illegal for a set declared with retention fixed or mandatory. After a record which was current of set of a set is disconnected, the current of set for that set becomes undefined. However, find next . . . within and find owner statements will still execute correctly.

Example:

#disconnect Student from OccupiedBy ;

4.4.3 Reconnect Statement

#reconnect recordName within setName [ retaining clause ] ;

The reconnect statement transfers the specified record's membership from the set occurrence of which it is currently a member to the set occurrence which is the current of set for the specified set type. The reconnect command is illegal for a set declared with retention fixed. As with the connect statement, the reconnected record becomes the current of set for the specified set type, unless a retaining clause keeps it from doing so.

Example:

#reconnect Student within OccupiedBy ;

4.5 Keep Statement

[ recordName ]
#keep [ setName ] using keeplistName ;
[ keeplistName ]

The keep command adds the specified entity (or the current of run unit if none is specified) to the end of keeplistName. The values of recordName, setName, and keeplistName(s) must each be a declared #record, #set, and #keeplist, respectively. If a recordName is given, the current of record for that type is inserted. If a setName is provided the current of set for that set type is appended. If a keeplistName is specified, the front of that keeplist is used. In any case, no currency pointers are altered.

Examples:

#keep Student using TempKeep ;
#keep using TempKeep ;

4.6 Retaining Clause

               record            record
. . . retaining  [ set ] setName   < , [ set ] setName > ;

As indicated above, the various find statements and the store statement normally make the record accessed current of run unit, current of record for its record type, and current of set for all sets in which it participates. Also, the connect, and reconnect commands make the record involved current of set for the specified set. Sometimes it is necessary to suppress the updating of currency for the record type and/or one or more set types when one of these operations is performed. This can be done by appending a retaining clause to the end of the statement.

Specifying record suppresses the updating of currency for the record type accessed by the statement. Specifying one or more sets keeps their corresponding current of set pointers from being changed. A successful statement with a retaining clause will still update all currency pointers affected by that statement and not mentioned in the clause.

No other statements besides find, store, connect, and reconnect may have retaining clauses attached to them. In the case of connect and reconnect, only the setName involved in the operation may be specified.

Examples

#find Student retaining set OccupiedBy currency ;
#find owner within OccupiedBy retaining record currency ;
#store Student retaining record, set OccupiedBy currency ;
#connect Student to OccupiedBy retaining OccupiedBy currency ;

4.7 Set Membership Conditional Statements
The following conditional statements test whether the current of run unit points to a record with is a member of some occurrence of the specified set, and the conditional returns true if it is. The test can be inverted by use of the C++ ! operator. In both cases, this setName must be a declared #set.

#if ( [ ! ] member setName . . . ) . . .
#while ( [ ! ] member setName . . . ) . . .

These conditionals translate into C++ if or while statements, and anything appearing on the line after the last keyword (normally a closing parenthesis or a Boolean connective such as &&) will be passed on to the pure C++ code unchanged. The conditions themselves are converted into C++ Boolean expressions. The entire conditional expression can should be followed by a typically C++ statement block.

Upon evaluating these conditionals, the utility variable Netsim::status will be set to SUCCESS unless they cannot be tested for some reason (e.g. the current of run unit is not defined, or the current of run unit is not of the correct record type to be a member of the specified set). A condition which cannot be tested evaluates to false.

Examples:

#if ( member OccupiedBy ) . . .
#while ( ! member OccupiedBy && ! done ) . . .

Note that these conditionals are defined for NETSIM C++, but not for the general DBTG model on which the system is based.

5.0 Utility Variables and Methods

5.1 The Netsim Class

The NETSIM C++ library declares a manager class called Netsim. This class has several methods and instance variables which may be useful to your application programs. A partial declaration of the Netsim class is given below.

class Netsim
{
  public :
    enum Status { SUCCESS, NOT_FOUND, ALREADY_CONNECTED,
        EMPTY_KEEPLIST, NO_CURRENT,
        NOT_CONNECTED, NOT_CRU,
        NOT_MEMBER_TYPE, RECORD_SIZE_MISMATCH,
        RETENTION_VIOLATION } status ;

    static void signal ( bool abort = true ) ;
    static void netdbg() ;
    static int setAutomaticSignals ( bool set = true ) ;

    static Status status ;

  private :
     // These variables and methods are not visible to the user.
     // ...

} ;

This declaration is available to any application program which #includes the NETSIM C++ library.

5.1.1 Status Variable

The variable Netsim::status is set by all of the DML statements outlined above. It is set to Netsim::SUCCESS if the statement succeeded and something else if it failed. The possible values which Netsim::status can take on are explained as follows (each prefaced by Netsim:: ):

Value
Interpretation

SUCCESS
The last operation performed was successful.

ALREADY_CONNECTED
You attempted to connect a record to a set it was
already connected to.

EMPTY_KEEPLIST
You tried to find the front of an empty keeplist.

NO_CURRENT
The current of run unit, record, or set pointer is
undefined.

NOT_CONNECTED
You attempted to disconnect a record which was
not connected to anything.

NOT_CRU
You attempted to manipulate a record that was not
the current of run unit.

NOT_FOUND
A find operation failed to locate a matching record.

NOT_MEMBER_TYPE
The record you specified is not the member type of
the specified set.

RECORD_SIZE_MISMATCH
The record size for a record type specified in its file
does not match the size in its declaration.

RETENTION_VIOLATION
The last operation would result in a violation of
the retention rule of some set it manipulates.

Note that, in general Netsim::NOT_FOUND is the only "benign" status code other than Netsim::SUCCESS. In most cases, other values for Netsim::status indicate a problem in the logic of your program.

It will probably be useful for you to test the value of Netsim::status throughout your program. You can write statements like the following.

if ( Netsim::status == Netsim::NOT_FOUND ) . . .
while ( Netsim::status == Netsim::SUCCESS ) . . .

5.1.2 Error Signaling

The signal() method of Netsim is called by the NETSIM C++ library when an operation returns a status other than Netsim::SUCCESS or Netsim::NOT_FOUND. It indicates that there was a problem with this last operation, and prints a message describing what failed.

The Netsim::signal() method takes a Boolean parameter, which has true for its default value. If this parameter is true, the signal() method will alert the host operating system of an error and trigger a stack dump. If it is false, the method will return control to its caller after printing its error message. (Error messages are described below.) If signal() is called when the current status is Netsim::SUCCESS, it will simply print a descriptive message, regardless of its parameter value.

Your applications may call Netsim::signal() using the declaration given above as a model. A good place to do this is at a point where you know that a DML statement has just returned an erroneous status.

Examples:

Netsim::signal() ;
// aborts after printing an error message
Netsim::signal( false ) ;
// always returns to caller

5.1.3 Setting Automatic Signaling

The signal() method is typically called when an erroneous status occurs. (NOT_FOUND is an exception.) You may turn this behavior off and on with the Netsim::setAutomaticSignals() method. This method takes a Boolean parameter which has a default value of true. If the parameter is set to true, automatic signaling for errors will be turned on. If it is false, signaling will be turned off.

Examples:

Netsim::setAutomaticSignals( ) ;
// turns on automatic error signaling
Netsim::setAutomaticSignals( false ) ;
// turns off automatic error signaling

5.1.4 Calling the Interactive Debugger

You can use the netdbg() method to activate the NETSIM C++ interactive debugger from within your program. Specifics of the debugger are detailed below.

Example:

Netsim::netdbg( ) ;

5.2 The Date Class

The NETSIM C++ library includes a Date class (declared in date.h) which facilitates working with date fields in records. A partial declaration of the Date class is given below;

class Date
{
  public :
    Date ( ) ;                        // Constructors.
    Date ( const Date & date ) ;
    Date ( const char * representation ) ;

    Date & operator = ( char * representation ) ;    // Arithmetic
    Date & operator = (Date & date);             // operators
    Date & operator += ( int offset ) ;
    Date & operator -= ( int offset ) ;
    Date & operator + ( int offset ) const ;
    Date & operator - ( int offset ) const ;
    int & operator - ( const Date other ) const ;

    bool operator < ( const Date & other ) const ;    // Logical
    bool operator <= ( const Date & other ) const ;   // operators.
    bool operator == ( const Date & other ) const ;
    bool operator != ( const Date & other ) const ;
    bool operator >= ( const Date & other ) const ;
    bool operator > ( const Date & other ) const ;

    static Date stringToDate ( const char * representation ) ;  // Auxiliary
    static char * dateToString ( const Date & date ) ;        // methods.

    friend ostream & operator << ( ostream & stream, const Date & d ) ;

  private :
    // These methods and variables are not visible to the user.
    // . . .

} ;

The public methods and operators of the Date class can be called on Date objects which your program defines. (Typically, your program will define Date objects indirectly, as fields of records. The preprocessor program will convert these field declarations into Date object instantiations. You will need to call methods in a manner like the following.)

recordName.fieldName.dateMethod

The two static methods should not be called on objects, but in the manner appropriate for C++ static methods (i.e. Date::stringToDate ( string ) ; ). Note that you can also declare Date variables explicitly within your application programs. The methods and operators declared above are detailed as follows:

5.2.1 Constructors

The first three methods listed in the Date class are constructors. The first takes no parameters and initializes that Date variable being created to today's date. The third takes a character representation of the form dd-mm-yyyy and assigns this date to the new instance. (Note that a character representation for a Date must always contain two digits for its day number. Also, the abbreviation for a month name in a Date character representation must be in all capital letters. Thus, "13-FEB-1997" and "03-JUN-2001" are valid character representations, while "8-MAR-1994" and "14-May-1991" are not. )

5.2.2 Arithmetic Operators

The next six operators declared allow arithmetic manipulation of dates. The first one allows a valid character representation to be assigned to a Date object. The next four allow a Date to be incremented or decremented by a certain amount, given by the offset parameter. The sixth operator computes the difference between two dates, taking its Date parameter as the second of these.

5.2.3 Logical Operators

The final six operators defined for the Date class allow comparison between two dates, with the second of these being contained in the Date parameter.

5.2.4 Auxiliary Methods

The two auxiliary methods allow for conversions from a character representation to a Date object and vice versa. An error in these conversions will result in a BAD DATE value being returned instead of a string or Date.

Friend Extraction Operator

The final declaration in the public section of the Date class allows instances of the class to be output via the extraction operator declared in the ostream class.

6.0 Interactive Debugger

NETSIM C++ provides an interactive debugging system which allows you to view information about items in your database. It can also help you to track down and eliminate errors in your application programs.

The debugger is entered by calling the Netsim::netdbg() function from within your program. (This function is included in the NETSIM C++ library.) You may also enter the NETSIM C++ debugger from within your system's debugger. Use your debugger's mechanism for calling a procedure by name to call the netdbg() function (not Netsim::netdbg()).

6.1 Preliminary Information

When the NETSIM C++ preprocessor encounters declarations for records, sets, and keeplists in your DDL files, it converts them to C++ class declarations. Each instance of these classes is assigned a type which conveys what kind of network database structure (e.g. record, set, keeplist) it represents, as well as the name you gave it in its DDL declaration. Additionally, record class instances are assigned numbers which identify them while your program is running.

Note that, in the debugger, you still refer to records, sets, and keeplists by the same names you gave them in their DDL declarations. The description provided above is intended to help you better understand how the NETSIM C++ system works, and the meaning of the data printed for you by the various debugger commands.

6.2 Debugger Commands and Syntax

The NETSIM C++ debugger will prompt for a command as follows:

NETDBG >

At this prompt, you may enter any of the following commands, always typing the command on a single line and pressing return to terminate it.

6.2.1 List Command

There are several formats to the list command (as well as others). The syntax of each is given below followed by their meanings.

list [ all ]
Generic list command

list [ recordName [ all ] ]
Record oriented list command


[ owner ]
list [ setName [ all ] ]
Set oriented list command

list [ keeplistName [ all ] ]
Keeplist oriented list command

The list command gives a one line description of each selected record, set, or keeplist which includes its number and type. The various operations possible with list are described as follows:

list
(by itself) Gives current of run unit and the value of
Netsim::status.

list all
Gives the current of run unit, the current of record for all
record types, the current of set for all set types, the front
entry in all keeplists, and the value of Netsim::status.

list recordName
Gives the current of record for the specified record type.

list recordName all
Gives all records of the specified record type.

list setName
Gives the current of set for the specified set type.

list setName owner
Gives the owner record of the current occurrence of the
specified set type.

list setName all
Gives the owner record and all member records of the
current occurrence of the specified set type.

list keeplistName
Gives the front entry in the specified keeplist.

list keeplistName all
Gives all entries in the specified keeplist.

6.2.2 Show Command

The show command gives a two line description of each selected record. This includes its number and type on the first line, and its contents (as much as will fit on one screen line) on the second line.

The show command is similar to list, except that it gives a little more information. The syntax and meanings of show are the same as for list, as described above. You should refer to them for details, replacing list with show.



6.2.3 Display Command

The display command gives a description of each selected record which extends over several lines. The record's number and type of this appear on the first line, followed by the names and values of each of its fields and sets it participates in, each on one line.

The display command is the most verbose of the three just outlined. It, too, has syntax and meanings like those given for list, and you should refer to these for more detail.

6.2.4 Find Command

The find command temporarily repositions all currency pointers to an record which you specify. There are two variants.

find recordName recordNumber

This first version of the command can be used to locate a specified record number within a record type.

first
find   next    recordName [ using fieldName = value ]
any
duplicate


This second form of the find command behaves like its corresponding DML statement, except that the using clause is a bit different. Here, you specify the name of a field and the value it should contain.

Example:

find next Student using name = "Karen"

All currency pointers altered by the find command are restored to their original values when the debugger was entered upon exiting the debugger. You can also explicitly reestablish these values from within the debugger with the restore command.

6.2.5 Restore Command

restore

The restore command resets all currency pointers to the state they were in when the debugger was entered. This command is executed automatically when the debugger is exited.

6.2.6 Signal Command

signal

The signal command invokes the Netsim::signal() method to display the full text associated with the current value of the Netsim::status variable.

6.2.7 Help Command

help

The help command displays a text file (included with the NETSIM C++ system) which contains a brief synonpsis of the functionality of the interactive debugger.

6.2.8 Exit Command

exit

The exit command returns control to your program after first restoring all currency pointers to the state they were in when the debugger was entered.

7.0 Error Messages

From time to time, you may encounter problems with your NETSIM C++ application programs. These may be in relation to the preprocessor program, the signaling method (in the NETSIM C++ library), or the debugger. This section explains the various error message which may be printed, and tries to direct you as how to proceed to fix the problem.

7.1 Preprocessor Messages

The preprocessor program will report errors if it sees things like invalid syntax in DDL declarations or DML statements, or DDL declarations that are illegal. If this happens, it will print the line number at which the error occurred, and a brief message describing the problem. These messages are listed and explained below. A blank in a message corresponds to an entity directly involved in the error.

"Saw ____ when expecting ____"

The preprocessor was parsing a statement and found something it was not expecting. Check the syntax for the statement in question to see why this happened.

"Non-positive string length: ____"

A record field of type string has been declared with a length which is less than or equal to zero. Give this field a positive length.

"Duplicate declaration of record: ____"

"Duplicate declaration of set: ____"

"Duplicate declaration of keeplist: ____"

You tried to redefine a record, set, or keeplist (depending on the message printed) which already exists. Give the item in question a different name.

"Set: ____ OWNER record undeclared: ____"

"Set: ____ MEMBER record undeclared: ____"

You attempted to declare a set with an owner or member record type, respectively, that does not exist. Check the spelling of the name of the record in question, and make sure that it has been previously defined in the DDL file.
"Record type ____ is not member type of set ____"

You attempted to connect a record to a set occurrence of the specified set type for which it is not the member type. You probably intended to do something else here. Check to see if you meant to connect this record to a different set, or a different record type to this set.

"Multiple occurrences of RECORD in RETAINING clause"

You can only have one instance of the record keyword in a retaining clause. Take one of these keywords out of the clause.

7.2 Signaling Method Messages

If your application program explicitly calls the Netsim::signal() method while it is running, this method will report on the state of the Netsim::status variable. Most messages which signal() can print indicate that there is some problem with the program, but not all of them. (In particular, if the status variable has the values SUCCESS or NOT_FOUND, the message issued will not indicate an error.) Note that if the signal() method is called automatically, it will always print an error message. The messages with Netsim::signal() can print are described below.

"Last operation succeeded."

The last network database operation was performed without any problems, and Netsim::status equals Netsim::SUCCESS. This is not an error message.

"Attempt to CONNECT a record to a set to which it is already connected."

Your program has tried to connect some record to the same set multiple times. (Netsim::status equals Netsim::ALREADY_CONNECTED.) Check the logic of your program, and make sure that this cannot happen before recompiling.

"Attempt to FIND front of an empty keeplist."

A find statement has tried to locate an entry in a keeplist empty. (Netsim::status equals Netsim::EMPTY_KEEPLIST.) Examine the find commands in your program which manipulate keeplists to uncover the source of the problem.

"Current of run unit, record, or set is undefined."

Your program has tried to manipulate a currency pointer which is undefined (for example, just after an erase statement has been completed). (Netsim::status equals Netsim::NO_CURRENT.) Look through your program for suspicious looking sequences of database commands like the one just described to locate the problem. "Attempt to DISCONNECT a record that was not connected."

Your program has tried to remove a record from a set occurrence of which it is not a member. (Netsim::status equals Netsim::NOT_CONNECTED.) Check the logic of the disconnect statements the program, and make sure that each one is doing what it is supposed to.

"Attempt to manipulate a record that is not current of run unit."

Your program has tried to store, modify, erase, connect, reconnect, disconnect, or keep a record which the current of run unit pointer is not pointing to. (Netsim::status equals Netsim::NOT_CRU.) This is a fairly common and tricky error. You need go through your program and make sure that each time a record is operated upon, it is the current of run unit. The debugger may be of help here.

"FIND operation failed to find a matching record."

Your program tried to locate some record with a find statement, but was unable to do so. (Netsim::status equals Netsim::NOT_FOUND.) This is not necessarily a bad thing. In fact, you will probably want to explicitly check for this happening in your program.

"Record specified is not member type of specified set."

Your program has tried to manipulate a record in the context of a set of which its type is not the member type. (Netsim::status equals Netsim::NOT_MEMBER_TYPE.) This problem may arise in conjunction with connect, reconnect, or set oriented find commands. Examine occurrences of these throughout your program to uncover the error.

"Record size specified in file does not match size of declaration."

The declaration for a record type in a DDL file which your program accesses has been altered (the total record size has changed). Your program has tried to use an old version of a database file with this new record declaration. Either discard the old file and create a new one for the new declaration, or change the record declaration to what it originally was when the database file was created.

"Operation would result in set retention rule violation."

Your program has tried to execute a statement which would break the retention rule of some set. (Netsim::status equals RETENTION_VIOLATION.) This is probably the result of a logic error in a connect, reconnect, or disconnect command. Look for problems with these statements in your program.

7.3 Debugger Messages

The interactive debugger will generate error messages due to syntax problems with the commands you enter. Specific messages are detailed below.

"'ALL' or record, set, or keeplist name expected."

You have entered a list, show, or display command which is invalid. In particular, the second word of the command might be incorrect. Check the syntax outlined above for dealing with specific situations.

"Record name, 'FIRST', or 'NEXT' expected"

You have entered a find command whose second term is invalid. Check your spelling, and if you are entering a recordName (in the case of the first variation of the find statement), make sure that it is a declared record type.

"Record number expected"

"Integer expected"

"Record number out of range"

These messages pertain to the first variant of the find statement (as described above). If you enter an illegal value for the recordNumber, one of these messages will result. Check to see that you have entered a positive integer that does not exceed the number of records in the database for this value.

"Record name expected"

"No current of record for this type"

These messages are associated with the second for of the find command, at the point where the debugger is looking for a recordName. Be sure that the name you enter here is a declared record type and that its current of record is defined.

"'USING' expected"

"Field name expected"

"'=' expected"

"String value expected"

"Numeric value expected"

"Date expected"

"Real number expected"

These messages all relate to the using clause of the second version of the find statement. If the syntax of the fieldName, value, or any other part of the clause which you specify is invalid, the debugger will print one of these messages. Be sure that the first word in your using clause is, in fact, using. Make certain that you have provided a fieldName which is a field of the record specified, and that it is followed by an equals sign. Also, check to see that the value you have entered has the same type as that of the field it corresponds to.

"Excess text at end of line"

This messages indicates that you have type extra characters or words at the end of a command. Make sure that you just enter the terms you need in a debugger statement, and not more.

"Unrecognized command verb. Type help for help."

You have entered a debugging command which is unknown to the system. You may have made a spelling error. If you do not understand what happened, get help as specified.

"Unable to open help file"

You have typed the help command, but NETSIM C++ is unable to access the help file for some reason. Check to make sure that this file is available to the system.