2.1 Overview
This chapter covers aspects of application building, such as environment variables, C++ classes, makefiles, compiling, linking, and other topics. Some details may differ between Windows, MacOS, UNIX, or C++ compilers. The ST-Developer release notes may have more information about a particular platform or compiler.
Section 2.2 describes environment settings on UNIX and Windows. Section 2.3 describes how to compile and link programs. Section 2.6 describe the C++ classes generated from EXPRESS schemas.
Programs do not need a C++ class for each type in an EXPRESS schema. When reading data that do not have an exact C++ class, the ROSE library use the closest available class. Section 2.5 describes how to select a working set of C++ classes for your application.
ROSE applications can work without a full complement of C++ classes because they have access to an EXPRESS data dictionary. Classes only provide strong compile-time type checking and specialized member functions. Section 2.7 describes the EXPRESS data dictionary in greater detail.
2.2 Environment Setup
ST-Developer applications, makefiles, and project files use several environment variables to find the programming libraries, header files, and tools.
On Windows, the ST-Developer installer sets environment values and several registry keys. On MacOS and UNIX, a special "logicals" file is normally run as part of your shell login scripts to set the variables.
On UNIX, the Rose_Logicals file sets environment variables using csh syntax, while Rose_Logicals.sh uses Bourne/Korn syntax. In the following example, replace /usr/local/steptools with the ST-Developer installation directory:
% source /usr/local/steptools/Rose_Logicals # CSH users
% set path=($path $ROSE_BIN) # set path
$ . /usr/local/steptools/Rose_Logicals.sh # Bourne/Korn users
$ PATH=$PATH:$ROSE_BIN # set path
The main variables are $ROSE , $ROSE_BIN , $ROSE_INCLUDE , $ROSE_LIB , and $ROSE_CONFIG and they are used as follows:
ROSE ST-Developer installation directory.
ROSE_BIN Directory containing executables. Usually $ROSE/bin . Put this in your shell search path.
ROSE_INCLUDE Directory containing the ST-Developer C++ header files. Usually $ROSE/include . Used by makefiles.
ROSE_LIB Directory containing the ST-Developer C++ libraries. Usually a subdirectory under $ROSE/lib . When a platform supports different link options or compilers, each set of libraries is in a separate subdirectory. Used by makefiles.
ROSE_CONFIG Makefile configuration file in $ROSE/config that defines settings for your compiler and build options. This file is included by the makefiles for all sample programs.
ROSE_RUNTIME Optional. If set, gives the location of the ST-Runtime support files (compiled schemas, short names abbreviations, etc.). See Packaging Applications for Distribution for more information.
ST-Developer may include versions of the libraries built for different compilers or link options. The $ROSE_CONFIG and $ROSE_LIB variables are set by the install script to your default choice, but you can change them later if you need to use alternate library versions.
The $ROSE_DB variable is an old, optional, variable that should be avoided. If set to a list of directories separated by colons (UNIX) or semicolons (Windows), the ROSE library will use the list as a search path for data files. By default, the default data search path is " .:./schemas ". The search path is also controlled using rose_get/setenv search_path() and RoseInterface::usePath().
setenv ROSE_DB .:/usr/local/schemas:/usr/local/designs # UNIX csh
ROSE_DB=.:/usr/local/schemas:/usr/local/designs # UNIX sh
set ROSE_DB=.:C:\local\schemas;C:\local\designs # Windows
The ST-Runtime directory ( $ROSE/runtime/schemas or Program Files/ST-Runtime/schemas ) is always appended to the search path. This behavior can be controlled using rose_get/setenv system_schema_path().
2.3 Compiling and Linking
ST-Developer ROSE applications are compiled and linked with the C++ compiler. Refer to Linking with other Languages if you must link your application in a different manner.
The $ROSE_INCLUDE and $ROSE_LIB environment variables described above tell the C++ compiler where header and library files are located. The following example shows a compile and link line for a simple program:
On UNIX and MacOS
% CC -I$ROSE_INCLUDE simple.cxx -L$ROSE_LIB -lrose
On Windows
> cl /I"%ROSE_INCLUDE%" simple.cxx /LIBPATH:"%ROSE_LIB%" rose.lib
The -I , /I , -L , and /LIBPATH directives tell the compiler where include files and libraries are located. On Windows we quote the directiories to avoid problems with spaces in the paths. All ROSE applications link against librose.a / rose.lib .
The ST-Developer online documentation has instructions and platform-specific notes on Visual Studio projects, makefiles or other build environments.
Sample Applications
ST-Developer ships with pre-installed class libraries for STEP APs and other information models. These also include sample applications with pre-configured Makefiles and Visual Studio projects. You can start programming just by linking against the appropriate library. Just start with one of the sample programs we provide for each library, then modify as needed.
Bring up the ST-Developer online documentation by selecting the STEP Tools | Documentation entry on the Windows Start menu or by directing your web browser to the following file in the ST-Developer installation directory:
file:/<stdev_install_dir>/docs/index.html
The online documentation has hyperlinked versions of all manuals, PDF versions for printing, and pages for each information model. The model pages contain one or more sample programs with sample output, web-browsable and plain text versions of the EXPRESS schema, lists of classes, and any recommended practices documents. The AP214 page is shown in Figure 2.1.
For each sample program you will see a Windows project link and a MacOS/Unix project link. Right-click on the link to save the project zip file. Unpack the zip file and then either open the project file within with Visual Studio (Windows) or compile the application by running make (MacOS/Unix).
The sample programs contain utilities for creating STEP units, measure values, contexts, and other common data that you can reuse for your own applications.
C++ Header and Source Files
Each of the pre-installed STEP AP libraries provides a stp_schema.h header file that declares the generated classes for the schema. Using this fixed name allows you to move code between APs without changing include directives. The correct version of the stp_schema.h file is found based on the compiler include path in your makefile/project settings.
#include <stp_schema.h> /* for a STEP AP */
For non-STEP AP schemas (like CIS/2 and IFC), or your own local schema, you must include the header file named <schema>.h generated by the EXPRESS compiler that brings in all of the classes.
#include <[schema-name].h> /* for a non-AP schemas */
#include <structural_frame_schema.h> /* CIS/2 */
#include <ifc2x2_final.h> /* IFC */
These header files also bring in the rose.h file for the ROSE Programming Library. There are several conventions for naming C++ source files but we use *.cxx and *.h for simplicity. Section 2.4 describes the generated C++ classes in more detail.
In your compile line, you must add another include path flag ( -I<dir> ) to tell the compiler where the class files are located. The ST-Developer online manuals give the correct paths for each pre-installed schema.
On UNIX and MacOS
% CC -c -I$ROSE_INCLUDE -I$ROSE_INCLUDE/ap203lib simple.cxx
On Windows
> cl /c /I"%ROSE_INCLUDE%" /I"%ROSE_INCLUDE%\ap203lib" simple.cxx
Force-Linking Classes
When reading a STEP file, the ROSE library creates new C++ instances indirectly, so some optimizing linkers may not realize that certain classes are used and will not bring in the class definition from a library.
The EXPRESS compiler generates a "force load" function containing a call to the ROSE_LOAD() macro for each class in the schema. Call this function once in your main() or other always-present function to force the linker to bring in all classes.
The pre-installed STEP AP class libraries declares the stp_schema_force_load() function as part of the stp_schema.h header file so do not need to worry about the schema name:
#include <stp_schema.h>
int main(int argc, char* argv[])
{
/* Force optimizing linkers to bring in all C++ classes */
stp_schema_force_load();
/* Rest of your application */
. . .
}
For non-STEP AP schemas (like CIS/2 and IFC), and classes you generate from yourself, this function is named after the schema ( <schema>_force_load() ) and is defined in a separate header file ( <schema>_ROSE_LOAD.h ).
#include <[schema-name]_ROSE_LOAD.h> /* for a non-AP schemas */
#include <structural_frame_schema_ROSE_LOAD.h> /* CIS/2 */
int main(int argc, char* argv[])
{
structural_frame_schema_force_load();
. . .
}
#include <ifc2x2_final_ROSE_LOAD.h> /* IFC */
int main(int argc, char* argv[])
{
ifc2x2_final_force_load();
. . .
}
Linking with other Languages
Some applications integrate packages written in multiple languages. This is not a problem as long as the C++ constructors for static objects are called. The simplest approach is to always link with the C++ compiler.
The ROSE library creates a static object for each C++ class. These objects (the RoseTypePtr instances returned by the ROSE_TYPE() macro) contain the C++ class information used by ROSE to read, write, create, and access objects. If the static constructors are not called, the ROSE library will not be properly initialized.
If you do not link with the C++ compiler, things will still work on newer platforms because compilers use features of the ELF and COFF object file formats to make sure that constructors and destructors are always called.
On older systems, you may need to do extra work. The original cfront compiler made sure that ctors/dtors were called by linking twice. The object files were linked, then a utility called munch scanned the resulting symbol table to find static constructor calls. It then built a C function that calls each static constructor and places a call to this new function at the beginning of main() .
ST-Developer contains a utility called stepmunch that can reproduce this process by scanning the output of nm , then generating a C function to call the ctors. The example below creates a function called call_ctors() , that calls the static constructors in the library. Your application should call this function when it first starts up.
% nm -g libclasses.a | stepmunch call_ctors > ctors.c
% cc -c ctors.c
The file ctors.o should then be linked in with your application. Refer to the C++ Development Tools section of the ST-Developer Tools Reference Manual for more information about options and usage.
2.4 Generating EXPRESS/C++ Classes
ROSE early-bound applications use C++ classes generated from an EXPRESS information model. This section describes the conventions used when converting an EXPRESS schema into a set of C++ classes.
Basic Operation
The ST-Developer EXPRESS compiler generates C++ classes from EXPRESS information models. The generated source includes a class declaration file ( class.h ) and definition file ( class.cxx ) for every entity, select, aggregate and enumeration in a schema. The compiler also generates the <schema>.h master include file, which includes the definitions of all classes.
The EXPRESS compiler is called expfront . The express2cxx alias calls expfront with the -classes -rose options. The ST-Developer Tools Reference Manual documents the many options and flags recognized by the compiler.
% expfront -classes model.exp
% express2cxx model.exp
The compiler generates class declaration files as <class>.h and class definition files as <class>.cxx , and places the output in the " ./classes " directory. The location can be changed by using the -o option to the compiler.
The class files are given the same name as the entity or type. Filenames for such classes can be customized using a working set and the -file option. See Working Sets and Best-Fit Classes for more information.
ROSE and SDAI C++ Name Conventions
The EXPRESS language is case insensitive, but C++ is case sensitive so a naming convention ensures that EXPRESS names are consistently mapped to C++ symbols. The compiler supports several conventions including SDAI C++ naming.
The pre-installed STEP APs and other schemas are generated with the "rose" convention described below and 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 .
Consider the following EXPRESS definitions:
SCHEMA config_control_design;
ENTITY representation_item
name : STRING;
END_ENTITY;
END_SCHEMA;
The different naming conventions are described below and show how these definitions become C++ classes.
- ROSE Convention . This is the default naming convention. Classes and attributes are given the name and case of the original EXPRESS definition. The entity definition above becomes:
class representation_item {
char * name();
void name (char * str);
};
If a symbol conflicts with a C++ reserved word or ST-Developer function, the compiler will issue a warning and eliminate the conflict by capitalizing the first letter of the word.
- Full SDAI C++ Names . The compiler is called 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. The entity definition above becomes:
class Config_control_design_Representation_item {
char * Name();
void Name (char * str);
};
- Terse SDAI C++ Names . The compiler is called with the -namestyle sdai flag. The terse version omits the initial schema name, but are otherwise the same as the Full SDAI C++ names. 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);
};
Primitive Types
When generating access or update functions for an entity, select, or aggregate, the EXPRESS primitive types are translated into C++ as follows:
The RoseBoolean and RoseLogical types are interchangable. The ROSE_TRUE value is defined to be one, while ROSE_FALSE is zero. ROSE_UNKNOWN is a nonzero value, so it tests as "not false" which matches EXPRESS semantics.
The C++ type used for getting and putting a string value is RoseSTR and values are stored in as null-terminated C strings. The string value passed to a put function is copied and the string value returned by a get function should be treated as read-only.
The C++ type used for getting and putting a binary value is RoseBinarySTR and values are stored in the string encoding used by the Part 21 file format. We provide the RoseBinaryObject helper class to handle bit and byte level access to the data.
The null values for each of these primitive types are described in NULL Attribute Values
Entities
EXPRESS ENTITY definitions become C++ subclasses of RoseStructure. Consider the following EXPRESS definition:
ENTITY Point;
x : REAL;
y : REAL;
END_ENTITY;
This definition becomes the following class definition:
class Point : virtual public RoseStructure {
/* x Access functions */
double x();
void x (double ax);
/* y Access functions */
double y();
void y (double ay);
Point ();
Point (double ax, double ay );
};
The C++ inheritance tree follows the EXPRESS inheritance as closely as possible. If an entity has no supertypes, the C++ class is an immediate subclass of RoseStructure . If an entity has multiple supertypes, the C++ class has multiple superclasses. The C++ classes are generated with virtual base classes to accommodate multiple inheritance.
Access Functions
The class has an access and update functions are generated for every explicit attribute. Derived attributes and WHERE , UNIQUE , and INVERSE clauses are not mapped into C++ at this time.
<type> <attribute_name>(); /* Access Value */
void <attribute_name>( /* Update Value */ <type> value );
For example, our cartesian point entity has "x" and "y" attributes and the following access functions. The C++ double type is used for EXPRESS REAL values as described in Primitive Types.
/* x Access Functions */
double x();
void x (double ax);
/* y Access Functions */
double y();
void y (double ay);
Consult Early-bound Functions for Entities for more information on the access and update functions for entity types.
Constructors
The compiler generates two constructors for each C++ class. The first is a default constructor that takes no arguments. This is the constructor used for the pnewInstance() function and also for a static prototype instance. This constructor sets each instance variable to null. Integer and real attributes are set to zero, booleans and logicals are set to false, and strings and binaries are set to null strings.
Point(); /* default constructor */
Point( /* custom constructor */ double ax, double ay );
The second constructor takes arguments for each instance variable. This constructor is generated for classes that have less than 22 instance variables. (the maximum number of function call arguments allowed by AT&T C++).
AND/OR Complex Classes
EXPRESS complex AND/OR entity types are handled as C++ classes that inherit from each of the multiple types in the complex entity. They have no new attributes, and are specified in the working set files. This is described in greater detail in Working Sets and Best-Fit Classes.
Selects
Each EXPRESS SELECT definition is translated into a subclass of RoseUnion . This class acts as a strongly-typed union, which stores a single value from a range of possible types, and remembers the type of value stored in the union.
This compiler generates an access, an update, and a query function for each possible type that can be stored in the select. Each instance can only hold one value at a time. Calling any of the update functions will replace the previous value.
<type> _<type>(); /* access value */
void _<type>( /* update value */ <typname> value );
RoseBoolean is_<type>(); /* query value */
The functions are named based on each type in the select. The access and update functions have an underscore prefix to avoid conflict with the C++ type name. The query function determines whether the value stored in the select matches the type. Consider the following EXPRESS definition.
TYPE shape = SELECT (Point, Circle, Text);
END_TYPE;
The corresponding C++ class is as follows:
class shape : public RoseUnion {
public:
ROSE_DECLARE_MEMBERS(shape);
/* 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);
/* constructor */
shape () {}
};
Consult Early-bound Functions for Selects for more information on the access and update functions for select types.
Aggregates
Each EXPRESS aggregates becomes a subclasses of RoseAggregate . Lists translate into subclasses of RoseList , arrays to subclasses of RoseArray , etc.
For aggregates, a class named <agg>Of<type> is generated if needed. Aggregates of primitive types are already present in the ROSE library. Aggregates of entities, selects and, enumerations, and other aggregates are generated. For example, the LIST OF LIST OF LIST OF XYZ construct will produce three new classes:
ListOfListOfListOfXYZ
ListOfListOfXYZ
ListOfXYZ
Bounds specifications on aggregates are ignored. The C++ aggregate classes are declared and implemented using macros. Refer to Early-bound Functions for Aggregates, Parameterized Aggregate Classes, and RoseAggregate for more information about declaring and using the C++ aggregate classes.
Enumerations
The compiler generates a C++ enum for each EXPRESS ENUMERATION . Each EXPRESS enumeration is a separate name space, while C++ uses one name space for all enumerators. To avoid conflicts, the compiler prepends the name of the enum to each enumerator separated by an underscore. Consider the EXPRESS:
SCHEMA example_schema;
TYPE colorType = ENUMERATION OF (red, green, blue);
END_TYPE;
END_SCHEMA;
The generated C++ enum will be:
enum colorType {
colorType_NULL = NULL_ENUM,
colorType_red = 0,
colorType_green,
colorType_blue
};
The compiler also generates a null value for each enum. These null values are equivalent to the constant NULL_ENUM , but are strongly-typed.
If you use one of the SDAI C++ name styles, the value and the type name are separated by two underscores, and uses <typename>_unset . for the NULL value. Using the full SDAI C++ name style, the C++ enum will be:
enum Example_schema_Colortype {
Example_schema_Colortype_unset = NULL_ENUM,
Example_schema_Colortype__red = 0,
Example_schema_Colortype__green,
Example_schema_Colortype__blue
};
Using the terse SDAI C++ name style, the generated C++ will be:
enum Colortype {
Colortype_unset = NULL_ENUM,
Colortype__red = 0,
Colortype__green,
Colortype__blue
};
Class Library Makefiles
If we have not provided pre-installed class library for your schema, or you want to change a pre-installed library, you can generate your own using the ST-Developer EXPRESS compiler.
First generate C++ classes as described in the previous section. Next you must compile the class files and build a library. On Windows, you may find it easiest to just import the source files to a Visual Studio project. Another option is to use the mkmakefile tool. This generates a makefile that compiles and assembles a library from all class files in a directory. By default the the library is named libClasses.a or Classes.lib , but there are command line arguments to change that.
The example below generates classes from geometry_schema.exp , then creates a makefile in the classes subdirectory. The makefile compiles all classes and collects them into a library called libgeometry.a .
% expfront -classes geometry_schema.exp
% mkmakefile -d classes -lib geometry
% cd classes
% make
Some of command line options for the makefile generator are shown below. Refer to the ST-Developer Tools Reference Manual for a complete list:
-o <file>- Save makefile to <file> rather than Makefile / win32.mak .
-d <dir>- Look in directory <dir> for source files, and generate the makefile there as well. Default is the current directory.
-cflags <str>- Set the value of the $(CFLAGS) macro. All files are given <str> as arguments to the compiler.
-includes <str>- Set the value of the $(INCLUDE) macro. All files are given <str> as arguments to the compiler.
-lib <str>- Set the library name to <str> rather than "Classes". The name can also be given without any flags as the last argument on the command line.
2.5 Working Sets and Best-Fit Classes
A working set file is a small text file that controls how the EXPRESS compiler generates C++ classes and other output. You can use these files to generate a subset of definitions, change the naming of classes, and force the compiler to generate complex AND/OR classes or extra aggregates. ST-Developer online manuals contain sample workinset files for each of the pre-installed schemas.
You can create a working set file using any editor as long as you save your output as plain text. Tell the compiler to use a working set file with the -ws command line option or the Use working set field of the EXPRESS compiler control panel.
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
Generating a Subset of Definitions
The STEP Application Protocols, such as AP-203 and AP-214, 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 Late-bound Functions for All Objects.
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 method 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 geometry kernels, database engines, or other packages, you may end up with 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 AP-203 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.
EXPRESS AND/OR Entities
EXPRESS AND/OR complex entities are a form of multiple inheritance on a per instance level, where one adds types to a particular instance, rather than creating a new subtype to cover the combination. The resulting object has a set of types, which is difficult to map into one C++ class because it does not have a name.
However, a limited number of type combinations are used, and we list them in the working set description. The EXPRESS compiler will generate specific C++ class for each. We specify an AND/OR combination with the ANDOR keyword, then a parenthesized list of type names. e.g.:
ANDOR (type1 type2 type3 ...)
An example working set is shown below. If the components come from different schemas, the -schema option is used to specify the owner for the result. It is not needed if all of the types are from the same schema.
# This adds an AND/OR entity to the workings set. This makes
# it possible to use the following combination in your apps.
# The name is the supers concatenated together with "_and_"
#
b_spline_curve_with_knots
rational_b_spline_curve
ANDOR (b_spline_curve_with_knots rational_b_spline_curve) \
-schema geometric_model_schema
The C++ class generator normally produces classes for everything in the working set, but if the working set is empty, or contains only AND/OR entries, it will generate all classes.
Below is a sample STEP working set file with some common AND/OR combinations. The ST-Developer online 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 ( plane_angle_unit si_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
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 my schema had a cartesian_point definition, but never used any aggregates. In my application, I might want to 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 force the compiler to generate a ListOfcartesian_point class for me. Remember that, by default, with a working set file, the compiler generates only those classes listed in the file. I might want to use the schema -all option if I wasn't using a subset of classes. This would 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
Best-Fit Classes
The ROSE library stores C++ type information and will match an entity instance to the appropriate C++ class. When using a working set, or when a schema has been extended, an application may read instances for which there is no exact C++ type.
Figure 2.2 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 Figure 2.3. Our application program contains only the Point class, but we are reading instances of both Point and Extended_Point types.
The best fit for the Point instance is the exact match Point class. All attributes are contained within the class and have access and update functions. The best fit for the Extended_Point instance is the Point C++ class. Attributes inherited from Point have generated access and update functions, while those defined by Extended_Point can only be accessed through late-bound functions. Although it is an instance of the Point C++ class, the object is still an EXPRESS Extended_Point and will be written back out to secondary storage as such.
With multiple inheritance, we may encounter a situation where an instance could potentially be best-fit to more than one C++ class. This might occur when a file contains an instance of an entity that has more than one supertype, but the application contains C++ classes for only supertypes. This situation often occurs when a file contains a complex entity instance, but the application does not contain a matching ANDOR class.
In a situation such as this, the library must pick only one of the potential best-fit classes. The library will issue the following warning in such a situation:
"typename": Several possible best-fit classes, only one will be used
Consider linking in a specific C++ class for this type.
It is advisable to link in the additional class, because the library will only choose one of the best-fit classes. This may prevent an application from handling such an instance correctly, because the instance may not be of the expected class.
At the other extreme, Figure 2.4 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 .
2.6 Customizing EXPRESS/C++ Classes
The generated C++ classes can be extended, and we provide the extclass tool to simplify adding member functions or data to a generated C++ class in such a way that you can regenerate it without losing your changes. Another, more general, way of adding data is by using Manager Objects.
Using extclass , you put extensions in separate files then add #include statements at key places within the classes. A ".hi" file is used for extra include files, a ".hx" file for new members declarations, and a ".cx" file for the member definitions.
Extending the Declaration File (.h)
Declarations for new functions and non-persistent instance variables need be inserted into the class declaration file <class>.h . We do this by placing the declarations into auxiliary files. The files we will be working with are:
<class>.h- Class declaration file. Generated automatically
<class>.hi- Class extensions. Contains 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. Contains new member function declarations and non-persistent instance variables. The contents of this file are placed inside the scope of the class definition after all other member functions have been declared.
For example, let us examine how to extend the Point class to include new instance variables and member functions. Given the following EXPRESS definition, generate a C++ class using the EXPRESS compiler.
ENTITY Point;
x : REAL;
y : REAL;
END_ENTITY;
We want to extend the C++ class with some graphics routines from our application. These routines might require additional member data and member functions. The first step is to add the hooks into the generated C++ Point class.
% cd classes
% extclass Point
This adds the hooks to the Point.h and Point.cxx files. Here is the Point declaration file. The additions made by the extclass tool have been highlighted.
#include <rose.h>
/* CLASS INCLUDE-FILE EXTENSIONS */
#include "Point.hi"
ROSE_DECLARE (Point) : virtual public RoseStructure {
private:
double PERSISTENT_x;
double PERSISTENT_y;
public:
ROSE_DECLARE_MEMBERS(Point);
/* Access and Update Functions */
double x();
void x (double ax);
double y();
void y (double ay)
/* Constructors */
Point ();
Point (double ax, double ay );
/* CLASS DECLARATION EXTENSIONS */
#include "Point.hx"
};
The extclass tool has added includes for Point.hi and Point.hx . The Point.hi file will bring in the new include files that we will need for our graphics application.
/* 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 will declare the new member functions and instance variables that we need 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.
*/
RoseBoolean NP_redraw_state; /* non-persistent data */
Window NP_drawing_window;
void draw(); /* new member function */
The Point.hx file will be included in the middle of the Point class declaration so any #include statements should be placed in the Point.hi file and not in Point.hx .
Extending Implementation File (.cxx)
Now that we have extended our class declaration, we must finish the job by extending the class implementation. The files we will be working with are:
<class>.cxx- Class implementation file. Generated by the compiler.
<class>.cx- Class extensions. Contains new member function definitions and constructor extensions.
The Point implementation file is shown below. The additions made by the extclass tool have been highlighted.
#include "Point.h"
/* CLASS IMPLEMENTATION EXTENSIONS */
#include "Point.cx"
/* Default Constructor - This constructor may be modified,
* but *DO NOT* add any calls that would initialize ROSE.
*/
#ifndef ROSE_CTOR_EXTENSIONS
#define ROSE_CTOR_EXTENSIONS /* additional initializations */
#endif
Point::Point () {
PERSISTENT_x = 0;
PERSISTENT_y = 0;
ROSE_CTOR_EXTENSIONS;
}
Point::Point (
double ax,
double ay )
{
x (ax);
y (ay);
ROSE_CTOR_EXTENSIONS;
}
We extend the Point constructors to initialize the new member data by defining the macro ROSE_CTOR_EXTENSIONS to expand into initialization code. Our Point.cx contains the following member function definitions and constructor extensions:
/* 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 */
}
The backslashes in the #define statement hide newlines so that it is one line to the C preprocessor. The generated code for the constructors refer to this macro.
Extend All Classes
The extall tool scans a directory for extension files and calls extclass on matching classes. This script searches the current directory for files with the extensions .hx .hi and .cx . It will run the extclass tool to insure that each extension file is included by the appropriate class definition. Since this tool looks at all the files in the directory, you may have problems if there are stray files with the .hx .hi or .cx extensions.
2.7 ROSE Compiled Schemas
Any tool that manipulates EXPRESS-defined data needs access to the EXPRESS schema definitions. For efficiency, ST-Developer tools use a compiled data dictionary form of the EXPRESS schema. EXPRESS and C++ Data Dictionary describes this compiled form in greater detail.
The ST-Runtime package contains the data dictionaries for all of the STEP application protocols and several other popular information models (CIS/2, IFC). If you are using a different one, you will need to compile and install the data dictionary information yourself.
This is a simple process. Call the EXPRESS compiler with the -rose option to generate a compiled schema definition. The compiled schema files are named after the original EXPRESS schemas ( <schema>.rose ).
% expfront -rose my_express.exp
Copy the <schema>.rose file to the ST-Runtime schemas directory ( Program Files\ST-Runtime\schemas or $ROSE/runtime/schemas ). For testing you can also put in a local subdirectory " ./schemas " as well.
Files with the "_EXPX" suffix ( <schema>_EXPX.rose ) are contain parse information used by the EXPRESS interpreter to evaluate WHERE clauses and derived attributes. This file may be safely removed and recreated at any time.
Warning
In most cases, you can remove and regenerate compiled schema files without any problems. STEP Part 21 files refer to EXPRESS definitions by name, so removing and regenerating compiled schemas is safe. However, files stored in binary ROSE working form refer to compiled definitions by object identifier (OID) which change if the schema is removed. This would render unreadable any binary ROSE working form files written with those definitions.
2.8 Packaging Applications for Distribution
Applications built with the ROSE library require ST-Runtime support files to operate properly. You are allowed to ship the ST-Runtime package with your applications, as long as the application is distributed according to the terms of your license for ST-Developer. Unless otherwise noted, these are the only files in ST-Developer that may be redistributed with applications.
ST-Runtime
ST-Runtime is a redistributable package containing runtime support files required by ST-Developer applications. We provide ST-Runtime as a Windows Installer and a merge module as well as a zip file.
The ST-Runtime Windows Installer can be found in the ST-Developer installation directory as redist/struntime.msi . This package can be installer on a Windows machine by double clicking on it. Once ST-Runtime is installed, you can then run your ST-Developer application on the target machine.
An ST-Runtime merge module is also provided so that it can be incorporated into another msi file. It can be found in redist/struntime.msm
Finally, we also provide ST-Runtime as a zip file in redist/struntime.zip . To install, unzip the file then set the $ROSE_RUNTIME environment variable to the location of the struntime_<ver> directory. For example:
> cd /path/to/extract
> unzip $ROSE/struntime.zip
Then before running an ST-Developer application.
> setenv ROSE_RUNTIME /path/to/extract/struntime_11
Custom Configuration
If you prefer, you can force your application to look elsewhere for runtime support files. Start-up parameters are normally taken from the environment or registry. Applications can query these settings and provide different initial values.
See rose_get/setenv registry_key(), rose_get/setenv search_path() and rose_get/setenv system_schema_path() for examples and more information.
If you prefer to ship your own compiled schema files, rather than use ST-Runtime, you must include the *.rose files for all of the schemas you plan to read or write. You must also include the header_section_schema.rose file, which is needed for the Part 21 HEADER section entries.
Search Details
Most Windows applications find runtime support files using the ST-Runtime registry key, and UNIX / MacOSX applications use the $ROSE_RUNTIME environment variable. For historical reasons, the ROSE library searches other places as well and this section describes the search order.
Windows Search Order
The software looks for registry entries under both HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE . This allows per-user customization if desired. The location of the ST-Runtime registry settings can be set with rose_setenv_registry_key() . The default location is SOFTWARE\STEP Tools, Inc.\ST-Runtime\<version> .
The seach order is as follows:
- The environment variable ROSE_SCHEMAS .
- The schemas value of the ST-Runtime registry key.
- The ROSE_RUNTIME environment variable with \schemas appended.
- The default value of the ST-Runtime registry key with \schemas appended. This is the value that is populated by installing the ST-Runtime package, and this is the value that will generally be used in most cases.
- The ROSE_SYSTEM_DB environment variable.
- The ROSE_SYSTEM_DB value of the SOFTWARE\STEP Tools\ST-Developer\<version> registry key.
- The ROSE value of the SOFTWARE\STEP Tools\ST-Developer\<version> registry key with /runtime/schemas appended. This directory must exist or else the search continues to the next step.
- The ROSE value of the SOFTWARE\STEP Tools\ST-Developer\<version> with /system_db/schemas appended to the path. This directory must exist or else the search continues to the next step.
- The environment variable ROSE with /runtime/schemas appended. This directory must exist or else the search continues to the next step.
- The environment variable ROSE with /system_db/schemas appended.
UNIX Search Order
On UNIX machines, the runtime is found exclusively through the environment. The following search order is used, which is the same as the Windows search order without registry values.
- The environment variable ROSE_SCHEMAS .
- The ROSE_RUNTIME environment variable with \schemas appended.
- The ROSE_SYSTEM_DB environment variable.
- The environment variable ROSE with /runtime/schemas appended. This directory must exist or else the search continues to the next step.
- The environment variable ROSE with /system_db/schemas appended.
2.9 ST-Developer Message Window
A ROSE application may display Part 21 syntax errors or warnings when reading a file, as well as other useful status messages. These are normally printed to the console via stdout or stderr as described in Messages, Warnings, and Errors.
ST-Developer for Windows contains the rose_logwin extension to simplify display of status and debugging information from GUI applications (which may not have a console to print to). The extension creates a window to hold a cumulative log of status messages issued by the ROSE library or your application. The messages can be cleared, copied, and saved, and the window can be dismissed when not needed.
The the rose_logwin extension provides its own Windows message pump in a dedicated thread, and can be used by any type of ROSE C++ application: MFC, raw Windows API, or even a console application. The screendump below shows the message window and some diagnostic messages from a sample application.
To enable this functionality, simply link against the rose_logwin.lib library, include the rose_logwin.h file, then call rose_logwin_init() at the start of your program to register the message window with the ROSE library.
After this call, all of the ROSE nessages are redirected to the console, and no other changes are required for your application. Calling rose_logwin_wait() at the end of your program will block until the user dismisses the message box. This gives the user a chance to view the messages, particularly in short-lived programs.
The following example shows how to initialize the message window
#include <rose.h>
#include <rose_logwin.h>
int main (int argc, char ** argv)
{
rose_logwin_init();
/* your code */
rose_logwin_wait();
}
The roselog DLL must be in your path for the log window to appear. It is installed in the ST-Developer bin directory, which should be in your path. If the DLL cannot be found, rose_logwin_init() will return zero and the default error handling used.
If you want to display your own status messages in the window, simply call one of the ROSE error reporting functions:
ROSE.message ("a status message"); // supressed if ROSE.quiet(1);
ROSE.warning ("a warning message");
ROSE.error ("an error message"); // will exit by default