The RoseP21Writer class is an internal utility class used for writing object data as STEP ISO 10303-21 (Part 21) formatted files. It is not created or used directly, but it does have a few class variables that can be adjusted to customize the appearance of output files.

See STEP Part 21 Exchange Files for more information on writing and customizing STEP files.


typedef RoseErrorCode (*RoseP21WriteCommentFn) (
	FILE * file

static RoseP21WriteCommentFn comment_fn;

The comment_fn static class variable that holds a hook function called once to put a comment in the header section of the Part 21 file. This allows you to identify your product in more detail than allowed by the originating system field in the FILE_NAME() entry, or for documenting internal settings.

If this variable is not null, the function that it points to will be called while writing the beginning of the header section. The function is called with a pointer to the file being used.

    /* EXAMPLE */
    RoseErrorCode write_custom_comment (FILE * file) {
        fprintf (file, "/* Produced by MonkeyCAD\n");
        fprintf (file, " * Debugging Information:\n");
        . . .
        fprintf (file, " */");
        return ROSE_OK;
    /* somewhere in your main() */
    RoseP21Writer::comment_fn = write_custom_comment;

See Also

Reading and Writing Comments;


typedef char * (*RoseP21EnumNameFn) (
	RoseObject *    obj, 
	RoseAttribute * att, 
	const char *	real_name,
	int		real_value

static RoseP21EnumNameFn enum_name_fn;

The enum_name_fn static class variable can specify a hook function that will be called to find the string value of an enumeration. This variable is normally null and the enum string is found from the compiled EXPRESS definitions.

If the variable is set, the hook function will be called and passed the object and attribute being written, the numeric value of the enum, and string values that would normally be written. The function should return the actual string value to be used. A null ($) will be written if the function returns an empty string or null pointer.


static unsigned fill_column;   	/* line wrapping */ 

The fill_column static class variable can specify where to wrap lines when writing Part 21 files. By default, this value is 70 which means that the library will attempt to wrap lines so that they are not longer than 70 characters. Setting this to zero will disable wrapping completely.

The STEP Part 21 file format explicitly ignores line break characters (ISO 10303-21 clause A.2), so lines can be as long or short as desired. We recommend that you leave this setting at the default value, because this will prevent email systems from corrupting STEP, IFC, and CIS/2 files if they are mailed without first zipping them.

Email software will strip or double periods in a message, usually when they appear as the first character of a line. If you mail a file as an attachment and did not wrap the lines yourself, the mailer will, and if a number or enum value happens to be the first thing in the line, it could be make the syntax bad (.F. to ..F. or F.) or (.123 to ..123 or even worse 123!) and the system on the other side will not be able to read them. Zipping files avoids this, but your customers will probably not do so.

When wrapping is enabled, the Part 21 writing software checks to make sure that period characters do not appear in column zero and will delay wrapping or insert spaces as appropriate to make sure that the file can be emailed safely.

     RoseP21Writer::fill_column = 70;  /* recommended value */
     RoseP21Writer::fill_column = 1;   /* one char per line! */
     RoseP21Writer::fill_column = 0;   /* no wrapping, watch for email corruption */

See Also

Line Wrapping


static RoseBoolean preserve_eids;

The preserve_eids variable controls whether instances are given new entity_id() numbers (the #numbers "#123=") when a file is written. By default, this value is false and entities are renumbered each time they are written.

If set to true, the software will try to use the existing value of entity_id() for each instance. If an entity instance has a non-zero ID number, that will be used, and if the ID is zero and new one will be assigned one that does not conflict with any existing IDs. Preserving IDs can slow down the creation of Part 21 files, so this variable is set to false by default.

This entity numbering behavior is provided by the rose_p21_dflt_renumber() function. If you have provided a custom renumber function using the renumber_fn hook described below, the

preserve_eids variable will have no effect.

    /* save eids from input file */
    RoseP21Writer::preserve_eids = ROSE_TRUE;
    /* (default) renumber all entities, most effiecient */
    RoseP21Writer::preserve_eids = ROSE_FALSE;   

See Also

Entity ID Numbering


static unsigned int renumber_density;

The renumber_density static class variable controls how IDs for new object are found when the preserve_eids variable is set to true. To generate an ID number for a new instance, the software can either increment the largest ID in the file or it can find gaps in the numbering sequence and try to fill those.

Incrementing the largest ID is fast, but may lead to numeric overflow. Filling gaps is safer, but requires the creation of an index. The algorithm to use is determined by the value of the renumber_density variable. If the ratio of the largest ID in the model divided by the number of instances in the model is greater than renumber_density, then the gaps are filled, otherwise the values are appended to the file. The default value is 5.

See Also



typedef RoseErrorCode (*RoseP21RenumberFn) (
	RoseDesign * design

static RoseP21RenumberFn renumber_fn;

The renumber_fn static class variable hold a pointer to a function that will be called to assign ID numbers to all instances before writing begins. By default, this varable is null and the rose_p21_dflt_renumber() function is called to renumber the entities. See the preserve_eids flag for more information on customizing the default behavior.

If a hook function is specified, it should traverse the entire design, assigning new IDs with RoseObject::entity_id() and then should return ROSE_OK to indicate successful completion. The following function demonstrates how to renumber the objects by increments of five. It uses a RoseCursor to visit every entity in the data sections of a design.

    RoseErrorCode renumber_by_five (RoseDesign * design)
        RoseCursor objects;
        register RoseStructure * object;
        register unsigned long eid = 5;

        /* Traverse entities in all data sections. Seting the 
         * cursor section type is not really needed since data sections
         * are the default */
        objects.traverse (design);
        objects.section_type (ROSE_SECTION_DATA); /* the default */
        objects.domain (ROSE_DOMAIN(RoseStructure));

        /* number the entity ids by 5s */
        while (object = (RoseStructure *) objects.next()) {
            object-> entity_id (eid);
            eid += 5;

        return ROSE_OK;

    /* to use this function, be sure to set renumber_fn */
    RoseP21Writer::renumber_fn = renumber_by_five;

    /* reset to default behavior */
    RoseP21Writer::renumber_fn = 0;

See Also

Entity ID Numbering


typedef const char * (*RoseP21SchemaNameFn) (
	RoseDesign * design,   /* added in v11 */
	RoseDesign * schema

static RoseP21SchemaNameFn schema_name_fn;

The schema_name_fn static class variable can specify a hook function that supplies the names of each schema in a design. By default, this variable is null and the schema name will be taken from the compiled EXPRESS schema associated with the design or generates C++ classes.

If given, the hook function will be called on each element of the schemas() list of the design. If the function returns null for a particular schema, no entry will be written for it in the Part 21 FILE_SCHEMA list. The function is passed two arguments, a pointer to the design being written, and a pointer to the schema (which is also a design).

typedef const char * (*RoseP21SchemaNameFn) (
    RoseDesign * design,
    RoseDesign * schema,

static RoseP21SchemaNameFn RoseP21Writer::schema_name_fn;

const char * a_simple_write_schema_function (
    RoseDesign * /* design, unused */,
    RoseDesign * schema 
    return schema-> name();

See Also

Customized Schema Handling


static RoseBoolean sort_eids;

The sort_eids static class variable controls the order in which entity instances are written to a Part 21 file. By default, the instances are written in the most efficient manner, which may or may not be sorted.

In general, the entities will be sorted, since the writer assigns IDs to the instances and then to write them in the same order. If you are using the preserve_eids described above to save IDs from an existing file, then writing using the most efficient traversal may result in instances appearing in a random order. An unsorted Part 21 file may look strange but is completely legal.

If you are using preserve_eids and want to force the instances to be written in numeric order, set the sort_eids variable to true. This forces the library to build an index of the IDs before writing the file, which is somewhat expensive.

    /* only write instances sorted by ID */
    RoseP21Writer::sort_eids = ROSE_TRUE;

    /* (default) write in the most efficient way */
    RoseP21Writer::sort_eids = ROSE_FALSE;

See Also


status_fn / status_freq

typedef RoseErrorCode (*RoseP21WriteStatusFn) (
	RoseP21Writer *, 
	unsigned long

static RoseP21WriteStatusFn status_fn;
static unsigned status_freq;

The status_fn static class variable specifies a hook function that will be called periodically when writing a Part 21 file. The status_freq variable gives the calling frequency as a number of objects. A value of 100 means the status function will be called after every 100 objects are written. A value of 1 means the function will be called after every object. If the calling frequency is zero or the function pointer is null, the status function will not be called.

The status function returns a RoseErrorCode. Successful completion is indicated by the value ROSE_OK. If a non-zero value is returned, file writing will be halted, so applications can use this to implement a cancel button for a status bar.

See Also

Periodic Status Updates; RoseP21Parser::status_fn


static void use_conformance_class (
	unsigned flag

The use_conformance_class() function can be used to change the conformance class which determine how entity instances are written to a file. The Part 21 specification supports two different conformance classes.

Under conformance class one (CC1), only complex entity instances are written using an external mapping, while all other entities are written using the internal mapping. This is the default behavior for ST-Developer and other STEP processors. Under conformance class two (CC2), which is rarely used, all entities with supertypes are written using the external mapping. Use the symbol PART21_INTERNAL_CC to indicate CC1 and the symbol PART21_EXTERNAL_CC to indicate CC2.

    /* default conformance class */
    RoseP21Writer::use_conformance_class (PART21_INTERNAL_CC);
    /* write everything as an external mapping */
    RoseP21Writer::use_conformance_class (PART21_EXTERNAL_CC);


static int use_short_names;

The use_short_names static variable controls whether long or short names are written for type names in STEP Part 21 files. This variable is false By default and the full name is written, which is the most widely supported usage.

If you set it to a nonzero value, a short name alias will be used if a schema equivalents file (<schema_name>.nam) is present available in the schema search path and contains an alias for the particular type. See Short Names for more information.

RoseP21Writer::use_short_names = 1;   /* short names if possible */ 
RoseP21Writer::use_short_names = 0;   /* default, always long names */

use_spec_version() / max_spec_version() / min_spec_version()

static void max_spec_version (
	unsigned flag
static void min_spec_version (
	unsigned flag
static void use_spec_version (
	unsigned flag

The use_spec_version() function and related max/min functions control the publication version of the ISO 10303-21 specification that is used to encode a file. Several different editions are possible as described below:

The symbol PART21_IS indicates the original 1994 version, which was replaces shortly after publication by a revised, technical corrigendum version - PART21_TC. The TC version is the most commonly used version. The PART21_ED2 symbol indicates the second edition of Part 21, which was published in 2001 and adds support for multiple data sections in a file. The PART21_ED3 symbol indicates the third edition of Part 21, which is scheduled for publication in 2014 and adds URL links between files, ZIP archive support, and Javascript capabilities.

For maximum compatibility with other implementations, the ROSE library uses the oldest specification version that will support the contents of the file. For example, when a file contains second edition features (multiple data sections, etc.) the second edition specification will be used, otherwise the technical corregendum version will be used. The max_spec_version() and and min_spec_version() functions set the upper and lower limit for the allowable versions. The use_spec_version() function sets the upper and lower limit to the same value.

By default, the minimum version is PART21_TC and the maximum version is PART21_ED2. If you want to experiment with the new Third Edition features, you can set the max to PART21_ED3. If you need to write a file for use with an application from 1995 that expects the original IS encoding for SELECT values, you can set the upper and lower to PART21_IS

// Allow new ISO 10303-21:2016 features
RoseP21Writer::max_spec_version (PART21_ED3);

// Default behavior, try ISO 10303-21:1994, Corr 1:1996, 
// but use ISO 10303-21:2002 (Edition 2) if needed
RoseP21Writer::max_spec_version (PART21_ED2);
RoseP21Writer::min_spec_version (PART21_TC);

// Force files to use original ISO 10303-21:1994
RoseP21Writer::use_spec_version (PART21_IS);