mercredi 11 mars 2015

Using C++ variadic templates to initialize variable-length arrays in static structs

I've been writing some code that deals with USB at the packet level. One "hangup" I've run into is that with USB, you have some structs that contain arrays of other structs (not pointers):



typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
} PACKED endp_descriptor_t;

typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
endp_descriptor_t endpoints[];
} PACKED int_descriptor_t;

typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
int_descriptor_t interfaces[];
} PACKED cfg_descriptor_t;


The cfg_descriptor_t contains zero or more int_descriptor_ts, and each int_descriptor_t contains zero or more endp_descriptor_ts.


I'm working on a microcontroller with fairly limited space (256K flash, 64K RAM), so I wanted to avoid dynamic allocation as much as possible and define these structs at compile time:



static cfg_descriptor_t cfg_descriptor = {
.bLength = 9,
.bDescriptorType = 2, //config descriptor
.wTotalLength = 18,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0x80, //bus powered, no remote wakeup
.bMaxPower = 250, //x2 = 500mA (maximum allowed by spec)
.interfaces = {
{
.bLength = 9,
.bDescriptorType = 4, //interface
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = 0xFF,
.bInterfaceSubClass = 0x0,
.bInterfaceProtocol = 0x0,
.iInterface = 0,
.endpoints = {
{
.bLength = 7,
.bDescriptorType = 5, //endpoint
.bEndpointAddress = 0x81,
.bmAttributes = 0x03,
.wMaxPacketSize = 64,
.bInterval = 10,
}
}
}
}
};


Unfortunately, it seems gcc doesn't understand what I'm trying to do here, and complains about too many initializers for ‘int_descriptor_t [0]’ - i.e. it apparently can't determine the length of the array from the initializers. (Maybe I've just made some simple mistake here?)


I thought I might take advantage of C++11's variadic templates for this:



template<typename... interfaces>
struct PACKED x_cfg_descriptor_t {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
interfaces...;
};


but this doesn't work either: expected unqualified-id before ‘...’ token on the interfaces... line.


I've seen some examples of variable-sized tuples using variadic templates, but I couldn't quite understand what they were doing, and they suggested that the actual layout in memory would be in reverse order of the template parameters. (I think recursive templates are involved...)


So the question is: can I define a static struct instance with a variable number of members at compile time?


Aucun commentaire:

Enregistrer un commentaire