alex
alex

Reputation: 267

C variable type assert

uint32_t fail_count = 0;

...

if(is_failed)
    if(fail_count < UINT32_MAX - 1 )
        ++fail_count;

It works fine, but this code is fragile. Tomorrow, I may change the type of fail_count from uint32_t to int32_t and I forget to update UINT32_MAX.

Is there any way to assert fail_count is a uint32_t at the function where I have written my ifs?

P.S. 1- I know it is easy in C++ but I'm looking for a C way.

P.S. 2- I prefer to use two asserts than relying on the compiler warnings. Checking the number size via sizeof should work but is there any way to distinguish if type is unsigned?

Upvotes: 18

Views: 3230

Answers (3)

Ryan Haining
Ryan Haining

Reputation: 36802

As of C11, you can use a generic selection macro to produce a result based on the type of an expression. You can use the result in a static assertion:

#define IS_UINT32(N) _Generic((N), \
  uint32_t: 1, \
  default: 0 \
)

int main(void) {
  uint32_t fail_count = 0;
  _Static_assert(IS_UINT32(fail_count), "wrong type for fail_count");
}

You could of course use the result in a regular assert(), but _Static_assert will fail at compile time.

A better approach could be dispatching the comparison based on type, again using generic selection:

#include <limits.h>
#include <stdint.h>

#define UNDER_LIMIT(N) ((N) < _Generic((N), \
int32_t: INT32_MAX, \
uint32_t: UINT32_MAX \
) -1)

int main(void) {
  int32_t fail_count = 0;

  if (UNDER_LIMIT(fail_count)) {
    ++fail_count;
  }
}

Upvotes: 24

Jens
Jens

Reputation: 72639

What about a low-tech solution that works even with K&R C and any compiler past and present?

Place the right comment in the right place:

/*
 * If this type is changed, don't forget to change the macro in
 * if (fail_count < UINT32_MAX - 1) below (or file foobar.c)
 */
uint32_t fail_count = 0;

With a proper encapsulation this should refer to exactly one place in the code. Don't tell me you increment the fail count in many places. And if you do, what about a

#define FAIL_COUNT_MAX  UINT32_MAX

right next to the declaration? That's more proper and clean code anyway. No need for all the assertion magic and rocket sciencery :-)

Upvotes: 1

P.W
P.W

Reputation: 26800

As you mentioned GCC, you can use a compiler extension to accomplish this in case you are not using C11:

First write a macro that emulates the C++ is_same. And then call it with the types you want to compare.

A minimal example for your particular case:

#include<assert.h>

#define is_same(a, b) \
  static_assert(__builtin_types_compatible_p(typeof(a), typeof(b)), #a " is not unsigned int")

int main()
{
    int fail_count = 0;    
    is_same(fail_count, unsigned int);
}

The compiler asserts:

<source>: In function 'main':
<source>:4:3: error: static assertion failed: "fail_count is not unsigned int"
   static_assert(__builtin_types_compatible_p(typeof(a), typeof(b)), #a " is not unsigned int")
   ^~~~~~~~~~~~~

<source>:9:5: note: in expansion of macro 'is_same'
     is_same(fail_count, unsigned int);
 ^~~~~~~

See Demo

Upvotes: 1

Related Questions