IFC SDK and SDAI: Work with a List of Application Instances

Egor Vorobyov

November 27, 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 aggregate attributes, specifically a list of application instances.

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

An application instance with an aggregate attribute is when each attribute is represented with a handle to another instance. For example, a polyline object is a set of points that defines the polyline route. A polyline can be stored in an .ifc file as the following:

#173= IFCCARTESIANPOINT((-2232.66666666677,-11000.));
#175= IFCCARTESIANPOINT((3767.33333333345,-11000.));
#177= IFCCARTESIANPOINT((3767.33333333345,7000.));
#179= IFCCARTESIANPOINT((-1534.66666666668,7000.));
#181= IFCCARTESIANPOINT((-1534.66666666668,4000.));
#183= IFCCARTESIANPOINT((-2232.66666666677,4000.));
#185= IFCPOLYLINE((#173,#175,#177,#179,#181,#183,#173));

Here we can see the standard access for the attribute of an application instance (#185). We use the sdaiAGGR type to extract the aggregate instance from the attribute:

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

  SdaiAggr  aggregate = NULL;

  TEST_ASSERT(sdaiGetAttrBN(applicationInstance, 
 				"points", 
 				sdaiAGGR, 
 				&aggregate) != NULL);

To get aggregate attribute items via an index, use the sdaiGetAggrByIndex() function. This function accepts four parameters:

  • An aggregate instance that contains the needed attributes.
  • The index value of the attribute to get.
  • The data type of the requested attribute.
  • A raw pointer to the attribute value.

The function returns a raw pointer to the attribute value stored in the fourth output parameter.

First, create an array with aggregate attribute handles to read.

const SdaiInteger checkHandleData[] = { 173, 175, 177, 179, 181, 183, 173 };
const SdaiInteger checkHandleDataLength = sizeof(checkHandleData) /    
 								sizeof(checkHandleData[0]);

Get the number of aggregate attributes by calling the sdaiGetMemberCount() function. The value returned by this function is used as a bound of the cycle used to iterate through attributes:

const SdaiInteger indexUpperBound = sdaiGetMemberCount(aggregate);
  for (SdaiInteger instanceIndex = 0; instanceIndex < indexUpperBound; ++instanceIndex)
  {
    SdaiAppInstance  instanceFromAggrIndex = NULL;

    TEST_ASSERT(sdaiGetAggrByIndex(aggregate, 
      				 instanceIndex, 
     				 sdaiINSTANCE, 
     				 &instanceFromAggrIndex) != NULL);
    
    if (instanceIndex >= checkHandleDataLength){ break; }

    TEST_ASSERT(_sdaiGetEntityId(instanceFromAggrIndex) == 
   				checkHandleData[instanceIndex]);
  }

Another way to get an aggregate attribute is to use an iterator object to get sequential access to attributes. To get the iterator, use the sdaiCreateIterator() function. For interaction with the attributes collection, use the following functions:

  • sdaiBeginning() for moving to the beginning of the attributes collection.
  • sdaiNext() for moving to the next attribute item.
  • sdaiGetAggrByIterator() for getting the attribute value.
SdaiIterator aggregateIterator = sdaiCreateIterator(aggregate);
  TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);

  SdaiInteger iterationCounter = 0;

  for (sdaiBeginning(aggregateIterator); sdaiNext(aggregateIterator);)
  {
    SdaiAppInstance  instanceFromIterator = NULL;
    TEST_ASSERT(sdaiGetAggrByIterator(aggregateIterator, 
                                      sdaiINSTANCE, 
                                      &instanceFromIterator) != NULL);

    if (iterationCounter >= checkHandleDataLength) { break; }

    TEST_ASSERT(_sdaiGetEntityId(instanceFromIterator) ==                 
                checkHandleData[iterationCounter]);

    ++iterationCounter;
  }

To avoid memory leaks, destroy the created iterator:

sdaiDeleteADB(adbToGet);