Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

For any typedef you can get arrount this by changing to

#define FLASH_BLOCK_SIZE ((uint8_t)+64)

notice the little plus in there?

The preprocessor replaces the typename it doesn't know anything about by 0, so this works out in both contexts. (The preprocessor is supposed to do all arithmetic in [u]intmax_t, anyhow)

For the actual type you are using all of this makes not much sense. There is no such thing as a uint8_t constant. As soon they are evaluated all expressions of a type that is narrower than int are converted to int. So it probably doesn't makes any difference.

After macro replacement, a preprocessor expression may have subexpressions of the form defined identifier or defined ( identifier ). Other than that, identifiers and keywords have no meaning; each identifier or keyword is replaced by 0. So this:

#if 64 != ((uint8_t)64)

is equivalent to this:

#if 64 != ((0)64)

which is a syntax error. The fact that it's a typedef isn't the problem; a keyword like int gets the same treatment.

So given:

#define FLASH_BLOCK_SIZE ((uint8_t)64)

you can't use FLASH_BLOCK_SIZE in a preprocessor expression.

The best solution I can think of is to change your definition:

#define FLASH_BLOCK_SIZE_NUM 64
#define FLASH_BLOCK_SIZE ((uint8_t)FLASH_BLOCK_SIZE_NUM)

You can then use FLASH_BLOCK_SIZE_NUM in preprocessor expressions and FLASH_BLOCK_SIZE elsewhere.

On the other hand, do you really need the (int8_t) cast in the first place? Arithmetic expressions are implicitly converted in many contexts, usually to the appropriate type. In many contexts, (uint8_t) will be promoted to int anyway. It's very likely you can just drop the cast and use:

#define FLASH_BLOCK_SIZE 64

To be sure, you'll need to examine all the code that refers to FLASH_BLOCK_SIZE. (Evaluating sizeof FLASH_BLOCK_SIZE would be a problem, but I'm betting you never do that.)

I know the issue thread is old, however I encountered it today... If you do not want to or cannot change the #define, the other answers are great, but if you have access to the #define, I recommend using for pre-processor operations (#if - #else - #endif) instead of:

#define FLASH_BLOCK_SIZE ((uint8_t)64)

use this define:

#define FLASH_BLOCK_SIZE (64U)

This way the cast is still there, and the compiler won't be "confused".

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top