|
|
Another obvious difference between the G2++ and G2 programming language interfaces is that G2++ uses String(C++) everywhere that G2 uses character arrays. Two advantages of Strings are (1) Strings are easy and natural to manipulate and (2) you never have to worry about string overrun. String overrun can be a particularly nasty problem in G2 when using strcpy(S) to store characters into a character array member of a structure defined by g2comp. To illustrate, consider the original usr.g file in the section ``A G2 Example''. Compiling this file with the G2 compiler g2comp generates the following type definition:
typedef struct USR{ char login[6+1]; struct{ long usr; short grp; }id; char name[20+1]; long proj[4]; }USR;
When a client program stores characters into the login field, it must take care to assign no more than seven characters, including the terminating null byte. If it does assign more than seven, the excess characters may run over into the id field, with disastrous results:
main.c - C program #include "usr.h" main(){ USR u; strcpy(u.login,"hello world"); // overrun! putrec(&u,usr,stdout); }
String overrun can't happen in G2++. Compiling the same file usr.g using g2++comp(CP), generates the following type definition:
typedef struct USR{ String login; struct{ long usr; short grp; }id; String name; Vblock<long> proj; USR(); }USR;
Note that the login field is now of type String rather than char[]. The possibility of overrunning the login field no longer exists, even though usr.g declared the field as having a maximum length of six characters:
main.c - C++ program #include "usr.h" main(){ USR u; u.login = "hello world"; // no overrun! cout << u; }
If we inspect the u.login field immediately after the assignment statement, we will find that it contains all eleven characters of the string "hello world", even though usr.g declared the field as having a a maximum length of six. Is this a problem? The answer is ``No:'' although we can grow the login field to an arbitrary size by assigning strings of any size to it, the inserter will not write out more than the six characters we declared as its maximum size. Similarly, the typed extractor will ignore characters in excess of six when reading from an input stream. This behavior is strictly compatible with G2. In the next section, we will see that G2++ also allows users to define records containing arbitrary size strings.
Strings also show up in untyped I/O, where they are used instead of character arrays as the types of the name and val fields of a G2NODE:
g2++.h struct G2NODE{ String name; String val; G2NODE* next; G2NODE* child; }; struct G2BUF{ ... G2NODE* root; ... };
As a result, untyped extraction is guaranteed to work (barring heap exhaustion) even when reading records with huge value fields. This capability was needed to guarantee the integrity, under manipulation by programs using untyped I/O, of records containing external representations of abstract data types.