Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove cast from constant in preprocessor

Background

In a microcontroller code, I am using a library provided by the producer where there are many constants defined. I'm trying to give an error if there's a mismatch between some of my constants (shared with components outside the microcontroller, with git-subtree) and the microcontroller constants.

For example, the library defines:

#ifdef SOME_PARTICULAR_MODEL
#define FLASH_BLOCK_SIZE ((uint8_t)64)
/* else, other models */
#endif

And somewhere, in a header shared between the microcontroller code and some code compiled on the PC, I have for example:

#define MYPROG_BLOCK_SIZE 64

And to make sure these constants match, in the microcontroller code, where both constants exist, I have:

#if MYPROG_BLOCK_SIZE != FLASH_BLOCK_SIZE
#error "mismatch between actual block size and defined block size"
#endif

This is to make sure if the code is ever ported to a bigger microcontroller, the shared header would also be updated.

The Problem

The problem is that this gets reduced to:

#if 64 != ((uint8_t)64)

which I'm not sure if is valid C, but nonetheless makes the compiler choke up. Testing, I found out that the problem is not that uint8_t is a typedef and it still chokes up with a cast to int for example.

The Question

Is there a way to remove the (uint8_t) part from a value defined as ((uint8_t)64)? If not, is there any way to change it so the expression turns into one without a cast?

I thought about defining uint8_t as something and undefining it after the #if, but I can't figure out how I can avoid the cast nature of (Y)X and turn it into an arithmetic expression.

like image 321
Shahbaz Avatar asked Oct 16 '13 14:10

Shahbaz


People also ask

What does ## mean in preprocessor?

This is called token pasting or token concatenation. The ' ## ' preprocessing operator performs token pasting.

What is preprocessor constant?

The preprocessor defined operator is used in constant expressions to determine if an identifier is defined using #define. If the specified identifier is defined, the value is true (non-zero). If the symbol is not defined, the value is false (zero). The defined operator is specified as follows − #include <stdio.h> #if !

How do you define a string macro?

We can create two or more than two strings in macro, then simply write them one after another to convert them into a concatenated string. The syntax is like below: #define STR1 "str1" #define STR2 " str2" #define STR3 STR1 STR2 //it will concatenate str1 and str2. Input: Take two strings.

Which of the following options would you use to define preprocessor?

For ans [A]. #define is a preprocessor directive.


2 Answers

Here is an improved version (the first version is below). This one does not depend on the cast being uint8_t; it will work with any FLASH_BLOCK_SIZE replacement list of the form ((some type) number).

#define MYPROG_BLOCK_SIZE   64
#define FLASH_BLOCK_SIZE    ((uint8_t)64)

#define B(x)
#define C(x)    B x
#define D(x)    C x

#if MYPROG_BLOCK_SIZE != D(FLASH_BLOCK_SIZE)
    #error "mismatch between actual block size and defined block size"
#endif

Here is the original version:

#define MYPROG_BLOCK_SIZE  64
#define FLASH_BLOCK_SIZE   ((uint8_t)64)

#define uint8_t             
#define Helper(x)          x
#define Deparenthesize(x)  Helper x

#if MYPROG_BLOCK_SIZE != Deparenthesize(Deparenthesize(FLASH_BLOCK_SIZE))
    #error "mismatch between actual block size and defined block size"
#endif
#undef uint8_t

When writing code, I would prefer a static assert, but the above does what you requested in the preprocessor.

like image 157
Eric Postpischil Avatar answered Oct 07 '22 14:10

Eric Postpischil


The solution is to use static assert. With a good STATIC_ASSERT macro, you can put a static assert at file-scope in your header file:

STATIC_ASSERT(FLASH_BLOCK_SIZE == MYPROG_BLOCK_SIZE);

Here is an exemple of definition for the STATIC_ASSERT macro:

#define CAT(x, y) CAT_(x, y)
#define CAT_(x, y) x ## y

#define STATIC_ASSERT(expr)  \
    extern int CAT(static_assert_failed_, __LINE__)[(expr) ? 1 : -1]

With some compilers (e.g., IAR), you have static_assert as a compiler builtin. static_assert is also present in C11 but unfortunately not a lot of embeddded C compiler support C11.

like image 32
ouah Avatar answered Oct 07 '22 14:10

ouah