Reputation: 2363
The Problem
I am doing some firmware coding and have a lot of bitmasks to manage. Until now, I have been repeating the same block for each field I want to mask as follows:
#define LinkStateMask 0x1
#define LinkStateShift 8
#define LinkState(x) (((x) >> LinkStateShift) & LinkStateMask)
Note that the fields are quite often multi-bit fields so the mask is not always guaranteed to be 0x1
. The final macro can be used in the code quite readably and this is something I quite like:
if( LinkState(temp) == 0 ) {
// Link is down, return error
return -ENODEV;
}
The Question
Is there any way to have the C preprocessor generate these macros for me? I know that the preprocessor only works in a single pass so a macro cannot directly define another but hopefully there is another way.
Ideally, I would like to write something similar to the following and use the same C code as my second listing above:
BIT_FIELD(LinkState, 8, 0x1) // BIT_FIELD(name, lsb, mask)
The key for me is keeping the symbolically named, function-style usage in the main C file without having to write the whole BIT_FIELD(...)
invocation each time I need to generate a mask (some of these fields are used extensively, leading to a maintenance nightmare).
One final restriction: the fields I need are sparsely scattered across hundreds of registers. If at all possible, I would really like to avoid having to define a struct
for each as I usually only need one or two fields out of each.
Upvotes: 5
Views: 3640
Reputation: 4164
Another trick, which might be even nicer, is defining bit fields using a ternary operator.
These macros handle the interpretation :
// Extract starting and ending bit number from a bit_range,
// defined as the arguments for a ternary operator (low:high).
// Example : #define Least3Bits 0:2
#define BitRange(StartBit, BitCount) (StartBit):(StartBit+BitCount-1)
#define BitRangeStart(bit_range) (1?bit_range)
#define BitRangeEnd(bit_range) (0?bit_range)
#define BitRangeMask(bit_range) ((~0 << BitRangeStart(bit_range)) ^ (~0 << BitRangeEnd(bit_range)))
#define BitRangeShift(bit_range) BitRangeStart(bit_range)
#define BitRangeValue(bit_range, value) (((value) & BitRangeMask(bit_range)) >> BitRangeShift(bit_range))
Now your example can be declared like this :
#define LinkState_BitRange BitRange(8, 1)
#define LinkState(value) BitRangeValue(LinkState_BitRange, value)
And the other example :
#define Whatever_BitRange BitRange(9, 3)
#define Whatever(x) BitRangeValue(Whatever_BitRange, value)
Declaring the bit ranges incorrectly will lead to compile errors, so this is quite a safe method to use too.
Upvotes: 1
Reputation: 19504
Combining Jonathan's and Felix's answers with my own favorite preprocessor trick X-Macros yields the following.
#define Fields(_) \
_(Link, 0x1, 8) \
/*enddef Fields() */
#define HASH #
#define GenState(name, mask, shift) \
HASH define name ## State (((x) >> (shift)) & (mask))
Fields(GenState)
To which additional fields may be easily added to the table.
cpp -P
yields:
# define LinkState (((x) >> (8)) & (0x1))
The -P
option suppresses all the unneeded "information" directive lines in the output.
Upvotes: 0
Reputation: 753725
As Art suggested, one possibility is to use a macro to create an inline function:
#define BIT_FIELD(name, lsb, mask) \
static inline int name(int value) { return (value >> (lsb)) & (mask); }
The parentheses are needed for lsb
and mask
, in case someone gets fancy with the invocation.
You can then create functions as needed:
BIT_FIELD(LinkState, 8, 0x1)
and invoke them when needed:
int x = LinkState(y);
The macro definition and invocations can go in a header — though there's no harm if there are invocations local to a specific file.
Is there any major performance hit for doing this as a function versus doing this as a pre-computed value?
There's unlikely to be any performance hit because, for a function this simple, the compiler will inline all the code, exactly as if you'd used a macro, and with no function overhead. Yes, there's a chance that your compiler is broken (or doesn't support C99 or C11 — inline
was added to C99). It's tempting to say "get a better compiler". Test, measure, but there shouldn't be overhead.
If that's actually a problem, then you have to fall back on indirect alternatives. For example, you could create a file bitfields.hdr
containing:
#define BIT_HASH #
#define BIT_FIELD(name, lsb, mask) \
BIT_HASH define name(value) (((value) >> (lsb)) & (mask))
BIT_FIELD(LinkState, 8, 0x1)
You could then arrange to compile it into a header file:
cpp -E bitfield.hdr > bitfield.h
With the cpp
from GCC 5.1.0, I get the output:
# 1 "bitfield.hdr"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "bitfield.hdr"
# define LinkState(value) (((value) >> (8)) & (0x1))
You can then use #include "bitfield.h"
in the working code. This is a simple form of code generation. You could use an alternative macro processor if you preferred; m4
would be the obvious alternative, but you could use anything that takes your fancy (awk
, perl
, python
, ruby
— any of them could be used).
Or you can do as Felix Palmen suggests — that's better and simpler than precompiling as suggested. I'd be tempted to go with the inline functions as long as they work as expected, but Felix's suggestion is what to use if they don't.
Upvotes: 3
Reputation:
A macro cannot define another macro, but it can call another one (the preprocessor won't stop until there are no non-terminals left)
So, how about something like this? at least saves some typing ...
#include <stdio.h>
#define SHIFTMASK(x,s,m) (((x) >> (s)) & (m))
#define LinkState(x) SHIFTMASK(x, 8, 0x1)
#define Whatever(x) SHIFTMASK(x, 9, 0x7)
int test[] = { 0x0f00, 0x0a01, 0x0100, 0x0007 };
int main()
{
int i;
for (i = 0; i < 4; ++i)
{
printf("test[%d]: 0x%x\n", i, test[i]);
printf("LinkState(test[%d]): 0x%x\n", i, LinkState(test[i]));
printf("Whatever(test[%d]): 0x%x\n", i, Whatever(test[i]));
}
return 0;
}
Upvotes: 5