# Generic Attributes

Attributes are general purpose data that can be associated to any entity. Using an A3DRootBase instance, you can browse all its attributes.

Reading or writing generic attributes involves good knowledge on attributes hierarchy:

- An A3DMiscSingleAttributeData instance can hold any value of the following types:
  - 32-bit integer.
  - 32-bit floating-point (IEEE 754).
  - [time_t](https://en.cppreference.com/w/c/chrono/time_t).
  - UTF-8 C-Style string.
- An A3DMiscAttribute instance groups any number of A3DMiscSingleAttributeData together.
- An A3DRootBase instance can hold any number of A3DMiscAttribute.

This page is a general presentation on how to read attributes from any ref A3DRootBase instance.

## Reading a Single Attribute Instance

A single attribute is an instance of A3DMiscSingleAttributeData, a data-only structure which is described as:

    typedef struct
    {
      A3DUns16                  m_usStructSize;
      A3DBool                   m_bTitleIsInt;
      A3DUTF8Char*              m_pcTitle;
      A3DEModellerAttributeType m_eType;
      A3DUTF8Char*              m_pcData;
      A3DUns16                  m_usUnit;
    } A3DMiscSingleAttributeData;

Any attribute is a Title/Value pair where:

- m_pcTitle is either a C-string or a 32 bit unsigned integer.
- m_pcData is of any of the above mentioned type.

### 32-Bit Integer Data

If m_eType == kA3DModellerAttributeTypeInt the data is stored as a 32-bit integer data (A3DUns32):

    if(in_sAttr.m_eType == kA3DModellerAttributeTypeInt)
    {
      A3DUns32 uiData;
      memcpy(&uiData, in_sAttr.m_pcData, sizeof(A3DUns32));
      printf("Value: %d", uiData);
    }

### 32-Bit Floating Point

If m_eType == kA3DModellerAttributeTypeReal the data is stored as a 32-bit floating point data (A3DFloat):

    if(in_sAttr.m_eType == kA3DModellerAttributeTypeReal)
    {
       A3DFloat uiData;
       memcpy(&uiData, in_sAttr.m_pcData, sizeof(A3DFloat));
       printf("Value: %f", uiData);
    }

When the value is a floating-point the field m_usUnit is used to specify a unit for this value. If no unit is provided, m_usUnit is set to A3D_DEFAULT_NO_UNIT.

For more information about generic unit specifiers, see [the unit attributes page](unit_attributes.html).

### UTF-8 C-String

If m_eType == kA3DModellerAttributeTypeString the data is stored as a null-terminated UTF-8 string (A3DUTF8Char*):

    if(in_sAttr.m_eType == kA3DModellerAttributeTypeString)
    {
      A3DUTF8Char* pcData = in_sAttr.m_pcData;
      printf("Value: %s", uiData);
    }

### Time Specifier

If m_eType == kA3DModellerAttributeTypeTime the data is stored as a time specifier as defined by the [time_t specifications](https://en.cppreference.com/w/c/chrono/time_t):

    if(in_sAttr.m_eType == kA3DModellerAttributeTypeString)
    {
      time_t iData;
      memcpy(&iData, in_sAttr.m_pcData, sizeof(A3DInt32));
      printf("Value: %s", asctime(gmtime(&iData)));
    }

## Reading an Attribute

An attribute is an instance of A3DMiscAttribute. Any attribute has a title and zero or more A3DMiscSingleAttributeData:

    void ReadAttribute(const A3DMiscAttribute* in_pAttribute)
    {
      A3DMiscAttributeData sAttributeData = A3D_MAKE_DATA(A3DMiscAttributeData);

      A3DMiscAttributeGet(in_pAttribute, &sAttributeData);

      // Iterate through the single attributes
      const A3DUns32 uiNSingleAttributes = sAttributeData.m_uiSize;
      fo(A3DUns32 sa = 0 ; sa < uiNSingleAttributes ; ++sa)
      {
        const A3DMiscSingleAttributeData* pSingleAttributeData = sAttributeData.m_asSingleAttributesData + sa;
        ReadSingleAttribute(pSingleAttributeData);
      }

      A3DMiscAttributeGet(A3D_NULL_HANDLE, &sAttributeData);
    }

## Getting the List of Attributes

Generic attributes of an `A3DRootBase()` instance are available as an array:

    void ReadAttributes(const A3DRootBase* in_pBase)
    {
      A3DRootBaseData sRootBaseData = A3D_MAKE_DATA(A3DRootBaseData);

      A3DRootBaseGet(in_pBase, &sRootBaseData);

      // Iterate through the attributes
      const A3DUns32 uiNAttributes = sRootBaseData.m_uiSize;
      for(A3DUns32 a = 0 ; a < uiNAttributes ; ++a)
      {
        const A3DMiscAttribute* pAttribute = sRootBaseData.m_ppAttributes[a];
        ReadAttribute(pAttribute);
      }

      A3DRootBaseGet(A3D_NULL_HANDLE, &sRootBaseData);
    }

All attributes are instances of `A3DMiscAttribute()`.

## Attribute Titles

Within `A3DMiscAttributeData` and `A3DMiscSingleAttributeData`, the m_pcTitle may contain:

- A 32 bits unsigned integer if m_bTitleIsInt is A3D_TRUE
- A traditional null-terminated string otherwise

If m_bTitleIsInt is A3D_TRUE, the A3DUTF8Char* value may directly be casted into A3DUns32* and dereferenced.

Here’s an example of function which gets the title of an attribute given its format, and prints in into a buffer:

    // Reads in_pcTitle and in_bTitleIsInt to print the title into
    // The buffer must be long enough to contains the formatted title.
    // Returns A3D_FALSE if in_pcTitle is NULL, A3D_TRUE otherwise.
    A3DBool SPrintTitle(A3FUTF8Char* in_pcTitle, A3DBool in_bTitleIsInt, A3DUTF8Char* out_pcBuffer)
    {
        if(in_pcTitle == NULL) {
          return A3D_FALSE;
        }

        if (in_bTitleIsInt == A3D_TRUE) {
          sprintf(out_pcBuffer, "%d", *((A3DUns32*)in_pcTitle));
        } else {
          wprintf(out_pcBuffer, "%s'", in_pcTitle);
        }

        return A3D_TRUE;
    }

The following examples use SPrintTitle() for readability.

# A Complete Example

The code below is a complete example which traverses an ref A3DRootBase instance and print all its attributes in the standard output in the form:

    // Displays attributes as follow:
    // Attribute Title0:
    // SingleAttribute Title00: Value (Type): Value00
    // SingleAttribute Title01: Value (Type): Value01
    // Attribute Title1:
    // SingleAttribute Title10: Value (Type): Value10
    // SingleAttribute Title11: Value (Type): Value11
    // ...
    void PrintGenericAttributes(const A3DRootBase* in_pRootBase)
    {
      A3DRootBaseData sRootBaseData = A3D_MAKE_DATA(A3DRootBaseData);

      A3DRootBaseGet(in_pRootBase, &sRootBaseData);

      A3DUTF8Char pcTitle[64];

      // Iterate through the attributes
      const A3DUns32 uiNAttributes = sRootBaseData.m_uiSize;
      for (A3DUns32 a = 0; a < uiNAttributes; ++a)
      {
        const A3DMiscAttribute* pAttribute = sRootBaseData.m_ppAttributes[a];

        A3DMiscAttributeData sAttributeData = A3D_MAKE_DATA(A3DMiscAttributeData);

        A3DMiscAttributeGet(pAttribute, &sAttributeData);

          // Display the attribute title
        if(SPrintTitle(sAttributeData.m_pcTitle, sAttributeData.m_bTitleIsInt, pcTitle) == A3D_TRUE) {
          printf("Attribute: %s\n", pcTitle);
        }

        // Iterate through the single attributes
        const A3DUns32 uiNSingleAttributes = sAttributeData.m_uiSize;
        for (A3DUns32 sa = 0; sa < uiNSingleAttributes; ++sa)
        {
          A3DMiscSingleAttributeData* pSingleAttributeData = sAttributeData.m_asSingleAttributesData+sa;

          // Display the single attribute title
          if(SPrintTitle(pSingleAttributeData.m_pcTitle, pSingleAttributeData.m_bTitleIsInt, pcTitle) == A3D_TRUE) {
            printf("Attribute: %s\n", pcTitle);
          }

          // Display the single attribute value on the same line
          switch (pSingleAttributeData->m_eType)
          {
            case kA3DModellerAttributeTypeInt:
              printf("Value (A3DUns32): %d\n", *(A3DUns32*)pSingleAttributeData->m_pcData);
              break;

            case kA3DModellerAttributeTypeReal:
              printf("Value (A3DFloat): %f\n", *(A3DFloat*)pSingleAttributeData->m_pcData);
              break;

            case kA3DModellerAttributeTypeTime:
              printf("Value (Time): %s\n", asctime(gmtime((time_t*)pSingleAttributeData->m_pcData)));
              break;

            case kA3DModellerAttributeTypeString:
              printf("Value (String): %s\n", pSingleAttributeData->m_pcData);
              break;

            default:
              break;
          }

          printf("\n");
        }

        A3DMiscAttributeGet(A3D_NULL_HANDLE, &sAttributeData);

      }

      A3DRootBaseGet(A3D_NULL_HANDLE, &sRootBaseData);
    }

## Attributes and Categories

Organization of `A3DMiscSingleAttribute` instances within A3DMiscAttribute instances is entirely up to the implementation. Yet, upon reading any format to PRC, HOOPS Exchange organizes attributes into categories.

Note

Within a PRC tree, a category is a named A3DMiscAttribute instance grouping named A3DSingleAttributeInstances. This applies only if both A3DMiscAttribute and A3DSingleAttributeInstances instances have a title.

To sum up for any instance of A3DMiscSingleAttribute within an #A3DMiscAttribute:

- If both the A3DMiscSingleAttribute and the #A3DMiscAttribute have a title, then:

  - A3DMiscAttribute::m_pcTitle is the name of the category.
  - A3DMiscSingleAttribute::m_pcTitle is the name of the attribute.

- If only one of the instances have a title, there is no category and m_pcTitle is the name of the attribute.

      void ReadAttributesWithCategories(const A3DRootBase* in_pRootBase)
      {
        A3DRootBaseData sRootBaseData = A3D_MAKE_DATA(A3DRootBaseData);
        A3DRootBaseGet(in_pRootBase, &sRootBaseData);

        // Iterate through the attributes
        const A3DUns32 uiNAttributes = sRootBaseData.m_uiSize;
        for (A3DUns32 a = 0; a < uiNAttributes; ++a)
        {
          const A3DMiscAttribute* pAttribute = sRootBaseData.m_ppAttributes[a];
          A3DMiscAttributeData sAttributeData = A3D_MAKE_DATA(A3DMiscAttributeData);
          A3DMiscAttributeGet(pAttribute, &sAttributeData);

          // Iterate through the single attributes
          const A3DUns32 uiNSingleAttributes = sAttributeData.m_uiSize;
          for (A3DUns32 sa = 0; sa < uiNSingleAttributes; ++sa)
          {
            A3DMiscSingleAttributeData* pSingleAttribute = sAttributeData.m_asSingleAttributesData+sa;

            A3DUTF8Char pcAttributeTitle[64];
            A3DBool bAttributeHasTitle = SPrintTitle(
              pAttribute.m_pcTitle,
              pAttribute.m_bTitleIsInt,
              pcAttributeTitle
            );

            A3DUTF8Char pcSingleAttributeTitle[64];
            A3DBool bSingleAttributeHasTitle = SPrintTitle(
              pSingleAttribute.m_pcTitle,
              pSingleAttribute.m_bTitleIsInt,
              pcSingleAttributeTitle
            );

            // If both the A3DMiscSingleAttribute and the A3DMiscAttribute have a title
            if(bAttributeHasTitle == A3D_TRUE && bSingleAttributeHasTitle == A3D_TRUE)
            {
              printf("Category: %s, Attribute Name: %s\n", pcAttributeTitle, pcSingleAttributeTitle);
            }
            else if(bAttributeHasTitle == A3D_TRUE || bSingleAttributeHasTitle == A3D_TRUE)
            {
              printf("Attribute Name: %s\n", pcAttributeTitle | pcSingleAttributeTitle);
            }
            else
            {
              printf("No Attribute or category name\n");
            }

            printf("\n");
          }

          A3DMiscAttributeGet(A3D_NULL_HANDLE, &sAttributeData);

        }

        A3DRootBaseGet(A3D_NULL_HANDLE, &sRootBaseData);
      }

While very unlikely, it remains possible that both A3DMiscAttribute and A3DMiscSingleAttribute have a NULL title.

Conversation saved
HOOPSY BETA

### Hello! I'm HOOPSY

Your multilingual generative AI assistant for documentation. Ask me anything about HOOPS Exchange in your own language, or choose a sample question below to start a conversation:

What is HOOPS Exchange?
What are the latest release notes for HOOPS Exchange?
How do I start evaluating HOOPS Exchange?
How do I write my first sample application?
How can I get assistance?

HOOPSY may store conversations according to OpenAI's policy. Responses may not always be accurate.

HOOPSY is thinking...