Book Contents | Book Index | Master Index | Previous Chapter | Next Chapter
Search STEP Tools Web Support

4.1 Overview

ROSE applications create and manipulate EXPRESS-defined data as C++ objects. Section 4.2 describes the C++ classes for different EXPRESS types. This section covers ENTITY , SELECT , and aggregates values. Section 4.3 and Section 4.4 show how to create and delete objects. Section 4.6 describes how to access and update object attributes using early-bound functions generated by the EXPRESS compiler, as well as late-bound functions that use the EXPRESS data dictionary. Section 4.8 describes helper objects, called managers, that hold auxiliary information.


4.2 EXPRESS-defined C++ Objects

The key feature of the ROSE library is the ability to read and write C++ objects to STEP Part 21 files. Only the RoseObject class and its RoseStructure , RoseUnion , and RoseAggregate subclasses can hold EXPRESS-defined data. Together, these classes can handle any EXPRESS-defined data.

STEP data objects are considered "persistent" if they are owned by a RoseDesign , because they will be written to secondary storage when the design is saved. Objects that are not owned by a RoseDesign are sometimes referred to as "non-persistent". An object may belong to only one RoseDesign at any given time. The RoseObject::design() function returns the design that owns an object. If this is non-null, the object is persistent. You can also use the RoseObject::isPersistent() function to tests if an object is persistent.

    RoseObject * obj;
     
    if (obj-> isPersistent())    { /* obj is persistent */ }
    if (obj-> design() != 0)     { /* obj is persistent */ }

Each STEP data object is associated with an EXPRESS data-dictionary definition, called its "domain." This is discussed in detail in EXPRESS Data Dictionary. The ROSE_DOMAIN() macro returns the domain for a generated C++ class.

Each STEP data object is also associated with C++ runtime type information, called its "rosetype." This is discussed in detail in C++ Type Information. The ROSE_TYPE() macro returns the C++ runtime type information for a generated C++ class.


Entity and Select Classes

The top few levels of the ROSE class hierarchy are shown in Figure 4.1. The EXPRESS compiler generated subclasses of these..

The EXPRESS compiler generates a subclass of RoseStructure for each ENTITY type as described in Entities. The RoseStructure class stores an entity instance with associated attribute values. C++ inheritance follows the EXPRESS as closely as possible. An EXPRESS entity with no supertypes is an immediate subclass of RoseStructure , but a subtype in the EXPRESS is generated as a subclass of the corresponding type in the C++.

The EXPRESS compiler generates subclasses of RoseUnion for each SELECT type type as described in Selects. The RoseUnion class stores one attribute value in a strongly-typed union. All select classes are generated as immediate subclasses of RoseUnion .


Aggregate Classes

Instances of the RoseAggregate class store aggregates of values. The EXPRESS compiler will create a subclass of RoseAggregate for each LIST , SET , BAG , and ARRAY in an information model. The classes have functions for adding and removing elements, as well as other operations. Each aggregate type has different behavior:

The classes are named by data structure (List, Bag, Set, or Array) followed by the element. For example, a list of integer elements would be ListOfInteger . There are two interchangeable notations for aggregates:

RoseAggregate has four subclasses: RoseList , RoseBag , RoseSet , and RoseArray . The RoseAggregate hierarchy is shown in Figure 4.2. All object aggregates are siblings in the class hierarchy. While Point is a subclass of RoseObject , ListOfPoint is not a subclass of ListOfRoseObject .

Aggregates are implemented as parameterized classes using macros, so they do not require any special extensions to the C++ compiler. These classes are discussed in greater detail in Section 4.7.


Primitive Values vs. Object Values

Entities hold attribute values, aggregates hold collections of values, and selects hold one value at a time. Whether an object stores a copy of, or a reference to a value depends upon the type of the value.

Primitive values (EXPRESS integer, real, boolean, logical, string, binary or enumeration) are stored "in place" as copies of the value. For example, when you assign a string to an entity attribute or add a new value to a list of strings, the entity or list will store a copy of your input string. If the entity or list is deleted, the copy of the string will also be deleted.

    ListOfString * list;
    Person * obj;
     
    obj -> name("Sam");   /* stores copy of string */
    list-> add ("Bob");   /* stores copy of string */

Object values (EXPRESS entity, select, or aggregate types) are stored as a pointer to an instance. For example, if we delete a ListOfPolygon object, only the list and pointers to the Polygons are destroyed. The Polygon objects are not affected. In order to delete both, we must iteratively delete every Polygon in the list before deleting the list. Section 4.4 discusses how to delete objects.

Note that nested aggregates, such as a ListOfListOfString , are represented as an aggregate object that holds pointers to other aggregate objects.

    ListOfString * list;
    ListOfListOfString * nested;
    Circle * poly;
    Point * pnt;
     
    poly-> center (pnt);   /* stores reference to point object */
    list-> add ("Bob");    /* stores copy of string */
    nested-> add (list);   /* stores reference to list object */


4.3 Creating Objects

The ROSE library has several ways to create objects. Early-bound applications can use C++ new to create instances. The C++ new operator allocates storage and, when used a RoseObject subclass, creates a instance not owned by any RoseDesign .

The pnew and pnewIn() operators also allocate storage, but they create objects that are owned by a RoseDesign (also called "persistent"). The pnew operator uses the current design while pnewIn() takes a design pointer as an argument. The pnew and pnewIn() operators can only be used with classes derived from RoseObject .

    Point * p1 = new Point;     /* non-persistent point */
    Point * p2 = pnew Point;    /* persistent point in current design */
     
    RoseDesign * d = ROSE.newDesign ("picture");
    Point * p3 = pnewIn (d) Point;   /* persistent point in picture */

Late-bound applications use the RoseDesign::pnewInstance() function to create persistent objects. This function uses the EXPRESS data dictionary such as a type name or a RoseDomain object which describes a type.

    RoseObject * obj;
    RoseDomain * pnt_domain = ROSE.findDomain ("point");
    RoseDesign * design = pnew RoseDesign ("picture");
     
    obj = ROSE.pnewInstance ("point"); /* new point in current design */
    obj = ROSE.pnewInstance (pnt_domain); /* same */
     
    obj = ROSE.pnewInstance ("point", "picture"); /* new point in picture */
    obj = ROSE.pnewInstance ("point",  design);    /* new point in picture */
     
    obj = design-> pnewInstance ("point");     /* new point in picture */
    obj = design-> pnewInstance (pnt_domain);  /* same */


4.4 Deleting Objects

When an object is no longer needed, it can be removed in two different ways. A persistent object can be moved to a special "trashcan" design, then deleted later when the trash is "emptied". Alternatively, any object can be deleted immediately using the C++ delete operator.

The advantage of the trashcan approach is that it can eliminate "dangling references" to deleted objects. When the trashcan is emptied, all of the persistent objects in memory are examined and references to any non-persistent or trashed objects are set to null. When an object is deleted using the C++ delete operator, no reference checking is done; other objects may hold pointers to freed memory. Such dangling references could cause crashes or other such problems elsewhere in the application, particularly when writing designs to secondary storage.

Objects can be moved to the trash with the rose_move_to_trash() function. If an application wishes to do something fancy, it can access the trash design directly using the rose_trash() function.

When it is time to reclaim memory, call rose_empty_trash(). This traverses every object in memory, nulling pointers to nonpersistent or trashed objects. Then it deletes all of the trashed objects.

    /* Trashcan delete functionality */
    extern void rose_move_to_trash (RoseObject * obj);
    extern void rose_empty_trash();
    extern RoseDesign * rose_trash();       /* trash design */
     
    /* example */
    Point * p_pnt = pnew Point;
     
    rose_move_to_trash (p_pnt);    /* moves from design, to trash */
    rose_empty_trash();            /* clears bad pointers, deletes obj */

The C++ delete operator works on both persistent and non-persistent objects. When a persistent object is deleted, it is removed from its owner RoseDesign . The ROSE library does not check for dangling references from other objects, so the application should remove any references to the purged object before deleting it.

    Point * np_pnt = new Point;
    Point * p_pnt = pnew Point;
     
    delete np_pnt;      /* normal C++ deletion */
    delete p_pnt;       /* removes from design, then normal C++ deletion */

The delete operator has no effect when passed a null pointer.


4.5 Moving and Copying Objects

Persistent objects belong to only one design at a time, although they may be moved between designs with rose_move_to_design() or RoseObject::move(). The rose_move_to_design() function moves a single object, while RoseObject::move() is a layer on top that can "deep move" a tree of objects. There is also a rose_move_to_section() function for moving between RoseDesignSections . This is discussed in Design Sections.

These functions can make persistent objects non-persistent and vice versa. When a non-persistent object is moved to a design, it will gain an EXPRESS data dictionary definition and possibly a RoseOID . If an object is made non-persistent by moving it to null, it will lose its OID and EXPRESS definition. If the object was read into memory as a best-fit class, any extra attributes that do not match C++ attributes will be lost. While an object is persistent, these attributes are available through the late-bound access and update functions.

If a persistent object is simply moved from one design to another, it does not lose its OID, EXPRESS definition, or extra attribute information.

    RoseDesign * d1 = new RoseDesign ("picture");
    RoseDesign * d2 = new RoseDesign ("gadget");
    Point * np_pnt = new Point;
    Point * p_pnt = pnew Point;
     
    np_pnt-> move (d1);        /* make persistent, owned by d1 */
    p_pnt-> move (NULL);       /* make non-persistent */
     
    rose_move_to_design (np_pnt, d1);    /* same operations using */
    rose_move_to_design (p_pnt, NULL);   /* rose_move_to_design */
     
    p_pnt = pnewIn (d1) Point;    /* new object, owned by d1 */
    p_pnt-> move (d2);            /* now owned by d2 */

The RoseObject::copy() function duplicates an object in the same design or in a different design. By default, attribute values of the new object are copies of the originals. This means, for example, that a copy of a circle object would refer to the same center point object as the original. It is possible to create a "deep copy" of the circle; i.e., a copy of the circle, and a copy of the objects the circle refers to.

    Circle * c1 = pnew Circle;
    Point * p1 =  pnew Point;
     
    c1-> radius (5.9);
    c1-> center (p1);
     
    /* shallow copy: c2 radius is 5.9, and center is p1 */
    RoseDesign * new_design = pnew RoseDesign ("gadget");
    Circle * c2 = ROSE_CAST (Circle,c1->copy(new_design));

For a "deep copy," specify a depth argument to the copy operation. This signifies how many levels of references to follow. A value of zero indicates no references; i.e "shallow copy." A value of one follows one level; i.e. the center point. A value of two would copy any objects that the center point referenced.

    Circle * c2 = ROSE_CAST (Circle,c1->copy(new_design, 1));
    /* deep copy: c2 radius is 5.9, and center is a copy of p1 */


4.6 Access and Update Functions

Attribute values within a persistent object can be examined and manipulated in two ways. Any application can use the late-bound data dictionary-based access and update functions defined by RoseObject . In addition, early-bound applications can use strongly-typed access and update functions defined by generated C++ classes.


Late-bound Functions for All Objects

The RoseObject class defines get and put functions for each value type. The parameters differ slightly depending upon whether the object being interrogated is an entity, select, or aggregate. The basic form of the put function is:

    put<type> (value, attribute);     /* entity */
    put<type> (value, att_for_type);  /* select */
    put<type> (value, index);         /* aggregate */

The basic form of the get function is

    value = get<type> (attribute);     /* entity */
    value = get<type> (att_for_type);  /* select */
    value = get<type> (index);         /* aggregate */

The get and put functions for entities accept the name of an attribute as well as the RoseAttribute data dictionary object that describes the attribute. The functions for selects are similar, but each attribute represents a type allowed by the select. To avoid name conflicts, the attributes of a select are have the same name as their associated type, but are prefixed by an underscore. For example, to set a select object to hold a floating point value 5.0 of type "length_measure," one would use:

    obj-> putDouble (5.0, "_length_measure"); 

The get and put functions for aggregates accept unsigned integer index. If an application tries to put a value beyond the end of an aggregate, the aggregate will be resized and the value will be appended. Regardless of how much larger the index is, the aggregate will only be expanded by one element. So given a list with six elements, the following calls all have the same effect:

    /* given list with 6 elements */
    list-> putString ("foo", 7);     /* expands list by one element */
    list-> putString ("foo", 7000);  /* expands list by one element */

The put and get functions for the different types are as shown below.

    putObject (RoseObject* ...);    RoseObject* getObject (...);
    putInteger (int ...);           int getInteger (...);
    putFloat (float ...);           float getFloat (...);
    putDouble (double ...);         double getDouble (...);
    putString (const char * ...);   char * getString (...);
    putBinary (const char * ...);   char * getBinary (...);
    putBoolean (RoseBoolean ...);   RoseBoolean getBoolean (...);
    putLogical (RoseLogical ...);   RoseLogical getLogical (...);

The putObject() and getObject() functions expect or return a RoseObject pointer. Since RoseObject is the superclass of all STEP data objects, these two functions can be used to assign and retrieve entity objects, aggregate objects, and select objects.

The list does not have functions for enumeration types. An application should use the putString() and getString() functions to set enumeration values. The integer access and update functions will also work, but should be avoided for clarity reasons. The application should assign the string name of the enumerator. For example, if we have an enumerator that defined elements for "made" and "bought":

    TYPE make_or_buy = ENUMERATION OF (made, bought);
    END_TYPE;
    ENTITY part;
        source: make_or_buy;
        . . . 
    END_ENTITY;

We might see the following code in early and late-bound applications. Note that make_or_buy_made is an element of the C++ enum make_or_buy that would be generated by the EXPRESS compiler:

    partobj-> putString ("made", "source");    /* late-bound */
    partobj-> source (make_or_buy_made);       /* early bound */


Early-bound Functions for Entities

As described in Entities, the C++ classes generated by the EXPRESS compiler have access and update functions for each attribute. For example, given the entity definition:

    ENTITY Point;
        x : REAL;
        y : REAL;
    END_ENTITY;

The EXPRESS compiler will generate the following functions:

    /* x Access Functions */
    double Point::x();
    void Point::x (double ax);
     
    /* y Access Functions */
    double Point::y();
    void Point::y (double ay);

Access and update functions for attributes that are entities, aggregates, or selects take or return a pointer to an instance of that specific type. For example, given the definition for a circle:

    ENTITY Circle;
        center : Point;
        radius : REAL;
    END_ENTITY;

The compiler will generate the following access and update functions for the "center" attribute:

    /* center Access Functions */
    Point * Circle::center();
    void Circle::center (Point * ay);

As discussed in Section 4.2.3, the value that is passed in and out is a pointer to an instance rather than the instance itself. One implication of this is that an object can be simultaneously referenced by several others.


Early-bound Functions for Selects

As described in Selects select classes contain an access, update, and query function for each type in the select.

    <type> _<type>();                 /* access value */
    void   _<type>(<typname> value);   /* update value */
    RoseBoolean is_<type>();           /* query value */

Consider the select of the following three entity types:

    TYPE shape = SELECT (Point, Circle, Text);
    END_TYPE;

EXPRESS compiler will generate the following access functions.

    /* Access and Update Functions */
    RoseBoolean is_Point();
    Point *  _Point();
    void _Point (Point *  a_Point);
     
    RoseBoolean is_Circle();
    Circle *  _Circle();
    void _Circle (Circle *  a_Circle);
     
    RoseBoolean is_Text();
    Text *  _Text();
    void _Text (Text *  a_Text);


Late-bound functions for Selects

The ROSE library includes several late-bound functions for manipulating nested select types. These function provide a convient interface when dealing with the nested Select types which are defined by STEP Application Protocols.

The rose_get_nested_object() function retreives the entity value of a possibly nested RoseUnion object. This function searches the (possibly) nested RoseUnion objects until it finds the underlying entity instance. See rose_get_nested_object() for more information.

The rose_put_nested_object() function sets that value of a RoseUnion to a given value. This function will create nested RoseUnion objects as required by the schema to represent the value. See rose_put_nested_object() for more information.

The following code demonstrates how to use these functions.

    RoseObject * pd;
    RoseUnion * char_def = pd->getObject("definition");
     
    /* Get the old value */
    RoseObject * old_val = rose_get_nested_object(char_def);
     
    /* Update to a new value */
    RoseObject * shape_aspect = new_val;
    rose_put_nested_object(char_def, shape_aspect);

The put function is useful for changing an existing select instance. If the select instance does not yet exist, one can create the top level instance and then call the put function, but the rose_create_select() function creates a nested select in one step, given the type and value to be placed into the select. This can be used to populate an attribute as in the following example:

    void put_definition(RoseObject * prop_def, RoseObject * value)
    {
        RoseDomain * att_dom = prop_def->getAttribute("definition")
            ->slotDomain();
        RoseUnion * sel = rose_create_select(att_dom, value);
        prop_def->putObject(sel, "definition");
    }

See rose_create_select() for more information.


Early-bound Functions for Aggregates

The parameterized aggregate classes define numerous functions for accessing and updating the values of an aggregate. This section describes some of them, but you should refer to RoseAggregate for more detailed information about each of these. All of the aggregates have the same basic access functions:

    first();                /* return first element */
    last();                 /* return last element */
    operator[] (index);     /* return element at index */
    get (index);            /* same */

The update functions differ from aggregate to aggregate. All of the aggregates support a put() function, but some support additional functions:

    put (value, index);      /* all */
    add (value);             /* only lists, sets, and bags */
    addIfAbsent (value);     /* only lists, sets, and bags */
    insert(value, index);    /* only lists */
     
    removeAt (index);        /* all */
    removeValue (value);     /* all */
    emptyYourself();         /* all */

In addition, there are functions for searching and controlling the aggregates:

    size();
    size (newsize);          /* set size - only arrays */
    capacity();
    capacity (newcap);
     
    unsigned find (value);
    RoseBoolean contains (value);

These functions are defined by the ROSE_DECLARE_AGG() macros and are not individually generated by the EXPRESS compiler. See Section 4.7 for more details on parameterized aggregate classes.


NULL Attribute Values

The ROSE library defines null values for all primitive types. These values can be set, retrieved, and tested like any other value. When an attribute contains a null value, it will be written to a Part 21 file as the missing value symbol "$".

The null values are defined using the following symbols:

    ROSE_NULL_INT        /* null int value */
    ROSE_NULL_REAL       /* null double value */
    ROSE_NULL_ENUM       /* null enumeration value */
    ROSE_NULL_BOOLEAN    /* null boolean/logical value */
    ROSE_NULL_LOGICAL    /* null boolean/logical value */
    ROSE_NULL_STRING     /* null string/binary value */

The following example show how one might use the null values. These examples use the Point class defined in Section 4.6.2.

    Point * pt;
     
    pt-> x (ROSE_NULL_REAL);         // unset the x and y atts
    pt-> y (ROSE_NULL_REAL);
     
    if (pt->x() == ROSE_NULL_REAL)   // test for unset atts
        printf ("x value not set\n");

The boolean and logical null values are identical and may be used interchangeably. The ROSE_NULL_STRING symbol is provided for completeness, but the string null value is just a null pointer. Applications can detect an unset string attribute by testing for a null pointer. Given an object with a string name attribute, the following two code fragments are equivalent:

    SomeObject * obj;
     
    if (obj-> name() == ROSE_NULL_STRING)
        printf ("name is unset\n");
     
    if (!obj-> name())   /* easier to write and also works */
        printf ("name is unset\n");

Applications may want to be aware of the null values used for REAL and INTEGER types. Following the SDAI C++ conventions, null for REAL attributes is the POSIX constant DBL_MIN . The INTEGER attributes uses INT_MIN as null. These values are unlikely to be used by normal applications, but the potential for conflict exists.

For late-bound applications, the RoseObject::isUnset() function can determinate if an attribute value is unset. These methods are equivalent to calling the appropriate get method, and comparing to the appropriate NULL value. It is, however, simpler since you do not need to be concerned about the type of the attribute.

    RoseBoolean isUnset(RoseAttribute * att, unsigned index=0);
    RoseBoolean isUnset(const char * att_name, unsigned index=0);

To set an attribute to the NULL value, you can use the RoseObject::unset() methods. These are equivalent to calling the appropriate put method with the appropriate NULL value.

    void unset(RoseAttribute * att, unsigned index=0);
    void unset(const char * att_name, unsigned index=0);


4.7 Parameterized Aggregate Classes

Aggregates are implemented as parameterized classes using macros. The EXPRESS compiler generates a ROSE_DECLARE_AGG() macro in a header file, and a ROSE_IMPLEMENT_AGG() macro in a ".cxx" file for each aggregate in a schema.

    ROSE_DECLARE_AGG(agg_type,element_type);
    e.g. ROSE_DECLARE_AGG(List,Point);

The "agg_type" must be List , Bag , Set , or Array . The "element_type" must be a subclass of RoseObject . The implement statement provides the definition for the class functions:

    ROSE_IMPLEMENT_AGG(agg_type,element_type);
    e.g. ROSE_IMPLEMENT_AGG(List,Point);

The C++ compiler is sensitive to spaces between parameters. It is best not to place any spaces in the arguments to the macros. Aggregates for all of the primitive types are defined by the ROSE library. Consult Predefined Aggregate Classes for a full list of the classes.


Customizing RoseAggregates

It is possible to add new functions and instance variables to the aggregate classes. You can do this with a begin/end variant of the ROSE_DECLARE_AGG() macro. The statement would be as follows:

    ROSE_DECLARE_AGG_BEGIN(List,Point);
        ...
    {additional functions}
        ...
    ROSE_DECLARE_AGG_END;

This technique makes it possible to add functions and data without subclassing the RoseAggregate classes. The implementation of the additional functions would be included in the *.cxx file for that class. The header file would contain:

    ROSE_DECLARE_AGG_BEGIN (List,Point);
        int my_nonpersistent_data;
        void anExtraFunction();
        void anotherExtraFunction();
    ROSE_DECLARE_AGG_END;

The *.cxx file for that class would contain:

    ROSE_IMPLEMENT_AGG (List,Point);
    void List(Point)::anExtraFunction(){
        printf ("Hi There\n");
    }
     
    void List(Point)::anotherExtraFunction(){
        printf ("I contain %d\n", my_nonpersistent_data);
    }


Aggregates of Non-EXPRESS Objects

It is also possible to create aggregates for non-EXPRESS C++ classes. This type of aggregate can store references to C++ objects which are not derived from RoseObject but they can not be persistent.

For example, suppose you are writing an X11 application, and would like to create a non-persistent aggregate class to hold Widget objects. You could declare and implement this ListOfWidget class as follows:

    ROSE_DECLARE_AGG(npList,Widget);
    ROSE_IMPLEMENT_AGG(npList,Widget);

Since this is a non-persistent class, the "np" prefix is prepended to the data type.


4.8 Manager Objects

Managers are objects that store extra information with a RoseObject . Your applications can define subclasses of RoseManager to store temporary information. The information in managers will not be written to a Part 21 file, but can be useful for storing translation results, reverse-pointers, and other cached values.

A managers is more useful than extending the C++ class (see Section 2.6) because managers can be associated with any object and do not require recompilation of an entire class library. Plus, managers can be used by late-bound code without generated C++ classes.


Defining a Manager Class

Declare a subtype of RoseManager with any member variables or functions that you need and use ROSE_DECLARE/IMPLEMENT_MANAGER_COMMON() macros to give the class a unique RoseManagerType identifier. Below is a manager class that holds a single count variable.

    /* in your header file */
    class CountManager : public RoseManager
    {
    public:
        unsigned long m_count;
     
        CountManager() { m_count=0; }
        ROSE_DECLARE_MANAGER_COMMON();
    };
     
    /* In your cxx file */
    ROSE_IMPLEMENT_MANAGER_COMMON (CountManager)


Finding and Using Managers

To add a manager to an object, create a instance of the manager on the heap (with new ) then RoseObject::add_manager() to associate it with an object.

The macros in the class above define a CountManager::type() static function that returns a unique class identifer. To see if an object is associated with a manager of a given type, call RoseObject::find_manager() pass in this type() value. Similarly, call RoseObject::remove_manager() to find and delete a manager of a particular type.

Usually code will check to see if a manager is present and then add one if it is not as shown in the code below:

    cartesian_point * pt;
    CountManager * mgr;
     
    // find a previously added manager on the point.  If none
    // is present, then add a new one */
    mgr = (CountManager*) pt-> find_manager (CountManager::type());
    if (!mgr) {
        mgr = new CountMagager;
        pt-> add_manager (mgr);
    }
     
    // use or change a count value on the manager
    mgr-> m_count++;

When a RoseObject is destroyed, all of the managers registered to the object are also destroyed. The manager destructors will be called during the RoseObject destructor. We can remove a type of manager explicitly as shown below:

    /* remove any CountManager on the object */
    pt-> remove_manager (CountManager::type());

An object can have many different managers, but it can not have more than one of a given type. The RoseObject::managers() function returns the linked-list of managers registered to an object.

    RoseObject * obj;
    RoseManager * mgr = obj-> managers()
    unsigned i = 0;
     
    while (mgr) {
        i++;  mgr = mgr-> next();
    }
    printf ("Object has %d managers\n", i);

The ROSE library adds managers to hold database information such as overflow attributes and external references. Both persistent and non-persistent objects can have managers.

The ROSE_MGR_UNKNOWN value signifies an unknown manager type. Several other RoseManagerType values are reserved for system-defined manager classes.

    /* The RoseManagerType is for identifying manager classes. */ 
    typedef unsigned RoseManagerType; 
    #define ROSE_MGR_UNKNOWN (RoseManagerType) 0; 

 

| Book Contents | Book Index | Master Index | ST-Developer Home | Previous Chapter | Next Chapter |