Search code examples
carraysflexible-array-member

Alternative to flexible array member, without dynamic memory allocation


I was using a flexible array member to define a USB-String descriptor:

// defined
typedef struct {
    uint8_t  bLength;
    uint8_t  bDescriptorType;
    uint16_t bString[];
} ATTR_PACKED USB_StringDescriptor;

// declared
#define STR_PRODUCT  L"My Cool Product"
const USB_StringDescriptor __flash manufacturer_string = {
    sizeof(STR_PRODUCT)+2,
    USB_DTYPE_String,       // 0x03
    STR_PRODUCT
};

This worked great and allowed the actual string constants to be defined elsewhere for easy changes between projects. But I can no longer use flexible array members because of compiler restrictions. I would like to avoid hardcoding a char array, I basically just need to tack two bytes to the front of a wide character array. Is there any clever way of declaring an array or a pre-processor macro that could accomplish this?


Solution

  • I see no way to achieve a struct with flexible size without the use of dynamic memory allocation in a portable way.

    However, one of the following two approaches with a fixed size struct might work for you as well:

    1. Define the struct such that bstring is an array of fixed size; If you have only one STR_PRODUCT in your program, you can use it's size. If you have more instances of the struct, define the array with the maximum size you want to allow.
    2. Define the struct with bstring being a pointer; You can still initialize it with a pointer to a string literal. Note that an object will then not contain all the information in a continuous memory block. If you require continuous memory containing all the information, this approach 2 will not work.

    See the following program which demonstrates both approaches:

    #define STR_PRODUCT_1   L"My Cool Product"
    #define STR_PRODUCT_2   L"Another Cool Product"
    #define MAX_PRODUCT_STR  "012345678901234567890"
    
    // Approach 1:
    typedef struct {
        uint8_t  bLength;
        uint8_t  bDescriptorType;
        wchar_t  bString[sizeof(MAX_PRODUCT_STR)];
    } USB_StringDescriptor;
    
    const USB_StringDescriptor manufacturer_string = {
        sizeof(STR_PRODUCT_1)+2,
        0x03,
        STR_PRODUCT_1
    };
    
    const USB_StringDescriptor manufacturer_string2 = {
        sizeof(STR_PRODUCT_2)+2,
        0x03,
        STR_PRODUCT_2
    };
    
    // Approach 2:
    typedef struct {
        uint8_t  bLength;
        uint8_t  bDescriptorType;
        wchar_t  *bString;
    } USB_StringDescriptor_V2;
    
    const USB_StringDescriptor_V2 manufacturer_string_v2 = {
        sizeof(STR_PRODUCT_1)+2,
        0x03,
        STR_PRODUCT_1
    };
    
    const USB_StringDescriptor_V2 manufacturer_string_v2_2 = {
        sizeof(STR_PRODUCT_2)+2,
        0x03,
        STR_PRODUCT_2
    };
    
    int main() {
        wprintf(L"1: %ls\n",manufacturer_string.bString);
        wprintf(L"2: %ls\n",manufacturer_string2.bString);
    
        wprintf(L"1 ptr: %ls\n",manufacturer_string_v2.bString);
        wprintf(L"2 ptr: %ls\n",manufacturer_string_v2_2.bString);
    }