Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

compile-time struct size check, error out if odd

Tags:

c

Is there any possible way to make the compiler bail out if the sizeof (struct Astruct) is uneven?

Background information: We have a 16-bit microprocessor which will give processor alignment errors if a 16-bit value is mis-aligned. That might happen in the following scenario:

typedef struct
{
    U8BIT u8BitValue1;
    U8BIT u8BitValue2;
    U8BIT u8BitValue3;
} unevenAmountOf8BitValues;

typedef struct
{
    U16BIT u16BitValue1;
    U16BIT u16BitValue2;
} my16BitValues;

#define U8BIT_COUNT 3
#define U16BIT_COUNT 2

typedef struct
{
    unevenAmountOf8BitValues u8BitValues;
    my16BitValues u16BitValues;
} valuesCombined;

typedef union
{
    valuesCombined myValues;
    U8BIT buffer[sizeof(valuesCombined)];

    struct
    {
         U8BIT bufferU8[U8BIT_COUNT];
         U16BIT bufferU16[U16BIT_COUNT]; /* <<-- missalignment */
    } valuesPerType;
} myValuesInRamAndRom

What we do now is counting the amount of U8BIT/U16BIT/U32BIT values (well, keeping track of the amount using excel) manually and putting that in the U(8/16/32)BIT_COUNT define and then the following:

#if U8BIT_COUNT % 2 == 1
#error The number of U8BIT parameters need to be even, add a dummy
#endif

Keeping track of the amount of U8-/U16-/U32BIT values is pretty error prone and we've had quite some moments that we were thinking "hey, it ain't working", an hour or what later, oh! Darn, forgot to adjust the amount of values define.

A preferred method would be to use the sizeof operator, however that can't be used in the error checking, which I would really like to keep.

So is there anyway to use the sizeof operator and to keep some form of error checking that the amount of U8BIT values must be even?


Combined solution by Lundin and Aaron McDaid:

#define COMPILE_TIME_ASSERT(expr) {typedef U8BIT COMP_TIME_ASSERT[((!!(expr))*2-1)];}
like image 945
Daan Timmer Avatar asked Oct 16 '13 12:10

Daan Timmer


3 Answers

With a C11 compiler, use:

static_assert (sizeof(the struct) % 2 == 0,
               "Misaligned");

With older compilers, you can use dirty tricks like

#define COMPILE_TIME_ASSERT(expr) typedef char COMP_TIME_ASSERT[(expr) ? 1 : 0];

...

COMPILE_TIME_ASSERT(sizeof(the_struct) % 2 == 0);

The real solution to your specific problem might however be to ensure that struct padding is enabled. You shouldn't get any misalignments then.

like image 195
Lundin Avatar answered Nov 20 '22 10:11

Lundin


It's possible, using a trick that's also being used in the Linux kernel:

#define BUILD_BUG_OR_ZERO(e) (sizeof(struct{ int:-!!(e);}))
#define ENSURE_EVEN_SIZE(e) BUILD_BUG_OR_ZERO(sizeof(e) % 2 == 1)

struct uneven{
  char a,b,c;
};

struct even{
  char a,b,c,d;
};

int main(){
  ENSURE_EVEN_SIZE(struct even);
  /* compiler error: */
  ENSURE_EVEN_SIZE(struct uneven);
}

If sizeof(e) % 2 == 1 is true, the bitfield int:-!!(e) would have a negative size, which is forbidden. (Ideone)

like image 40
Zeta Avatar answered Nov 20 '22 11:11

Zeta


Here is the version which allows using same assertion macro multiple times in the same file.

/*
    General purpose static assert.

    Works in/out -side of scope:
        STATIC_ASSERT(sizeof(long)==8);
        int main()
        {
            STATIC_ASSERT(sizeof(int)==4);
        }
*/
#define STATIC_ASSERT(X)            STATIC_ASSERT2(X,__LINE__)

/*
    These macros are required by STATIC_ASSERT to make token pasting work.
    Not really useful by themselves.
*/
#define STATIC_ASSERT2(X,L)         STATIC_ASSERT3(X,L)
#define STATIC_ASSERT3(X,L)         STATIC_ASSERT_MSG(X,at_line_##L)

/*
    Static assertion with special error message.
    Note: It depends on compiler whether message is visible or not!

    STATIC_ASSERT_MSG(sizeof(long)==8, long_is_not_eight_bytes);
*/
#define STATIC_ASSERT_MSG(COND,MSG) \
    typedef char static_assertion_##MSG[(!!(COND))*2-1]
like image 5
user694733 Avatar answered Nov 20 '22 10:11

user694733