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; }