cstructdynamicunionmtp

How to make a dynamic structure in C


For example if i have a structure like this :

typedef struct __attribute__ ((packed))
{
  uint16_t    ObjectPropertyCode;
  uint16_t    DataType;
  uint8_t     GetSet;
  union
  {
    uint8_t     DefaultValue_u8 ;
    uint16_t    DefaultValue_u16 ;
    uint32_t    DefaultValue_u32 ;
  }DefValue  ;
  uint32_t    GroupCode;
  uint8_t     FormFlag;
}
MTP_ObjectPropDescTypeDef;

I need to have just one element in DefValue , depending on my DataType. for example if my DataType is uint16 , so i will send my structure like this :

typedef struct __attribute__ ((packed))
{
  uint16_t    ObjectPropertyCode;
  uint16_t    DataType;
  uint8_t     GetSet;
  uint16_t    DefaultValue_u16 ;
  uint32_t    GroupCode;
  uint8_t     FormFlag;
}
MTP_ObjectPropDescTypeDef;

Solution

  • What you're suggesting cannot be done (well, reasonably anyway), unless you break your data layout into parts, or change the data layout so that your variable size type will sit at the end.

    With that out of the way, we can get to the unreasonable part. You can (ab)use the preprocessor to effectively achieve this to a degree. (for example):

    #define JOIN3(a, b, c) a##b##c
    #define MYSTRUCT(n) \
        struct __attribute__ ((packed)) { \
            uint16_t ObjectPropertyCode; \
            uint16_t DataType; \
            uint8_t GetSet; \
            JOIN3(uint, n, _t) DefaultValue; \
            uint32_t GroupCode; \
            uint8_t FormFlag; \
        }
    
    // ...
    
    {
        MYSTRUCT(8) obj; // uint8_t DefaultValue
        printf("%lu\n", sizeof(obj)); // 11
    
        MYSTRUCT(16) obj2; // uint16_t DefaultValue
        printf("%lu\n", sizeof(obj2)); // 12
    
        MYSTRUCT(32) obj3; // uint32_t DefaultValue
        printf("%lu\n", sizeof(obj3)); // 14
    }
       
    

    But, it achieves the similar effect as just having three different structs to begin with. Consider using alternative methods where applicable, i.e. breaking the struct to parts, having different ones, or just using a plain buffer.

    Consider the approach below which would build a buffer from the data provided. For example:

    void build_output_buffer(const MTP_ObjectPropDescTypeDef *def, uint8_t *out)
    {
        size_t header_size = offsetof(MTP_ObjectPropDescTypeDef, DefValue);
        memcpy(out, def, header_size);
        out += header_size;
        switch (def->DataType) {
            // Copy the appropriate value, e.g.:
            case DataType_U32: { // For demonstration's sake
                memcpy(out, &def->DefValue, 4);
                out += 4;
            } break;
        }
        size_t footer_offset = offsetof(MTP_ObjectPropDescTypeDef, GroupCode);
        size_t footer_size = sizeof(MTP_ObjectPropDescTypeDef) - footer_offset;
        memcpy(out, (uint8_t*)def + footer_offset, footer_size);
    }
    

    And make sure the buffer passed in will be big enough to hold all the data. Alternatively allocate it inside the function and return it. Or just skip the buffer and send the data out with similar logic.