Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC Preproccesor macro to determine maximum size of multiple structs

Is there a way to generate a macro which yields the max size of a list of data types?

Goal

typedef struct {
    uint8_t x;
} A;

typedef struct {
    uint16_t x;
} B;

typedef struct {
    uint8_t x[10];
} C;

#define sizeof_max(A,B,C)   //compiles to '10'

Use Case

Different value-mappings for a common data-segment.

typedef union {
    uint8_t array[sizeof_max(A,B,C)];
    A iso_hugenum_a;
    B ext_a0;
    C ext_a1;  //and someday down the road 'D' may also get added
} DATA_SEG;

This is for an embedded application where the device implements a base protocol ISO_HUGENUM_A. The device must also support extensions to this protocol EXT_A0, EXT_A1, EXT_B0. Heck, in this case there is a real chance of EXT_C0 showing up down the road (yuck)!

Notes

The primary goal here is for a top-level system to know the data-segment's size in an extensible and safe way. It is tempting to just say 'cast as array' when you need as array. But

  • At the system-level (who doesn't give an eff about the protocol) there are read, write and checks (e.g. crc) to this data segment

  • 2yrs down the road 'EXT_C0' may come along. I want to leave the poor soul who inherits my code with something that won't break when EXT_C0 grows the data segment

I am hoping there is a solution, but have not found one yet. Any ideas? All the sizes would be generated by the pre-processor, so it seems like an ideal candidate for a macro.

-Justin

like image 565
J-Dizzle Avatar asked Oct 26 '25 04:10

J-Dizzle


1 Answers

The following macro definition does precisely what you have asked:

#define sizeof_max(s1,s2,s3) sizeof( union{s1 a; s2 b; s3 c; })

For your example structures the following:

size_t s = sizeof_max( A,B,C ) ;

results in s = 10.

Of course you could omit the array member and simply cast a DATA_SEG object address to a uint8_t* when you want to access as a byte array:

DATA_SEG x ;
uint8_t* array = (uint8_t*)&x ; 

That would allow DATA_SEG to add more structures if necessary without having to change the macro - a lot safer and more maintainable.


Added:

Another possibility is to separate the specialised interpretations from the byte overlay thus:

typedef union 
{
    A iso_hugenum_a;
    B ext_a0;
    C ext_a1;  
    D added_someday ;
} DATA_SEG_U;

typedef union 
{
    uint8_t array[sizeof(DATA_SEG_U)];
    DATA_SEG_U data_seg ;
} DATA_SEG ;
like image 82
Clifford Avatar answered Oct 28 '25 21:10

Clifford