Overview
Most applications use the STEP libraries or IFC libraries. These contain C++ classes generated from the EXPRESS schemas, plus meshing and higher-level functions for working with assemblies, geometry, units, and measures. Refer to the project setup instructions for more details.
For other EXPRESS schemas, you may need to generate C++ classes and an EXPRESS data dictionary using the EXPRESS compiler as described below.
Generating C++ Classes
ROSE applications can use C++ classes created from EXPRESS for strong type-checking and simple access/update functions. The STEP Merged AP and IFC BIM libraries contain ready-to-use classes for the most widely used schemas. You can use the ST-Developer EXPRESS compiler to generate C++ classes from your own schemas, as described below.
Call the EXPRESS compiler
with the -classes
option to create a subdirectory
called classes
with generated C++ code. The class for
each ENTITY, SELECT, and ENUMERATION goes into a
separate .h
and .cxx
file. The structure
and member functions of each class are described in
to EXPRESS-defined C++
Objects
% expfront -classes custom_schema.exp
The classes for AGGREGATES are defined in the same file as their
base type. For example, the EXPRESS LIST OF LIST OF LIST OF
XYZ
will produce the following classes along
with XYZ
in XYZ.h/cxx
:
ListOfListOfListOfXYZ ListOfListOfXYZ ListOfXYZ
The compiler also generates the <schema>.h header file that includes the definitions of all classes. A working set file can be used to fine tune the classes that are generated.
Once you have the classes, you can import them into a Visual Studio project or other build environment. The mkmakefile tool can also generate a makefile that compiles and assembles a library from all class files in a directory.
Class Names
The C++ language is is case sensitive but the EXPRESS language is not. For example, "SAMPLE" and "sample" are the same in EXPRESS but different in C++. By default, the generated C++ classes use the name and case that appears in the EXPRESS. Two other conventions exist for SDAI C++ compatibility, but are not widely used.
ROSE Convention
This is the default naming convention, and used by all libraries
included with ST-Developer. The STEP libraries are also given
the stp_
prefix as described in Adding a
Prefix to Class Names so definitions like product
and
cartesian_point
become classes named stp_product
and stp_cartesian_point.
Classes and attributes have the same name and case as the EXPRESS. If a symbol conflicts with a C++ reserved word or known function, the compiler will issue a warning and eliminate the conflict by capitalizing the first letter of the word.
-- Sample EXPRESS Definitions SCHEMA example_schema; ENTITY representation_item name : STRING; END_ENTITY; TYPE colorType = ENUMERATION OF (red, green, blue); END_TYPE; END_SCHEMA; // // Resulting C++ definitions // class representation_item { char * name(); void name (char * str); }; enum colorType { colorType_NULL = NULL_ENUM, colorType_red = 0, colorType_green, colorType_blue };
Full SDAI C++ Convention
Call the compiler with the -namestyle fullsdai
flag.
The SDAI C++ states that classes are to be put in a namespace or given
a name consisting of the schema name prepended to the entity name.
The concatenated name is used for portability. All names are
lowercase, with the first letter capitalized. For enumerations, the
SDAI styles separate the type name and value by two underscores, with
a NULL value <typename>_unset
.
class Example_schema_Representation_item { char * Name(); void Name (char * str); }; enum Example_schema_Colortype { Example_schema_Colortype_unset = NULL_ENUM, Example_schema_Colortype__red = 0, Example_schema_Colortype__green, Example_schema_Colortype__blue };
Terse SDAI C++ Convention
Call the compiler with the -namestyle sdai
flag. The
terse version omits the initial schema name, but is otherwise the same
as the Full SDAI C++ style. Since most applications only use one
schema, terse names are a more practical alternative. The entity
definition above becomes:
class Representation_item { char * Name(); void Name (char * str); }; enum Colortype { Colortype_unset = NULL_ENUM, Colortype__red = 0, Colortype__green, Colortype__blue };
Class Customizing
The ROSE library can attach extra data to any object using a general mechanism called managers. Managers are the preferred way to keep extra data since they can be used on any class.
If you generate your own C++ classes, the
extclass tool can
insert #include
statements at key places in the generated
classes so that you can add member data and functions using separate
files that will not be lost if the classes are regenerated. There is
also an extall tool
that scans a directory for extension files and calls extclass
as needed.
The declarations for your new functions and extra instance data go
into the .hi
and .hx
files. The body of the
functions go into the .cx
file as described below:
- <class>.hi
- Class extensions, new include statements and ordinary function declarations. The contents of this file are placed before and outside of the scope of the class definition.
- <class>.hx
- Class extensions, new member declarations. The contents of this file are placed inside the scope of the class definition after all other members have been declared.
- <class>.cx
- Class extensions, new member functions bodies and constructor extensions.
For example, extending a point class with some extra graphics data
might look something like the following. The Point.hi
file brings in the include files that we need for our graphics
code.
/* File: Point.hi * New include-files and plain (non-member) * function declarations for the Point class. */ #include <graphics.h> /* graphics package decls */
The Point.hx file declares the new member functions and instance variables for our extended class. In this example, we add a couple of new fields and a new member function.
/* File: Point.hx * New member functions and data for the Point class. */ int NP_redraw_state; /* non-persistent data */ Window NP_drawing_window; void draw(); /* new member function */
Our Point.cx
contains the following member function
definitions and constructor extensions. We define the
macro ROSE_CTOR_EXTENSIONS
with initialization code to
extend the default constructor to initialize the new member data.
/* File: Point.cx * Member function definitions and constructor extensions */ #define ROSE_CTOR_EXTENSIONS \ NP_redraw_state = FALSE; \ NP_drawing_window = NULL; void Point::draw() { /* C++ code to draw the point on the screen */ }
Generating Data Dictionary
The libraries need a dictionary of EXPRESS schema definitions to read and write exchange files. These definitions are built into the STEP and IFC libraries, but an application that uses a custom schema will need to create a compiled data dictionary and ship it with their application.
Calling the EXPRESS compiler with the -rose
option
will create a compiled dictionary file named after the original
EXPRESS schemas (<schema>.rose).
% expfront -rose custom_schema.exp
STEP Part 21 files refer to EXPRESS definitions by name, so removing and regenerating compiled schemas is safe. However, files stored in the older binary ROSE file format use UUID references which change if the schema is regenerated.
Files with the _EXPX
suffix contain parse information
used by the EXPRESS interpreter to evaluate WHERE clauses and
derived attributes. These files may be safely removed and recreated
at any time.
Packaging Applications
Applications that use the STEP and IFC libraries are completely self-contained. Applications built with a custom schema must find their compiled dictionary file at runtime.
We have an collection of older dictionary files, called ST-Runtime,
that you can extend with your own compiled schemas. We provide
ST-Runtime as a Windows MSI, a merge module as well as a zip file in
the redist
directory under the ST-Developer
installation.
You can configure where your application looks for the compiled schemas. files. The libraries look at environment variables, and the Windows registry, in the following search order:
$ROSE_SCHEMAS
directorySOFTWARE\STEP Tools, Inc.\ST-Runtime\<version>
registry key plus/schemas
, Windows only. BothHKEY_CURRENT_USER
andHKEY_LOCAL_MACHINE
are examined.$ROSE_RUNTIME/schemas
directory$ROSE/runtime/schemas
directory
You can override these with the rose_get/setenv system_schema_path() function.
Working Sets and Best-Fit Classes
A working set file controls the EXPRESS compiler C++ class generation. This small text file can force extra AND/OR class combinations or extra aggregates, generate a subset of definitions, and change the naming of classes or files.
Working set files normally contain a list of entity or type names.
Each is listed on a separate line, with possible modifiers after them.
Use backslashes (\
) to break an entry over several lines.
Use the hash character (#
) to mark comment lines.
# This is a comment line # The entry for type2 is split across multiple lines # type1 type2 -option1 foo \ -option2 bar
A summary of the known entries and modifiers are summarized below and discussed in the following sections.
SCHEMA schema_name \ [-all | -none] \ -prefix pfx_name \ -p28ns xml_namespace_name entity_name \ -name class_and_file_name \ -file file_name \ -aggs L;S;B;A;LL \ -group grp_name \ -fitpriority ANDOR(ent1_name ent2_name ...) \ -name class_and_file_name \ -file file_name \ -schema schema_name # for identifying and generating subsets of definitions GROUP grp_name \ -prefix pfx_name \ -dllspec dll_symbol \ -schema schema_name \ -include extra_incfile
EXPRESS AND/OR Entities
EXPRESS allows a form of multiple inheritance on a per instance level, which is called an AND/OR or complex entity. A particular data instance has a set of types rather than one EXPRESS type for the combination. These appear as a complex instance in a Part 21 file, with each entity type enclosed in parenthesis. They are commonly used by STEP for "mix-in" combinations of types for units and contexts.
/* A complex instance from a Part21 file. This is a combination of * length_unit and si_unit. Both are subtypes of named_unit. */ #16=( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.) );
When generating C++, we must add extra classes for these complex instance combinations. A limited number of combinations are generally used, so we list them in the working set description and the EXPRESS compiler generates a C++ class for each. We specify a combination with the ANDOR keyword, then a list of type names. e.g.:
ANDOR (type1 type2 type3 ...)
This creates dictionary data and a C++ class. The ROSE library
automatically creates dictionary information for new combinations as
it finds them in a data file, but the workingset file is the only way
to get new classes. The name is built from the type concatenated
together with _and_
. The constructed name uses the types
in alphabetical order.
# A complex class length_unit_and_si_unit is used for the Part21 # sample data shown above. ANDOR( si_unit length_unit )
If the working set contains only AND/OR entries, it will generate all classes. Below is a sample working set file with some common AND/OR combinations for STEP schemas. The ST-Developer documentation has complete workingset files for each pre-installed application protocol or other model.
# The AP units for area, length, mass, plane_angle, solid_angle, and # volume. Create SI and conversion unit combinations for most. Just # SI for solid angle, because only steradians are ever used. ANDOR( si_unit length_unit ) ANDOR( si_unit mass_unit ) ANDOR( si_unit plane_angle_unit ) ANDOR( si_unit solid_angle_unit ) ANDOR( conversion_based_unit mass_unit ) ANDOR( conversion_based_unit length_unit ) ANDOR( conversion_based_unit plane_angle_unit ) # # Spline curves and surfaces (nurbs) # ANDOR( rational_b_spline_curve b_spline_curve_with_knots ) ANDOR( rational_b_spline_curve bezier_curve ) ANDOR( rational_b_spline_curve quasi_uniform_curve ) ANDOR( rational_b_spline_curve uniform_curve ) ANDOR( rational_b_spline_surface b_spline_surface_with_knots ) ANDOR( rational_b_spline_surface bezier_surface ) ANDOR( rational_b_spline_surface quasi_uniform_surface ) ANDOR( rational_b_spline_surface uniform_surface )
When the compiler generates a C++ class for and AND/OR entry, it
concatenates the entity names to produce a name for the class. If you
use the default rose name style, the names are concatenated
with _and_
between them. If you use the sdai
or fullsdai style, the names are capitalized before
concatenating them.
ANDOR ( si_unit plane_angle_unit ) plane_angle_unit_and_si_unit -- ROSE Config_control_design_Plane_angle_unitSi_unit -- full SDAI Plane_angle_unitSi_unit -- terse SDAI
Some of these names can get absurdly long, so you may want to specify your own using the -name option. If you are using the -prefix option, note that the prefix will only be applied to the final concatenated name, not to each element of the name.
ANDOR( measure_representation_item length_measure_with_unit ) \ -name stp_length_measure_representation_item
Best-Fit Classes
The ROSE library keeps runtime C++ type information and matches each EXPRESS definition to the most appropriate C++ class. When an application is built with a subset of classes using a working set, or when a data file contains new AND/OR combinations, there may be instances for which there is no exact C++ type.
The figure above shows a Polygon instance with no matching C++ type. This is represented in memory as an instance of a more general C++ type, such as RoseObject. The RoseObject class has no special access or update functions for Polygon attributes, but the late-bound RoseObject::get<name>()/put<name>() functions can be used to manipulate the values.
The ROSE library can do sophisticated class matching. When reading
an instance, the library will search the C++ type information for a
best-fit
class. The best-fit class is the closest match
to the EXPRESS definition. For example, consider the figure below.
Our application program contains only the Point class, but we
are reading instances of both Point and Extended_Point
types.
The Point instance has an exact match to the Point class. The Extended_Point instance has no exact match, but the Point class is the closest fit. All attributes inherited from Point have generated access and update functions, while any extra attributes defined by Extended_Point can be accessed through late-bound functions. It is an instance of the Point C++ class, but the object is still an EXPRESS Extended_Point and will be written as such if the file is saved.
If a type inherits from several supertypes (multiple inheritance) there might be more than one potential "best fit" class. The library will choose the class that covers the most attributes or subtypes.
Best-fit matching is also used when reading a complex entity instance, that does not contain a matching ANDOR class. For complex instances, the library will look for a class that has a a complex instance combination for a subset of types if the full combination is not available. It finds a combination with a supertype if one with a subtype is not available.
At the other extreme, the figure below shows an application that contains no generated classes. In this situation, every instance will be matched with the built-in RoseObject types. In fact, entity instances will be matched to RoseStructure, selects to RoseUnion, and aggregates to one of the built-in RoseAggregate subtypes such as RosePtrList, RosePtrSet, RosePtrBag, or RosePtrArray.
Generating a Subset of Definitions
STEP schemas can be large, but some have conformance classes with well-defined subsets of definitions. If you list the subset in a working set file, the EXPRESS compiler can generate classes for just these entities. References to anything outside of the working set appear as the generic type RoseObject.
When you read a data set into memory, objects in your working set are instantiated as the appropriate C++ class, while others are instantiated as RoseObjects. No data is lost. Attributes of the RoseObject instances are accessed through late-bound functions as in Data-Dictionary Access and Update.
The following example compiles the EXPRESS file schema.exp and generates C++ only for entities listed in the file myents:
% expfront -classes -ws myents schema.exp
where the myents file contains:
% cat myents ent_one ent_two ent_three ent_four %
In this case, the compiler generates C++ classes for ent_one through ent_four, but not for any other entities. If ent_one contained a attribute called some_att of some type ent_five, the compiler would generate an access and update function for it, but it would be of type RoseObject.
Changing Class and File Names
A workingset file can also control the file or class name used for a definition. The C++ class name or source file name can be changed as needed, but will still correspond to the original EXPRESS type.
As we have seen, the simplest workingset entry is just the name of a definition. The following will generate a C++ class Automobile for the EXPRESS type of the same name. The class definitions are put in the files Automobile.h/cxx
Automobile
The -file option changes the file name, but not the class name. The example below will generates a C++ class Automobile in the files auto.h/cxx. Use this option to make file names conform to special naming conventions such as 8.3 filesystems.
Automobile -file car
The -name option changes the file name and the class name. The example below generates a C++ class auto for the EXPRESS type named Automobile, and puts the class definition into auto.h/cxx.
Automobile -name auto
Adding a Prefix to Class Names
STEP application protocols define entites with common names (point,
line, address, vertex). If you are use other packages, you may see
name clashes because some other package contains a point
or line
class. (For example, one geometry kernel has
conflicts with bounded_curve, curve, ellipse, pcurve, plane, surface,
and vector)
The -name option will rename individual classes, but to rename them all, you can specify a prefix for all generated type names (entities, selects, and enumerations). You can give the prefix either on the command line or in a workingset file.
On the command line, just call the EXPRESS compiler with
the -prefix option. In a workingset file, add -prefix
to the SCHEMA entry. The following examples will prepend
stp_
to all definitions in the AP203 schema.
On the command line: > expfront -classes -prefix stp_ ap203.exp In a workingset file: SCHEMA config_control_design -prefix stp_
Instead of C++ classes like product and cartesian_point, you will get classes named stp_product and stp_cartesian_point. If a workingset file has both a schema prefix and -name for a particular type, the -name will be used for that type.
Other Control Options
Entire schemas can be called out in the working set file using the special SCHEMA keyword, then the name of the schema. You can use the -all and -none directives to control what definitions are generated.
By default, only definitions listed in the control file are generated. If the control file contains only AND/OR entries, all definitions are generated. Putting in a schema entry with the -all directive forces generation of all definitions for the schema. The -none directive stops generation of any definitions for the schema.
SCHEMA config_control_design -all SCHEMA config_control_design -none
There is also an ENTITY keyword to identify entity entries. It is not required, and rarely used, but it might make your working set clearer at times.
Part 28 Namespace
The Part 28 namespace for an AP can be added to the compiled EXPRESS data dictionary by using the -p28ns flag on a SCHEMA entry. If none is provided, XML will be written in the default namespace.
# Sample workingset file entry SCHEMA config_control_design \ -p28ns urn:oid:1.0.10303.203.1.0.1
Extra Aggregates
The EXPRESS compiler generates classes and data-dictionary entries for all aggregates used by the schema. You can use the -aggs directive to force extra aggregate definitions for a base type.
The -aggs argument indicates the aggregate types using the
first letter of the aggregate style. For example L
indicates a list, B
indicates a bag, LL
is a
nested list of list of the base type, and so on. The argument is not
case sensitive.
For example, suppose a schema had a cartesian_point definition, but never used any aggregates. My application might use a list of cartesian points for some temporary processing. Since the schema doesn't use a list, one would not normally be generated, but I could add the following line to my working set file:
cartesian_point -aggs l
This would forces the compiler to generate a ListOfcartesian_point class. By default the compiler generates only those classes listed in the working set file. The schema -all option will force the compiler to generate all of the other classes in addition to cartesian point:
SCHEMA my_schema_name -all <-- generate all classes in schema cartesian_point -aggs l <-- also generate the extra list
If you need to list several aggregates, separate them with semicolons. The examples below force the generation of list, set, bag, array, and list of lists of cartesian point. All three entries are equivalent.
cartesian_point -aggs l;s;b;a;ll cartesian_point -aggs L;S;B;A;LL ENTITY cartesian_point -aggs L;S;B;A;LL