Reputation: 643
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
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
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
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