Overview

Late-bound features of the ROSE library, such as access and update functions, traversal, type queries, and the STEP physical file and ROSE working form file processors are based upon an EXPRESS data dictionary. This dictionary is created by the EXPRESS compiler and read by any application that needs schema definitions. See below for a description of the contents of the dictionary. For more information on how the dictionary is generated, refer back to ROSE Compiled Schemas.

In addition, applications have access to run-time C++ type information. The section below describes how the ROSE library stores this information. Run-time type information allows an application to fit EXPRESS-defined data to C++ classes and to find the best-fit when an exact match for an EXPRESS entity is not available, as well as to perform pointer casting.

EXPRESS Data Dictionary

The EXPRESS data dictionary is a compiled description of the data structures defined by an EXPRESS schema. The structure description is stored as a RoseDesign containing RoseDomain and RoseAttribute objects. Schema designs are generated by the EXPRESS compiler and are read in by any application that needs to use a schema. Compiled schema designs are named after the original EXPRESS schemas <schema-name>.rose.

Schemas

Every STEP data set has at least one schema. Most applications are early-bound and find their underlying schema from the generated classes. The generated C++ classes contain information about the EXPRESS schema they were generated from. When an application creates the first persistent instance of a class, the ROSE library will use this information to search the runtime schemas and read in the schema design file.

Late-bound applications have no classes, so they declare what schema to use when creating a new design. The RoseDesign constructor accepts the name of a schema along with the name of the design.

// early bound, the point class knows that it was defined by
// the config_control_design express schema
RoseDesign * d = new RoseDesign (part.stp);
cartesian_point * pt = pnewIn(d) cartesian_point;

// late bound, finds the point definition at runtime
RoseDesign * d = new RoseDesign (part.stp, config_control_design);
RoseObject * pt = d-> pnewInstance (cartesian_point);

This example creates a design named part.stp that is based on the AP203 config_control_design EXPRESS schema. While schemas are usually declared when a design is created, the RoseDesign::useSchema() can change the schema of a design and RoseDesign::addSchema() gives a design additional schemas. These functions accept either a schema name or a pointer to a schema design.

design-> useSchema(config_control_design);
design-> addSchema(my_extensions);

The RoseDesign::schemas() function returns the list of schemas assigned to a design. Since the STEP APs normally only use one schema, the RoseDesign::firstSchema() function simplifies access to the first element of the schema list.

RoseDesign * design;
RoseDesign * schema;
ListOfRoseDesign * schema_list
unsigned i, sz;

printf (First schema %s\n, design-> firstSchema()-> name());

schema_list = design-> schemas();               /* get all schemas */
for (i=0, sz=schema_list-> size(); i<sz; i++) {
    schema = schema_list-> get(i);
    printf (Schema %s\n, schema-> name());
}

The RoseInterface::userSchemaNames() function returns a list of schema names used by the generated classes in an application.

ROSE.userSchemaNames();  /* list of strings */

If an application cannot find the compiled schema definitions that it needs, it will not be able to create, read, or save any persistent STEP data.

Domains and Attributes

Within a schema design, EXPRESS definitions are stored as instances of the RoseDomain and RoseAttribute classes. These domain objects describe the data structures of a schema, but not any of the rules or derived attributes. Domains are present for every ENTITY, SELECT and aggregate type, as well as basic types such as REAL and INTEGER. Definitions for the basic types and built-in classes such as RoseObject are kept in a special built-in schema called the keystone.

Every persistent object has a domain, which can be found with the RoseObject::domain() function. Every domain object has a name, which is usually taken from the original EXPRESS definition. Since aggregates may not have a name, aggregate domains are given the same composite names as their C++ classes. For example, a list of strings would have the name ListOfString. The name of a domain can be retrieved with the RoseDomain::name() function.

RoseObject * obj;
RoseDomain * dom = obj-> domain();

printf (Object is a %s\n, dom-> name());

The structure described by a domain object may include local and inherited attributes. The RoseDomain::typeAttributes() function returns a list containing definitions for both locally declared and inherited attributes. RoseDomain::typeImmediateAttributes() returns only locally declared attributes. Most of the RoseDomain member functions are prefixed with the word type to avoid confusion.

Each RoseAttribute object describes a storage slot. Depending on the EXPRESS definition, the storage slot could be an entity attribute, a component of a select, or the elements of an aggregate. The RoseAttribute::name() function returns the name of the attribute. The RoseAttribute::slotDomain() function returns the domain object that describes the type of the attribute while the RoseAttribute::slotOwner() returns the domain object that defines the attribute. Most of the RoseAttribute member functions are prefixed with the word slot to avoid confusion.

RoseDomain * domain;
RoseAttribute * att;
ListOfRoseAttribute * atts;
unsigned i, sz;

atts = domain-> typeAttributes();    /* get all attributes */
for (i=0, sz=atts-> size(); i<sz; i++) {
    att = atts-> get(i);
    printf (Attribute %s\n, att-> name());
    printf ( type is %s\n, att-> slotDomain()-> name());
}

Domains for ENTITY definitions have a RoseAttribute for each attribute in the EXPRESS, as well as ones for each attribute inherited from a supertype. Optional attributes are indicated by the RoseAttribute::isOptional() function. The list returned by typeAttributes() contains each of these in the order that they appear in a Part 21 file. The name of each attribute is taken from the EXPRESS definitions. Domains for entity definitions are indicated by the RoseDomain::typeIsEntity() function.

RoseDomain * domain;
RoseAttribute * att;
ListOfRoseAttribute * atts = domain-> typeAttributes();
unsigned i, sz;

if (domain-> typeIsEntity())
    printf (%s is an ENTITY\n, domain-> name());

for (i=0, sz=atts-> size(); i<sz; i++) {
    att = atts-> get(i);
    if (att-> isOptional())
	printf (%s is optional.\n, att-> name());
}

The domains for SELECT definitions have an attribute for every type in the select. Select domains do not inherit any attributes from supertypes. The name of each attribute is taken from the type, but is prefixed with an underscore. Domains for select definitions are indicated by the RoseDomain::typeIsSelect() function.

The domains for aggregate types have a single attribute. Aggregate domains do not inherit any attributes from supertypes. The one attribute represents the type of the aggregate elements. The name of the attribute is the same as the type of the attribute. Domains for aggregate definitions are indicated by the RoseDomain::typeIsAggregate() function.

RoseDomain * domain;

if (domain-> typeIsAggregate()) {
    printf (%s is an Aggregate\n, domain-> name());
    printf (Aggregate of %s\n, domain-> typeAttributes()-> 
	    first()-> slotDomain()-> name();
}

The simple types have domains as well. Domains for simple types are indicated by the RoseDomain::typeIsSimple() function. The only simple types that have attributes are enumerations. An enumeration domain has one attribute for each enumerator. The name of each attribute is the same as the enumerator. The type of the attribute is not used. Enumeration domains inherit two attributes from the RoseEnum domain. These are the null values NULL and $.

Supertypes and Subtypes

Domains are organized into a classification hierarchy. At the root are the primitive types INTEGER, DOUBLE, BOOLEAN, LOGICAL, STRING, BINARY, RoseEnum, and RoseObject. All enumeration domains are subtypes of the RoseEnum domain. The RoseObject domain is the root of all of the object types. The RoseStructure, RoseUnion, and RoseAggregate domains inherit from RoseObject.

Object domains generally correspond to the C++ classes of the same names. Entities are derived from RoseStructure and follow the EXPRESS inheritance relations. Selects are derived from RoseUnion. Aggregates are derived from RoseAggregate and the RoseArray, RoseBag, RoseList, and RoseSet subtypes. The RoseDomain::typeSuperTypes() function returns a list containing all supertype domains in inheritance order. For the primitive domains like INTEGER and STRING this list will be empty. The RoseDomain::typeImmediateSubTypes() function returns a list containing any domains that are one level above in the type hierarchy. For example, given the EXPRESS:

ENTITY geometric_item; END_ENTITY;
ENTITY polygon SUBTYPE OF (geometric_item); END_ENTITY;
ENTITY triangle SUBTYPE OF (polygon);; END_ENTITY;

A call to typeImmediateSuperTypes() on the triangle domain would return a list containing the polygon domain, while a call to typeSuperTypes() would return in the following order, the RoseObject, RoseStructure, geometric_item, and polygon domains.

Similarly, the RoseDomain::typeSubTypes() function returns a list of all domains derived from a domain. The domains are not returned in any particular order. The RoseDomain::typeImmediateSubTypes() function returns a list containing all domains that are one level below in the type hierarchy.

Considering our previous example, a call to typeImmediateSubTypes() on the domain object for geometric_item would return a list containing the polygon domain, while a call to typeSuperTypes() would return in the following order, the polygon and triangle domains.

The RoseDomain::typeIsa() function searches through a domain's supertypes to see if the specified domain appears anywhere. The typeIsa() function accepts either a domain object or the name of a domain. The RoseObject::isa() function is a shortcut to calling typeIsa() on the domain of an object.

RoseObject * obj;
if (obj-> domain()-> typeIsa (polygon)) { /* is object a polygon? */ }
if (obj-> isa (polygon))                { /* is object a polygon? */ }

Note that the tests above would return true even if the object were a subtype of polygon, such as a triangle.

Other Domain Properties

In addition to attributes, supertypes and subtypes, domains have other properties. For example, the RoseDomain::typeIsAbstract() function indicates whether the original EXPRESS definition was a ABSTRACT entity.

The RoseDomain::typeIsComplex() function indicates whether a domain represents an EXPRESS AND/OR combination. The domain for a complex type inherits from each of the AND/OR types and has no new attributes. This is described in greater detail in Working Sets and Best-Fit Classes. The name of a complex domain is the names of the AND/OR type concatenated with _and_.

The RoseDomain::typeIsBestFit() function indicates whether the domain has an exact C++ class match or has been best fit onto the nearest possible class.

The RoseDomain::short_name() function returns the abbreviated EXPRESS type name if one is present. These are usually specified as an annex to a STEP application protocol document. For more information, consult Short Names.

Searching for Domains

Applications can find the domain of any object using the RoseObject::domain() function. An application can also use the ROSE_DOMAIN() macro to find the domain associated with a particular C++ class or primitive. The class must be linked into the application. The macro pastes together tokens, so the argument to the macro should not contain any spaces.

RoseObject * obj;
RoseDomain * dom = obj-> domain();

printf (Object is a %s\n, dom-> name());

dom = ROSE_DOMAIN (RoseStructure);  /* for C++ class */
dom = ROSE_DOMAIN (ListOfString);   /* for C++ class */
dom = ROSE_DOMAIN (int);            /* for C++ integer type */
dom = ROSE_DOMAIN (double);         /* for C++ double type */
dom = ROSE_DOMAIN (STR);            /* for C++ string type */
dom = ROSE_DOMAIN (RoseLogical);    /* for C++ logical type */
dom = ROSE_DOMAIN (RoseBoolean);    /* for C++ boolean type */

A late-bound application may need to find the domain object for a particular EXPRESS definition. Use the RoseDesign::findDomain() and RoseInterface::findDomain() functions to search through the list of schemas associated with a design and find a domain by name. Each schema design uses the name index to find domains quickly. The search is case insensitive. Once found, the domain can be used for operations such as creating an object instance with the pnewInstance() function.

RoseDomain * dom;
RoseDesign * design;
RoseObject * obj;

dom = ROSE.findDomain (point);             /* uses current design */
dom = ROSE.findDomain (point, design);
dom = design-> findDomain (point)

obj = design-> pnewInstance (dom);           /* create a new point */

While the findDomain() functions search the schemas associated with a particular design, the RoseInterface::findDomainInWorkspace() function searches all schemas in memory.

RoseDomain * dom;
RoseDesign * design;
RoseObject * obj;

dom = ROSE.findDomainInWorkspace (point); 
obj = design-> pnewInstance (dom);           /* create a new point */

The RoseDesign::findComplexDomain() function takes a list of domain objects, and tries to find a complex domain that is formed from these domains. The following example shows an attempt to find a domain for an AND/OR combination of brep_with_voids and facetted_brep.

RoseDomain * dom;
RoseDesign * design;
ListOfDesign supers;

supers.add (design-> findDomain (brep_with_voids)); 
supers.add (design-> findDomain (facetted_brep)); 
dom = design-> findComplexDomain (&supers)

Searching for Attributes

Late-bound application will need to search for attribute objects. The late-bound access and update functions (putObject(), getObject(), etc.) are more efficient when given an attribute object instead of an attribute name. When an access function is given an attribute name, it will find the corresponding attribute object by using the RoseDomain::findTypeAttribute() function. This searches through all of the local and inherited attributes for one with the given name. The following two code fragments are equivalent:

obj-> putDouble (x_coordinate, 5.0);

RoseAttribute * att;
att = obj-> domain()-> findTypeAttribute (x_coordinate);
obj-> putDouble (att, 5.0);

The RoseDomain::findTypeImmediateAttribute() is a variant of this function that only searches the attributes local to a domain.

When domains are written to disk, only the locally defined attributes returned by typeImmediateAttributes() are saved. The list of attributes returned by the typeAttributes() function is computed at run time. When this list is built, the ROSE library makes copies of the attribute objects for each domain and annotates them with C++ type information such as storage size and offset within a C++ class. These non-persistent copies of the attribute objects are what the late-bound access and update functions use. If you should pass in one of the original objects, the library will automatically find the non-persistent copy.

When doing late-bound programming for selects, it is sometimes necessary to search for an attribute by type rather than by name. The RoseDomain::findAttributeForType() function searches through the attributes for one matching a particular type. In other words, given a type, the function will find an attribute that could hold data of that type. The function accepts either a domain name or a domain object

RoseAttribute * att;
RoseObject * polygon_obj

att = obj-> domain()-> findTypeAttributeForType (polygon);
obj-> putObject (att, polygon_obj);

C++ Type Information

The ROSE library must maintain information about the C++ classes compiled into an application. This information is used to match EXPRESS entity types to a C++ class and to cast pointers between different objects. The RoseType class is used to manage this information. There is one RoseType instance for each C++ type that has been compiled into an application. This includes all of the class types as well as enumerations and primitives such as double, RoseBoolean, and RoseLogical.

To find out the C++ class of an object, use the RoseObject::classType() function, or the RoseObject::className() function. The classType() function returns the RoseType object while the className() function returns the name of the class.

RoseObject * obj;
printf (Instance of C++ class %s\n, obj-> className()); 
printf (Instance of C++ class %s\n, obj-> classType()-> name()); 

Some advanced functions use RoseTypes as arguments. The ROSE_TYPE() macro returns the RoseType for a particular C++ class or primitive. The class must be linked in with your application. The macro pastes together tokens, so the argument should not contain any spaces. The macro actually returns a RoseTypePtr, which is a smart pointer to a RoseType object.

In addition to domain objects and the names of domains, the RoseObject::isa() function accepts RoseType information. This can be quite useful when trying to determine whether an object is of a certain class.

RoseTypePtr& thePointRoseType = ROSE_TYPE(polygon);
RoseTypePtr& theFloatRoseType = ROSE_TYPE(float);
if (obj-> isa (ROSE_TYPE(polygon))   {/* instance of C++ polygon class */}

Note that in the last example, the object might actually be an instance of a more specific EXPRESS entity type, such as triangle, that has been best-fit to the C++ polygon class. Refer to Best-Fit Classes for more information.