Overview

EXPRESS models have several different kinds of definitions, and are transformed into C++ classes for efficient programming.

The IFC EXPRESS definitions and matching C++ classes have the "Ifc" prefix, which avoids symbol conflicts and makes it easy to identify clases in your code. The IFC "IfcProduct" definition becomes the IfcProduct class, "IfcCartesianPoint" becomes the IfcCartesianPoint class, and so on. A list of all classes is available with links to the EXPRESS definitions. The raw EXPRESS text is also available.

EXPRESS models are made of ENTITY definitions, which describe the instances that appear in a file. They also define union, aggregate, and enumeration types to characterize the values of entity attributes. All of these appear in the C++ libraries.

ENTITY

EXPRESS models are made of ENTITY definitions, which behave like a C struct. These have attributes that hold references to other entities or primitive data, and are organized into inheritance hierarchies.

The C++ class has a get and put function for each attribute. The attribute name may be capitalized if it conflicts with a reserved word, but otherwise is not changed. The class will inherit from the C++ classes corresponding with its EXPRESS supertypes. If it has no EXPRESS supertypes, it will inherit from the RoseStructure C++ class.

-- EXPRESS
ENTITY IfcRoot
      GlobalId : IfcGloballyUniqueId;
      OwnerHistory : OPTIONAL IfcOwnerHistory;
      Name : OPTIONAL IfcLabel;
      Description : OPTIONAL IfcText;
END_ENTITY;


// C++
class IfcRoot : virtual public RoseStructure
{
public:
    const char* GlobalId();
    void GlobalId (const char* var);

    IfcOwnerHistory * OwnerHistory();
    void OwnerHistory (IfcOwnerHistory * var);

    const char* Name();
    void Name (const char* var);

    const char* Description();
    void Description (const char* var);
};

Primitive values are stored in place. Setting a string makes a copy of the value. Object values are stored as a pointer. All objects are created on the heap with pnewIn and owned by a RoseDesign.

RoseDesign * d;
IfcRoot * root;

root = pnewIn(d) IfcRoot;
root-> GlobalId ("1JV__TWfPA9POdKTfsVFa7");
root-> Name ("super noodle");
// leave other atts null

SELECT

A SELECT type behaves like a C union that knows the type of value that it holds. Selects may contain strings or numbers, but often bring together many ENTITY definitions which share no common supertype.

The C++ class has a get, put, and test function for each type. The get and put functions are prefixed with an underscore to distinguish it from the type name. The class always inherits from the RoseUnion C++ class.

-- EXPRESS
TYPE IfcAxis2Placement = SELECT
	(IfcAxis2Placement2D
	,IfcAxis2Placement3D);
END_TYPE;


// C++
class IfcAxis2Placement : public RoseUnion
{
public:
    RoseBoolean is_IfcAxis2Placement2D();
    IfcAxis2Placement2D * _IfcAxis2Placement2D();
    void _IfcAxis2Placement2D (IfcAxis2Placement2D * var);

    RoseBoolean is_IfcAxis2Placement3D();
    IfcAxis2Placement3D * _IfcAxis2Placement3D();
    void _IfcAxis2Placement3D (IfcAxis2Placement3D * var);
};

As seen in the example above, selects can get quite large, and may even be nested. The rose_get_nested_object and rose_put_nested_object functions get or put an entity in a select, taking care of any typing or nested selects, which covers most common situations.

IfcAxis2Placement * select_obj;
IfcAxis2Placement3D * ap3d;

// takes a RoseObject *, so we can put any type of object.  Will also
// create any nested selects if needed.
rose_put_nested_object (select_obj, ap3d);


// returns a RoseObject *, so still need to test if we want a specific
// type of object.  But will dig through any nested selects if present.
RoseObject * obj = rose_get_nested_object (select_obj);

if (obj->isa(ROSE_DOMAIN(IfcAxis2Placement3D))
{
     ap3d = ROSE_CAST(IfcAxis2Placement3D, obj);
     // do something
}

Lists, Sets, Arrays, Bags

Entity attributes can also contain collections of values. These collections inherit from the RoseAggregate C++ class and have a class name constructed by adding a "ListOf", "SetOf", "ArrayOf", or "BagOf" prefix to the content type.

These classes all act as expandable, strongly-typed, C arrays, with get, put, append, size, and other functions. An aggregate class is generated for each combination used in the schema, as found in the list of classes.

-- EXPRESS
ENTITY IfcRepresentation
      ContextOfItems : IfcRepresentationContext;
      RepresentationIdentifier : OPTIONAL IfcLabel;
      RepresentationType : OPTIONAL IfcLabel;
      Items : SET [1:?] OF IfcRepresentationItem;
END_ENTITY;

// C++
class SetOfIfcRepresentationItem : (ultimately RoseAggregate)
{
    IfcRepresentationItem * get (unsigned i);
    void add (IfcRepresentationItem * val);
    void put (IfcRepresentationItem * val, unsigned i);
    unsigned size();
}

Because of the way EXPRESS defines the different aggregates, null values are ignored when adding to lists, sets, and bags. Adding to sets is implemented as an add-if-absent.

IfcRepresentation * rep;
SetOfIfcRepresentationItem * items;
unsigned i,sz;

items = rep->Items();
for (i=0, sz=items->size(); i<sz; i++)
{
     IfcRepresentationItem * it = items->get(i);
     // do something
}

Enumerations

Attributes can also contain enumerations. These are generated as C++ enum types, but have an extra "NULL" value. To avoid symbol conflicts, each value is prefixed by the type name. This is necessary because C++ puts all enum values in the same namespace.

-- EXPRESS
TYPE IfcAlarmTypeEnum = ENUMERATION OF
	(BELL
	,BREAKGLASSBUTTON
	,LIGHT
	,MANUALPULLBOX
	,SIREN
	,WHISTLE
	,RAILWAYCROCODILE
	,RAILWAYDETONATOR
	,USERDEFINED
	,NOTDEFINED);
END_TYPE;

// C++
enum IfcAlarmTypeEnum {
	IfcAlarmTypeEnum_NULL = ROSE_NULL_ENUM,
	IfcAlarmTypeEnum_BELL = 0,
	IfcAlarmTypeEnum_BREAKGLASSBUTTON,
	IfcAlarmTypeEnum_LIGHT,
	IfcAlarmTypeEnum_MANUALPULLBOX,
	IfcAlarmTypeEnum_SIREN,
	IfcAlarmTypeEnum_WHISTLE,
	IfcAlarmTypeEnum_RAILWAYCROCODILE,
	IfcAlarmTypeEnum_RAILWAYDETONATOR,
	IfcAlarmTypeEnum_USERDEFINED,
	IfcAlarmTypeEnum_NOTDEFINED
};

The example below illustrates an enum in C++. In IFC4, many of the control elements have a predefined type enumerator that gives more detail about the element.

IfcAlarm * alarm;
switch (alarm->PredefinedType()) {
case IfcAlarmTypeEnum_NULL:
    printf("None\n"); break;

case IfcAlarmTypeEnum_BELL:
    printf("Bell\n"); break;

case IfcAlarmTypeEnum_LIGHT:
    printf("Light\n"); break;

default:
    printf("Something else\n"); break;
}