Is it possible to pad a C/C++ structure to a fixed size using a compile time calculation?

Viewed 204

Is it possible to compute at compile time the size of a padding array required in a structure such that the structure ends up being a per-determined size. That is I would like to do something like this (which I know won't work)

#define APPDESCTARGETSIZE       256         // Target size for the App Desc Structure

// Structure member size of
#define msizeof(type, member) sizeof(((type *)0)->member)

// Size of padding arrary
#define padsize     (APPDESCTARGETSIZE - (offsetof(custom_app_desc_t, pad) + msizeof(custom_app_desc_t, tail)))

/**  * @brief Description about application.  */ 
typedef struct {
    uint8_t   header[16];
    char      progname[16];
    char      boardname[16];
    uint8_t   chipsize;
    uint8_t   version_maj;
    uint8_t   version_min;
    uint8_t   version_iss;
    uint8_t   pad[padsize];
    uint8_t   tail[16]; 
 } custom_app_desc_t;

I can solve with a manual calculation such as below but it would nice for the compiler to do math for me:

#define padsize     (APPDESCTARGETSIZE - 68)

NOTE, it is important in this case that pad occurs before the 'tail' member.

3 Answers

One way to solve this is with a union:

struct {
    union {
        struct {
            // this is your structure, without the last element
        };
        char _reserved[APPDESCTARGETSIZE - 16]; // exclude the last tail element
    };
    char tail[16]; // the tail is outside of the padding
};

the easiest IMO way is to wrap into another struct.

#define APPDESCTARGETSIZE       256  
#define TAILSIZE    16
#define TAILTYPE    uint8_t

typedef struct {
    struct internal{
        uint8_t   header[16];
        char      progname[16];
        char      boardname[16];
        uint8_t   chipsize;
        uint8_t   version_maj;
        uint8_t   version_min;
        uint8_t   version_iss;
    }fields;
    uint8_t   pad[APPDESCTARGETSIZE - sizeof(struct internal) - sizeof(TAILTYPE) * TAILSIZE];
    TAILTYPE   tail[TAILSIZE]; 
 } custom_app_desc_t;

or as _Blindy (but using anonymous structure)

#define APPDESCTARGETSIZE       256  
#define TAILSIZE    16
#define TAILTYPE    uint8_t

typedef struct {
    union {
        struct {
            uint8_t   header[16];
            char      progname[16];
            char      boardname[16];
            uint8_t   chipsize;
            uint8_t   version_maj;
            uint8_t   version_min;
            uint8_t   version_iss;
        };
        struct {
            uint8_t _dummy[APPDESCTARGETSIZE - sizeof(TAILTYPE) * TAILSIZE];
        };
    };
    TAILTYPE   tail[TAILSIZE]; 
 } custom_app_desc_t;

Using unnamed union and structure members, you can control the layout for your purpose:

#define APPDESCTARGETSIZE  256    // Target size for the App Desc Structure

/**  * @brief Description about application.  */ 
typedef struct {
    union {
        struct {
            uint8_t   header[16];
            char      progname[16];
            char      boardname[16];
            uint8_t   chipsize;
            uint8_t   version_maj;
            uint8_t   version_min;
            uint8_t   version_iss;
        };
        uint8_t   pad0[APPDESCTARGETSIZE - 16];
    };
    uint8_t   tail[16]; 
} custom_app_desc_t;

You can access the members simply as if they were members of custom_app_desc_t:

    custom_app_desc_t obj;
    get_app_desc(&obj);
    printf("progname: %s\n", obj.progname);

This handy syntax trick is called anonymous union and anonymous structure.

The size of the anonymous union is the largest of the size of each of its members, ie: the structure with your named members and the byte array pad0. Since the named members amount to less than APPDESCTARGETSIZE-16 bytes, the union size is exactly APPDESCTARGETSIZE-16 which is the goal of the exercise.

If you need to access the padding bytes, you could add an extra member at the end of the inner structure uint8_t pad[1];

If the tail part is not a simple byte array, you need to define a structure type for it and subtract its size instead of 16.

Related