IFC SDK and SDAI: Work with Attributes of Simple Data Types

Egor Vorobyov

October 22, 2020

IFC SDK supports the Standard Data Access Interface (SDAI) which provides a low-level API for manipulating data defined with the EXPRESS Schema. This article describes how to work with attributes of simple data types.

For details about generally working with SDAI in IFC SDK, see this article.

Accessing simple type attributes

There are several simple types in SDAI: sdaiINTEGER, sdaiREAL, sdaiNUMBER, sdaiBOOLEAN, sdaiLOGICAL, sdaiSTRING, and sdaiINSTANCE.

For this example, we will read one of these attributes from an application instance #5:

//#5 = IFCAPPLICATION(#1, '2019', 'Autodesk Revit 2019 (ENU)', 'Revit');

First we get an application instance using sdaiGetEntityById. Then we call sdaiGetAttrBN to get the attribute from an instance. sdaiGetAttrBN has four parameters:

  • Instance
  • Name of attribute
  • Type of the queried parameter
  • Pointer to a parameter where to copy the value from the attribute

The function returns a pointer to the fourth parameter if successful and null otherwise:

SdaiAppInstance applicationInstance = _sdaiGetEntityById(model, 5);
TEST_ASSERT(applicationInstance != NULL);
TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);

We will make three attempts to read parameters next.

In the first attempt we get a parameter named “applicationdeveloper” successfully. We received an application instance with handle #1. So let’s check the received data. Get the ID from the application instance using _sdaiGetEntityById. It should be equal to 1:

SdaiAppInstance developer = NULL;
TEST_ASSERT(sdaiGetAttrBN(applicationInstance, 
 				"applicationdeveloper", 
 				sdaiINSTANCE, 
 				&developer) != NULL);
TEST_ASSERT(developer != NULL);
TEST_ASSERT(developer == _sdaiGetEntityById(model, 1));
TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);

In the second attempt we get a string value from the attribute named "version". It should read an attribute value successfully too. Compare it with “2019” to make sure that we read valid data:

SdaiString version = NULL;
TEST_ASSERT(sdaiGetAttrBN(applicationInstance, "version", sdaiSTRING, &version) != NULL);
TEST_ASSERT(odStrCmpA(version, "2019") == 0);
TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);

In the third attempt we try to get an integer value from an attribute named "version". The operation fails because we requested an incompatible type sdaiINTEGER. sdaiGetAttrBN returns NULL in this case. Call sdaiErrorQuery to get the last error and reset the error state. The error codes are collected at the error query using FIFO rules. Each sdaiErrorQuery call removes the last error state from the error queue:

SdaiInteger wrongAttrType = -1;
TEST_ASSERT(sdaiGetAttrBN(applicationInstance, "version", sdaiINTEGER, &wrongAttrType) == NULL);
TEST_ASSERT(sdaiErrorQuery() == sdaiVT_NVLD);

Another kind of simple type is an enumeration. SDAI also supports getting enumeration values from an application instance.

For example, suppose an .ifc file contains this entry in the DATA section:

// #43= IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.);

The string value within the dots is an enumeration value. Now read one of the values. At first, we get an instance from the model using _sdaiGetEntityById:

SdaiAppInstance applicationInstance = _sdaiGetEntityById(model, 43);
TEST_ASSERT(applicationInstance != NULL);
TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);

Then we ask the application instance to return a typed attribute. The enumeration has its own typedef:

/* enumeration data type: */
typedef char *SdaiEnum;

As we see, its value represents a string value. Declare a variable of this type and pass a pointer to this value as the fourth parameter to sdaiGetAttrBN. If the operation is successful, we get this parameter to compare it with the value from the sample file:

SdaiEnum enumValue = NULL;
TEST_ASSERT(sdaiGetAttrBN(applicationInstance, 
  				"prefix", 
 				sdaiENUM, 
   				&enumValue) != NULL);
TEST_ASSERT(!odStrCmpA(enumValue, "MILLI"));