
Hello,

Thank you for your interest in POET.  For more information on POET please contact:

	POET Software Corporation
	4633 Old Ironsides Drive, Suite #110
	Santa Clara CA 95054
	Sales (408) 970-4640
	Fax (408) 970-4630

Best Regards,

Brad Sturtevant
POET Software Technical Support
Compuserve: 70402.74
Internet: 70402.74@compuserve.com



POET TECHNICAL OVERVIEW                              April 20, 1993


POET (Persistent Objects and Extended database Technology) is an 
innovative object-oriented database development system for C++ 
applications.  POET has received a variety of awards, including Computer 
Language's Jolt award for product excellence, Computer Language's 
productivity award, and BYTE Magazine's award of distinction.

POET is truly object oriented. It supports fully all aspects of C++ objects, 
including encapsulation, inheritance, polymorphism, object identity, and 
references among objects.  POET also gives you full database functionality, 
including queries, device independent formats, and sharing of objects 
among programs in a multi-user environment.  Database operations 
manage your C++ objects directly without programmer intervention.  
Instead of making you understand our database formats, we have taught 
our database to understand your objects.

POET is available on MS-DOS, MS-Windows, MS-NT, Macintosh, OS/2, 
NeXT, SCO, Interactive, Sun, and various other flavors of UNIX.  Both 
single-user and client/server versions are available.  Heterogeneous 
networks are also supported--no matter what computer your application 
runs on, it can use any server on the network.  Because POET uses the 
same database format for all systems, objects stored by one computer can 
be used on any other computer.  


OBJECTS NEED POET...  


Applications continue to become more complex, and often require 
immense effort to develop and maintain.  Object oriented techniques let 
the structure of your program closely reflect the structure of the problem 
to be solved.  Related code and data are combined into objects, and C++ 
has rich features for expressing the structural and semantic relationships 
among objects.  Object oriented programs are modular, easy to understand, 
easy to maintain, and easy to extend.  This significantly reduces both 
development and maintenance costs, which explains the rapid growth in 
object-oriented programming.

Conventional database systems (relational database, ISAM, b-tree, etc.) 
manage their data in two dimensional tables.  Complex structures must be 
broken up and distributed among these tables, which offer no real support 
for the object oriented programming model.  As a result, many programs 
stop being object oriented whenever they use the database.  The program 
and the database have two different designs, and the program must 
constantly convert back and forth.  This significantly increases both 
program complexity and run time overhead.

A conventional database is like a garage which forces you to take your car 
apart and store the pieces in little drawers.  You can do it, but you 
probably don't want to.  POET lets you store even complex C++ objects 
directly, without modification.  Only object oriented databases can store 
complex objects 1:1.

POET's database operations preserve all aspects of your C++ objects.  
References among objects are preserved, as is the full polymorphic 
structure of each object.  Only one command is needed to store, read, or 
delete an object, now matter how complex the object is.  For instance, you 
could use one command to store a CAD drawing with many shapes, a document 
with many paragraphs and images, or an invoice with many items. The 
individual shapes, paragraphs, images, or items are objects in their own 
right, and can be processed independently in other parts of your program.

Database management with POET requires a fraction of the effort and a 
fraction of the code.  Less code means less work--your programs are easier 
to develop and maintain.


POET -- A THUMBNAIL SUMMARY


POET Keywords.  POET extends C++ syntax to allow you to specify 
database attributes directly in your class declarations.

POET Precompiler.  The precompiler translates POET's extended C++ 
declaration syntax into ANSI C++ header files which you include in your 
program.  It also adds your class declarations to the class dictionary of 
the database

POET Tool Classes.  POET's tool classes provide the C++ interface to the 
database.  In addition, they allow management of sets and query 
specification.

POET Run Time System.  The run time system manages the database and 
processes queries.  In multi-user systems it is part of the server; in 
single-user systems it is linked to the application.


PROGRAMMING WITH POET


In the next section we would like to illustrate POET programming with an 
example.  We will design an inventory management system for a computer 
supply store and show how it would be implemented with POET.

    Scenario : Inventory Management

An inventory system must manage a wide variety of data.  Our store sells 
hardware, software, literature, services, and accessories.  We need to 
generate packing lists and invoices which list the items we sell.  Our 
database must keep track of manufacturers, distributors, customers, and 
employees.  The first step in object oriented design is often to list the 
objects in the system and to group related objects.  Delivery notices and 
invoices are both documents.  We will call anything that can be listed on 
an invoice an item.   Manufacturers, customers, and employees all have 
names and addresses.  For our purposes we can call them all persons.

Our database will have to manage objects of the following types:

Item           	Hardware, Software, Literature, Service, Accessory 
Document       	Delivery Notice, Invoice
Person         	Manufacturer, Distributor, Customer, Employee

Representing the individual objects is not enough; we must also represent 
the relationships among objects, and doing this naturally is central to the 
design of the program.  Consider an invoice: An invoice contains the 
address of the customer and a list of items.  Each item refers to its 
manufacturer.  We want to represent these relationships directly in our 
database design.  Direct Integration in C++ 

    Direct Integration in C++

Because C++ is tightly integrated into the C++ programming language you 
do not need to convert C++ objects for the database.  POET manages your 
C++ objects, and it carefully follows the semantics of C++.  Database 
classes are defined using C++ class declarations with a few simple 
extensions.  POET does not need a separate class definition; your class 
declarations are the only database schema you need.  Your database objects 
are your program objects.  This dramatically simplifies database design.

Let's start with the base classes "Person" and "Item".  These classes 
represent the Person and Item sets in the previous diagram.  They will need 
data and functions for input and output.  The declarations below are 
normal C++ class declarations except for one keyword: persistent.  Any 
class declared with the persistent keyword can be stored in the database.

persistent class Person
{
private:
    PtString Name;
    PtString Firstname;
    PtString Street;
    ...
public:
    virtual int Input (..);
    virtual int Print (..);
};

persistent class Item
{
private:
    int ItemNumber;
    PtString Description;
    ...
public:
    virtual int Input (..);
    virtual int Print (..);
};

Now POET has declarations for the set of all Items or the set of all Persons. 
As shown in the diagram, each of these sets has subsets.  A Software is an 
Item, but not all Items are Software.  All items have the data and the 
methods needed to list them on an invoice.  A Manufacturer is a Person, 
but not all Persons are Manufacturers.  All persons have addresses.  How 
do we represent these subsets?

C++ represents subsets by using inheritance.  POET ensures that this 
inheritance always works properly for objects stored in the database.  In 
the following declaration, persistent class Software: public Item means that 
a Software is an Item and has all functions and data that any other Item has.

persistent class Software : public Item
{
private:
    PtString Release;
public:	
    ...
    virtual int Input ();
    virtual int Print();
};

persistent class Hardware : public Item
{
private:
    ...
public:
    virtual int Input();
    virtual int Print();
};

persistent class Manufacturer : public Person
{
    ...
    cset<Item*> Products;	
    virtual int Input();
    virtual int Print();
};

The "Manufacturer" class contains the statement cset<Item*> Products;.  
This is a container which can hold a set of references to Items.  These 
Items can be "Hardware", "Software", or any other kind of item -- even if 
it is a new kind of Item developed after this declaration was written!

The above example shows how POET derives the structure of your objects 
and the relationships among them by reading your C++ class declarations.  
You simply add  POET's keywords.  Now let's see how the resulting 
database definition is integrated into your program.

    The POET Precompiler

Unfortunately, no C++ compiler in the world understands our language 
extensions.  Therefore, we have written a precompiler which produces code 
that can be compiled with any ANSI 2.0 C++ compiler.

Persistent class declarations are usually placed in files with the .hcd 
extension.  Our precompiler produces a header file with the .hxx extension.  
You include this file in your source files.

Of course, the POET precompiler does a lot more than this behind the 
curtains.  It manages the class dictionary of the database to ensure that 
all of your class declarations are registered an up to date.  It also 
produces code to tell the POET run time system how to manage your objects.

    Less Code, Less Work

Since POET can manage both your objects and their relationships without 
programmer intervention, your programs are smaller and simpler.  POET 
applications are easy to write.  A single line in a POET program is often 
equivalent to many lines of code in traditional database systems.

Suppose our program has just changed the name and business form of a 
manufacturer and added a few items to their product line.  Now we need to 
save these changes.  In conventional systems 
these objects are stored in a series of two-dimensional tables (Persons, 
Manufacturers, Item, Software) and your program must know how to 
change your objects into the rows of these tables.  In POET you need only 
one line: pManufacturer->Store();  POET's precompiler always generates a 
Store() member function for any persistent class, so you don't have to 
bother with the details.  The object is completely stored, and the 
references to the items in the product line are automatically updated.

Your application code is much shorter and easier to understand.  Less code 
also means fewer programming bugs.  Moreover, since the class dictionary 
resides in the server, you simply send exchange complete objects with the 
server instead of all the rows of individual tables needed for a 
conventional system.  This reduces the communications overhead for 
client/server applications.

Of course, you need an open database before you can store an object.  The 
following code is all you need to connect to a server, open the database, 
and store an object:

main()
{
     // A PtBase manages the connection to the database.
     PtBase base;
     base.Connect ("Local");
     base.Open ("test");
     // Create a manufacturer and read its data
     Manufacturer * pManufacturer = new Manufacturer (base);
     pManufacturer->Input();
     pManufacturer->Store();
     base.Close();
     base.DisConnect();
}

That's all the code you need!
pManufacturer->Store() is the member function that the POET precompiler 
automatically creates to store a persistent object.  You need some way to 
input the data; in this example we assume you have written a member 
function to do this, and pManufacturer->Input() calls this function.

PtBase is a POET class that manages the connection to the database.  
base.Connect() and base.DisConnect() are used to establish and break the 
connection to the server.  In the single- user version you use the server 
name "Local".  base.Open() and base.Close() open and close the database.

    POET's Object Management Model

You have already seen that any C++ class declaration can be made 
persistent simply by adding one keyword to its class declaration.  The 
precompiler translates your persistent class declarations into ANSI 2.0 
C++ code.  Once the database is open, storing a persistent object requires 
one line of code.  However, database programming is not a matter of 
storing individual objects.

A database system must provide a clear and consistent programming model 
for expressing and managing the relationships among objects.  This is 
where POET really shines.  When you program in POET you program in 
C++.  POET carefully follows the semantics of C++ to provide powerful 
database functionality using the programming model that every C++ 
programmer already knows.  C++ inheritance, polymorphism, and pointer 
references provide POET with an elegant model for database 
programming.  All the classes you need to manage the database are 
provided, and are used like any other C++ class library.

AllSets: Every Object for a Given Class

Whenever the POET precompiler encounters a persistent class declaration 
it creates a set to hold all objects of this type.  This set is called an 
AllSet, and the name of the AllSet depends on the name of the class.  In 
our example we have defined the classes "Person", "Manufacturer", "Item", 
"Hardware", and "Software".  The POET precompiler would generate the 
AllSets "PersonAllSet", "ManufacturerAllSet", 
'ItemAllSet", "HardwareAllSet", and "SoftwareAllSet" to hold objects for 
these classes.  When you store the object it is added to the appropriate 
classes. Since C++ objects can belong to several classes, a POET object 
may belong to several AllSets.  

Of course, an object can belong to more than one set.  In our design every 
"Manufacturer" is also a "Person".  Whenever a Manufacturer is stored it 
will be added to both the ManufacturerAllSet and the PersonAllSet.

POET's AllSets, like all other sets, use the same inheritance as the objects 
they contain.  since a "Manufacturer" is a "Person", a ManufacturerAllSet 
is a PersonAllSet.  If you have a function that browses a PersonAllSet then 
you can also use this function with a ManufacturerAllSet.

Reading Objects

When POET creates an AllSet it automatically derives it from PtAllSet, 
giving it a variety of methods for managing objects of a given class.  
Suppose you want to read the first Person from the database.  You simply 
call the Get() method of the PersonAllSet, telling it you want the first 
person:  POET reads the first Person object from the database and builds 
this object in your program's memory.  In the code below pPerson points to 
this first object:

PersonAllSet * AllPersons = new PersonAllSet( base );
Person * pPerson = 0;
...
AllPersons->Get( pPerson );     // read object from database
...
pPerson->Print();

When you read an object from the database it behaves just like the object 
that was stored.  Its member functions, data, and references are identical 
to the original object.

    Objects and Polymorphism

If you ask two musicians to play a song, one may start squeezing an 
accordion while the other strums a guitar.  They are both musicians,  but 
they are different kinds of musicians and they respond to the same request 
in different ways.  If they did not know how to play a song, though, they 
would not be musicians at all.

Similarly, in C++ objects derived from the same base class may call 
completely different functions in response to the same member function 
call.  This is known as polymorphism, and any C++ function can be used 
polymorphically if it is declared virtual and occurs in both a base class 
and derived classes.

In practice, polymorphism can often simplify code.  For instance, in our 
example we need to be able to compute tax for each item on an invoice.  
However, taxes are computed differently for different kinds of items.  In 
the States there is no sales tax for services, but sales tax is charged for 
hardware or software.  The easiest way to deal with this is to ask each kind 
of item to compute its own tax.  If we want to compute the total tax for an 
invoice we could do it like this:

double TotalTax = 0.0;
Item * pItem;
long i;

for (i=0; Items.Get(pItem, i, PtSTART) == 0; i++)
{	
    TotalTax += pItem->ComputeTax();
    Items->Unget(pItem);   // removes the item from memory
}

Because C++ supports polymorphism, each kind of Item can have its own 
virtual ComputeTax() function, and we do not need to know the derived 
type to call this function.  If the object is of type Hardware then 
hardware::ComputeTax() will be called, if it is of type Service then 
Service::ComputeTax() will be called.  In fact, if a new tax category is 
introduced in the future then we can create a new kind of Item with a new 
ComputeTax() function--the above code will call it correctly without 
change and without recompiling!

Note that you can write your C++ objects to the database and retrieve them 
without affecting polymorphism.  When you read an object it has precisely 
the same member functions that it had when it was stored.

    Navigation

The objects in your program can refer to each other, and so can the objects 
in your database.  For instance, an invoice has a list of Items, and each 
item has a Manufacturer.  Following the references among objects is called 
navigation.

Suppose we want to print a description of the Customer for the second 
Invoice in the database or print a description for the manufacturer of the 
third item on this invoice.  In a normal C++ program you would do this by 
following pointers in memory.  You may be surprised to know that you can 
also do this with 
objects that you read from the database!

When POET stores an object which has pointers it automatically converts 
these into database references. If you read this object from the database 
then POET loads the referenced objects and sets your pointers 
appropriately.  Sets of references are also resolved automatically.  To 
follow the relationships in your database, you simply follow the pointers 
in your C++ programs.  This makes it quite easy to make hypertext-style 
programs which follow the references in a database.

For our example, let's assume that an invoice has a pointer to the customer 
and a set of pointers to Items.  An Item has a pointer to its manufacturer:

persistent class Invoice
{
    ...
    Customer *pCustomer;
    cset <Item*> Items;
    ...
};

persistent class Item
{
    ...
    Manufacturer *pManufacturer;
};

The pointers and sets of pointers in the above declarations will be resolved 
automatically by POET whenever objects of these classes are read from the 
database.  This makes navigation quite simple:

...
InvoicesAllSet *Invoices = new InvoicesAllSet(base);
Invoice *pInvoice;

if (Invoices.Get(pInvoice, 1, PtSTART) == 0)   // get the second invoice
{
    pInvoice->pCustomer->Print();   // print the customer
    Item *pItem;
    if (Items.Get(pItem, 2, PtSTART) == 0)   // get  the third item
        pItem->pManufacturer->Print();   // print the manufacturer
}

What if the referenced object is deleted?  POET has referential integrity: 
it knows that the referenced object is gone, so it initializes the pointer 
to zero and returns a warning.   What if you have large objects or large 
sets of references that you do not want loaded into memory?  POET also 
supports other kinds of references that do not load the object until the 
programmer asks for it.

    Queries

Queries allow you to find all objects that contain specific values. Suppose 
you want to list all customers whose names start with "A*".  Query objects 
are used to specify queries.  The POET precompiler generates a query 
specification class for every persistent class it encounters.  The query 
specification class generated for Customer is called CustomerQuery.  If you 
want to state that the Name field should start with A, you can do this using 
the SetName function.  You can also tell the query how to sort the result 
set; for instance, we can also sort by Name.  CustomerAllSet has a member 
function called Query which takes your query specification and builds a set 
of matching objects.  The code might look like the following:

CustomerAllSet * AllCustomers = new CustomerAllSet (base);
CustomerSubSet * Result = new CustomerResultSet;
CustomerQuery Query;
Customer * pCustomer = 0;

// Specify Query: All customers beginning with "A*" in ascending order.
Query.SetName("A*", PtMATCHES);
Query.SortByName(PtACSENDING);

AllCustomers-> Query(Query, Result);

Result->Get(pCustomer);

The results of the query are placed in a set.  You can read this set using 
Get(), just as you would with an AllSet.  In fact, all sets are derived from 
the same base class, PtObjectSet, so you can write one browser that can be 
used for result sets, AllSets, or any other sets in your program.

    Intelligent Client / Server Architecture

POET is available in both single-user and multi-user versions.  The multi-
user version of POET uses a client/server architecture.  Application 
programs can run on any computer on the network.  Access to the database 
is done by making requests to one central process, the database server. The 
server manages multi-user access and ensures that the database is kept 
consistent.  Because the server runs on the computer where the database is 
stored it can access objects quickly, without network overhead.  This 
computer is typically much more powerful than the computers that the 
client applications run on, which also improves system performance.  
Furthermore, since only the results of queries are sent to the clients 
network overhead is reduced significantly.

Multi-user databases need to provide features to ensure that updates made 
by different processes do not make the database inconsistent.  POET 
provides traditional features like locking and transactions.  It also it 
offers more innovative features--a program can ask the server to notify it 
when one of its objects is modified by another process, or to notify it 
when certain events occur during processing.

    Locking

A program can lock an object to ensure that no other process performs 
certain actions on that object.  Different kinds of locks prohibit different 
kinds of actions, such as reading, writing, deleting, or setting locks on 
the object.  A program can also lock a set, which means that a lock is set 
on all objects in the set.  A lock can also be set implicitly when an object 
is read from a set.

    Transactions

Transactions are used to ensure that a series of operations either succeed 
completely or fail completely without making any changes to the database.

Every PtBase has a transaction manager which is accessed using the 
member function Transaction().  When you start a series of logically 
related operations you call the Begin() function of the transaction manager.  
Now you can perform any series of database operations.  As far as your 
program is concerned the database acts as though all of these changes have 
actually been made, but in reality nothing has been written to the database.  
If operations succeed you can call the Commit() function to write all 
changes to the database.  If not, you simply call the Abort() function; the 
database now looks just like it did when 
you called Begin().

PtBase base;
Manufacturer * pMan = new Manufacturer (base);
Customer * pCust = new Customer (base)

pMan->Input();
pCust->Input();

//  We want to store both objects.  If one of the objects is locked then
//  store might fail. If it fails for one object, should fail for both.
base->Transact()->Begin();
if ((pMan->Store()  != 0) || (pCust->Store() != 0))
     base->Transact()->Abort();
else
     base->Transact()->Commit();

Does your program have many levels of nested logic?  No problem.  
Transactions can also be nested.  Each Abort() or Commit() applies to the 
current transaction level.

Incidentally, POET also has a second kind of transaction that is invisible 
to the user.  Any operation which changes a database is performed using our 
file-level transaction manager.  This ensures that changes either succeed 
completely or fail completely, and protects the integrity of your database.

    Watch & Notify

Sometimes it is perfectly acceptable for other processes to modify the 
objects you are using, but you want to ensure that your process always has 
the most recent version.  POET allows you to set a watch on object or set 
of objects; this means that the database will inform you whenever they are 
accessed or modified by another process.  You can also ask POET to 
update the object in your memory if it is modified by another program.  
POET does this automatically without further program intervention.

Suppose our inventory program is running on many different computers, 
and changes are being made to the database.  If an item is discontinued 
then we may no longer sell it.  We want to ensure that all programs using 
the database are informed instantly.  The traditional approach is to force 
all applications to reread items from the database if they may have changed.  
This increases the number of database accesses that are needed, slowing 
down the programs and increasing the network traffic.  Worse, the 
programmer has to ensure that the item is reread at many different points 
in the program.  Any part of the code that uses an object in memory can 
cause errors if it is critical to use the most recent version of the object 
and the programmer forgets to reread it.

In POET you can use the Watch() method to set a watch on an object or set 
of objects.  UnsetWatch() removes the watch.  The PtWatchSpec class is 
used to specify the operations that you want to know about, and the 
PtMethodRef class is used to specify the action that POET should take if 
this operation is performed by another program.  The server can notify 
clients when an object is modified in the database.  You can tell POET to 
update the object in your memory or to call a function in your program 
when this occurs.

....
pDistributor->Display();
//  Define the watch specification: if anybody updates the
//  distributor in the database, the POET server should call
//  our application's target function
PtMethodRef Target ( pDistributor, (PtFunc) &Distributor::Display()):
PtWatchSpec WatchSpec(PtWATCH_UPDATE, PtDEEP, Target);
//  Install the watch for the object. 
pDistributor->Watch (&WatchSpec);

Let's assume we have two clients using the same database on a network.  
Client 1 executes the code in the previous example.  Client 2 modifies the 
distributor and stores it.  This is what happens:

Time   Client 1                        Client 2

1      Installs watch using code 
       from above example
2                                      modifies and stores
                                       the product line for a 
                                       distributor

3      The POET server notices that 
       the watched object has been 
       modified, updates the distributor 
       in this client's RAM, and calls 
       the target function.


    Event Handling

An application is usually blocked while a database operation is performed.  
If the operation takes a long time then the application may need to gain 
control from time to update the screen, process user input, or allow the 
user to cancel the operation.

Event handling allows you to gain control when a database operation 
begins, ends, or when a certain number of objects have been processed.  
You simply give POET the address of a function which it calls when these 
events occur.  POET examines the return value of your function to see if 
it should continue.

In our example the application opens a window when the operation begins 
and periodically checks to see if the Cancel button has been pressed.  If 
so, it aborts the database operation.  When the operation is completed the 
window is closed.

The event handler can only call functions that are member functions of 
classes derived from PtCallback.  In our example we declare a class called 
ActionWindow.  It has three member functions to handle the window when 
processing begins, during processing, and when the operation is finished.


class ActionWindow : public PtCallback
{
public:

    int OpenWindow()
    {
        WindowSystem->OpenWindow(&MessageWindow);
        ...
        return PtEXCCONTINUE;  // continue database operation
    }

    int CancelButton(int percent)
    {
        WindowSystem->Message(&MessageWindow, "%d percent finished",
            percent);
        ...
        if ( ! CANCELBUTTON )
            return PtEXCCONTINUE;    // continue database operation
        else
            return PtEXCABORT;           // abort database operation
    }

    int CloseWindow()
    {
        WindowSystem->CloseWindow(&MessageWindow);
        ...
        return PtEXCCONTINUE;
    }

} myActionWindow;


Every PtBase has an exception manager which lets you install callback 
functions.  We will use three exception manager functions:

SetActionStarting()--Installs the callback function that will be called 
when a database operation begins.

SetActionPending()--Installs the callback function that will be called 
periodically during database processing.

SetActionTerminated()--Installs the callback function that will be called 
when a database operation is terminated.

Because C++ member functions need to know the address of their objects 
when they are called, you must specify both the object and the member 
function when installing a callback function:

ActionWindow myActionWindow;

base->GetExcMgr()->SetActionStarting (&myActionWindow, (PtFunc)
    &ActionWindow::OpenWindow);
base->GetExcMgr()->SetActionPending(&myActionWindow, (PtFuncInt)
    &ActionWindow::CancelButton);
base->GetExcMgr()->SetActionTerminated(&myActionWindow, (PtFunc)
    &ActionWindow::CloseWindow);

That's all there is to it.  Your functions will be called automatically 
when a database operation starts, during processing, and when the 
operation completes.


POET: An Overview


    C++ Language Support

o   Tight semantic integration with C++.
o   Any C++ object or structure can be made persistent by adding the 
    persistent keyword.
o   Storing and reading a C++ object does not change its state or behavior.
o   Full support for C++ encapsulation, object identity,  inheritance, and 
    polymorphism.
o   C++ pointers and references are automatically converted to database 
    references when storing objects.
o   Database references are automatically converted to C++ pointers and 
    references when reading objects.
o   All database definition is done through a small extension to C++ 
    declaration syntax.

    Database Functionality

o   Navigation
o   Queries
o   Sorting
o   Indexes
o   Single-user operation
o   Multi-user operation using client/server architecture
o   Flexible locking for objects and sets
o   Nested transactions
o   Watch & notify for objects and sets
o   Event handling
o   Database size limited only by hard disk size

    C++ Language Extensions

o   Persistence
o   Indexes
o   Transient data elements in persistent classes
o   Sets
o   Dependent objects

    PTXX-Precompiler

o   Automatically converts extended C++ class declarations into 
    ANSI 2.0 code
o   Registers classes in the class dictionary
o   Automatically generates POET Tool Classes
o   Provides class versioning

    Predefined C++ Classes

o   Predefined classes for date, time, strings, and BLOBS 
    (binary large objects) can save you development time

    Portability

o   All platforms are source-code compatible
o   Any POET database may be read by any computer
o   Full support for heterogeneous networks

    Platforms

o   Available for MS-DOS / MS-Windows (Borland C++, Microsoft C++, 
    Zortech C++), OS/2 (Borland C++), Novell, Macintosh (MPW), and 
    various Unix systems, including NeXT (NeXTStep), SCO (Liant C++), 
    and Sun OS (Sun C++).


POET Keywords


o   persistent--Defines database classes.  Instances of a persistent 
    class can be stored in the POET database.
o   transient--Defines non-persistent members of a persistent class.  
    Transient members are not stored in the database.
o   depend--Defines dependent sub-objects.
o   indexdef, useindex--Defines indices.  If available, indices are 
    automatically used for query optimization.
o   ondemand<type>--Defines special references among objects. Ondemand 
    references are explicitly loaded after object is read.
o   cset<type>, lset<type>, hset<type>--Define object containers that 
    may or may not swap objects to disk


POET Tool Classes


o   PtBase--Used for database management.
o   PtObject--Management of persistence and database characteristics.
o   PtAllSet<type>--Provides access to all objects of a given class.
o   PtSet<type>--Sets which can be used for one to many relationships 
    among objects or to hold the results of a query.
o   PtOndemand<type>--An optimization class for references to objects 
    which are to be explicitly loaded.
o   PtQuery<type>--A class to define and hold query specifications.


POET Data Types


o   int, long, char, float, double--All of the standard C++ data types 
    are supported.
o   class[n]--Arrays are fully supported.
o   PtString--A variable length string is provided.
o   PtDate--Date type.
o   PtTime--Time type.
o   PtBlob--BLOBs--Binary Large OBjects

