Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C variable type assert

Tags:

c

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?

like image 907
alex Avatar asked Apr 23 '19 04:04

alex


People also ask

What is assert () in C?

In the C Programming Language, assert is a macro that is designed to be used like a function. It checks the value of an expression that we expect to be true under normal circumstances. If expression is a nonzero value, the assert macro does nothing.

Is there assert in C?

C library macro - assert() The C library macro void assert(int expression) allows diagnostic information to be written to the standard error file. In other words, it can be used to add diagnostics in your C program.

What is assert 0 C?

assert(0) or assert(false) is usually used to mark unreachable code, so that in debug mode a diagnostic message is emitted and the program is aborted when the supposedly unreachable is actually reached, which is a clear signal that the program isn't doing what we think it is.

What is typeof in C?

The typeof keyword is a new extension to the C language. The Oracle Developer Studio C compiler accepts constructs with typeof wherever a typedef name is accepted, including the following syntactic categories: Declarations. Parameter type lists and return types in a function declarator. Type definitions.


Video Answer


3 Answers

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;
  }
}
like image 125
Ryan Haining Avatar answered Oct 24 '22 01:10

Ryan Haining


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

like image 39
P.W Avatar answered Oct 24 '22 00:10

P.W


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 :-)

like image 1
Jens Avatar answered Oct 24 '22 00:10

Jens