josaphatv
josaphatv

Reputation: 643

Is it sane to include casts in #defined constants based on how they're intended to be used?

Say I have a bunch of un-magicked constants that have been given symbolic names in the header file of a gyroscope module gyro.h:

#define GYRO_SPI_TIMEOUT 1000

In this instance, the hardware abstraction libraries for my MCU use uint32_t for the number of milliseconds. Is it sensible to, in lieu of allocating space and defining these as const, add type information in the macro?

#define GYRO_SPI_TIMEOUT ((uint32_t) 1000)

What are the benefits and drawbacks of this approach?

Upvotes: 9

Views: 168

Answers (3)

zwol
zwol

Reputation: 140748

You might want to consider using the [U]INT<n>_C macros from stdint.h instead (you may need to scroll down to where it says "Macros for Integer Constant Expressions").

The main advantage of these macros, over casts, is that constants defined with them are usable in more contexts. For instance, ((uint32_t)1234) cannot appear in an #if expression, but UINT32_C(1234) can.

Note that most C compilers will not complain about assignments of a constant expression that involve a narrowing conversion, unless the conversion actually changes the value. For instance, given

short a = 1234UL;
short b = ((unsigned long)1234);
short c = 65537UL;
short d = ((unsigned long)65537);

gcc 4.9 and clang 3.5 issue diagnostics about the conversion only for c and d, even when I crank the warnings up as high as they will go.

Upvotes: 3

mafso
mafso

Reputation: 5543

I'd consider this a good thing to do, especially ensuring the correct signedness (for this purpose, 1000u would be enough, though). Arithmetic types aren't strongly typed in C, so most code wouldn't change semantics if the macro expansion has the correct signedness and is promoted (that is, not smaller than int).

There are static analysers and options for compilers, however, which do some more type checking and disallow certain implicit conversions, where it would be nice if your constants behaved as if they were variables for most purposes for the code using your header.

Upvotes: 3

user3079266
user3079266

Reputation:

Yes, this is often done. In fact, the infamous NULL pointer is often defined like this:

#define NULL ((void*)0)

Advantages: your macro is now typesafe, whatever the global constant proponents say.

Disadvantages: if you will want to use it in an un-typesafe way, say, like this:

short a = GYRO_SPI_TIMEOUT;

You'd need a cast. However, if you're not intending for such usages to ever happen, this can be a good thing.

Upvotes: 0

Related Questions