3

I'm trying to write a macro which simplifies setting multiple bits in an integer. This commonly occurs in microcontroller code when initializing configuration registers. For example, one might configure an 8-bit timer by setting 3 bits in the register TCCR0A like this:

// WGM01, WGM00 and COM0A1 are constants between 0 and 7 // There are hundreds of these constants defined in avr-libc TCCR0A |= (1<<WGM01) | (1<<WGM00) | (1<<COM0A1); // Another way to write this: #define _BV(bit) (1 << (bit)) // <-- defined in avr-libc TCCR0A |= _BV(WGM01) | _BV(WGM00) | _BV(COM0A1); 

However, I'd find it a lot easier to write something like this:

TCCR0A |= BITS(WGM01, WGM00, COM0A1); // <- Variable # of arguments please! 

Since I can't imagine that nobody has thought of this yet, I searched around but found nothing which does exactly this. I wonder if this is possible at all, but I gave it a shot anyways while reading https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html and https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms.


Here's what I tried so far. I imagine the solution must be recursive macro, but didn't get very far when trying to get it to expand correctly. Since all my registers are 8 bits long, 8 expansion passes should be sufficient (for a first try).

#define BITS_EVAL(...) BITS_EVAL1(BITS_EVAL1(BITS_EVAL1(__VA_ARGS__))) #define BITS_EVAL1(...) BITS_EVAL2(BITS_EVAL2(BITS_EVAL2(__VA_ARGS__))) #define BITS_EVAL2(...) __VA_ARGS__ #define BITS(bit, ...) ((1 << bit) | BITS_EVAL(BITS(__VA_ARGS__))) 

The above doesn't quite work. What it currently does is:

// BITS(2,5,7) --> ((1 << 2) | BITS(5, 7)) 

However, what I would like to achieve is one of these (or equivalent):

// BITS(2,5,7) --> ((1 << 2) | (1 << 5) | (1 << 7)) // BITS(2,5,7) --> ((1 << 2) | ((1 << 5) | ((1 << 7)))) 

Can anyone help me with my quest, or tell me that it's impossible to achieve this?

12
  • 2
    You shouldn't use names that start with underscore, since they are reserved. Commented Jan 31, 2016 at 23:24
  • 1
    (1 << bit) -->> (1u << bit) . It helps. Sometimes. Commented Jan 31, 2016 at 23:30
  • 1
    You could use enum { BM_WGM01 = 1 << WGM01, BM_WGM00 = 1 << WGM00, BM_COM0A1 = 1 << COM0A1 … }; or a more formally typed mechanism equivalent to that (one that specifies the size of the values, probably ensuring they are unsigend), and then write: TCCR0A |= BM_WGM01 | BM_WGM00 | BM_COM0A1;, couldn't you? Commented Jan 31, 2016 at 23:44
  • 1
    This avoids the question, but personally I think it's easier to read if it's explicit (1<<a) | (1<<b) bitwise arithmetic rather than fancy macros that you have to find and understand before reading the actual code. Also, macros can be overridden. Commented Jan 31, 2016 at 23:50
  • 1
    The standard way is to use macros for the bits and bit-or them: a |= BIT1 | BIT2 ...; Don't use macros unless they enhance code quality (which includes readability). Commented Feb 1, 2016 at 0:16

4 Answers 4

7

Warning: Writing this was mostly a learning exercise.

DO NOT USE IN PRODUCTION CODE. People will rightly curse at you if you do.

So, after playing around a bit more with the macros from Paul's answers and github wiki, I actually managed to produce a working BITS(...) macro which does what I intended. It is a recursive macro that is scanned multiple times to expand the recursive replacements. It handles a variable number of arguments and supports integers up to 64 bits.

// test.c #include "bits.h" int a = BITS(1,5,7); int b = BITS(3); int c = BITS(); // This case is broken but irrelevant 

Using gcc -E test.c -o test.txt this expands to:

int a = (0 | (1ull<<1) | (1ull<<5) | (1ull<<7)); int b = (0 | (1ull<<3)); int c = (0 | (1ull<<)); // This case is broken but irrelevant 

The 0 | at the beginning is an artifact of the implementation but obviously does not affect the result of the expression.


Here's the actual implementation including comments:

// bits.h // Macros partially from https://github.com/pfultz2/Cloak #define EMPTY(...) // Defers expansion of the argument by 1, 2 or 3 scans #define DEFER(...) __VA_ARGS__ EMPTY() #define DEFER2(...) __VA_ARGS__ DEFER(EMPTY)() #define DEFER3(...) __VA_ARGS__ DEFER2(EMPTY)() // Concatenate the arguments to one token #define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__ // Apply multiple scans to the argument expression (>64 to allow uint64_t masks) #define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) #define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) #define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) #define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) #define EVAL4(...) __VA_ARGS__ // Always expand to the second token after expansion of arguments. // One level of indirection to expand arguments before selecting. #define SELECT_2ND(...) SELECT_2ND_INDIRECT(__VA_ARGS__, , ) #define SELECT_2ND_INDIRECT(x1, x2, ...) x2 // Expands to a comma (which means two empty tokens in a parameter list). // Thus, SELECT_2ND will expand to an empty token if this is the first argument. #define BITS_RECURSIVE__END_RECURSION , // Adds the END_RECURSION parameter, which marks the end of the arguments #define BITS(...) \ (0 EVAL(BITS_RECURSIVE(__VA_ARGS__, END_RECURSION,))) // When hitting END_RECURSION, the CAT will expand to "," and SELECT_2ND // will select the empty argument instead of the recursive call. #define BITS_RECURSIVE(bit, ...) \ SELECT_2ND(PRIMITIVE_CAT(BITS_RECURSIVE__, bit), \ | (1ull<<(bit)) DEFER3(BITS_INDIRECT)()(__VA_ARGS__)) // Needed to circumvent disabling contexts for recursive expansion #define BITS_INDIRECT() BITS_RECURSIVE 

And some code to test the extreme cases:

// test2.c #include "bits.h" #include <inttypes.h> #include <stdio.h> uint8_t u8 = BITS(0,1,2,3,4,5,6,7); uint32_t u32 = BITS(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31); uint64_t u64 = BITS(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63); uint64_t a64 = BITS(0,1,2,3,4,5,6,7, 16,17,18,19,20,21,22,23, 32,33,34,35,36,37,38,39, 48,49,50,51,52,53,54,55); int main(void) { printf("0x%02" PRIX8 "\n", u8); // Prints 0xFF printf("0x%08" PRIX32 "\n", u32); // Prints 0xFFFFFFFF printf("0x%016" PRIX64 "\n", u64); // Prints 0xFFFFFFFFFFFFFFFF printf("0x%016" PRIX64 "\n", a64); // Prints 0x00FF00FF00FF00FF return 0; } 
Sign up to request clarification or add additional context in comments.

4 Comments

Wow, that is insane.
@2501 Don't get me wrong by the way. I totally agree that your solution is the better one in principle. However, in practice these bitmask constants are not defined in avr-libc, and manually defining them all is not really feasible. Thus this hackery.
Exactly insane!! It's not so hard to write a small C program to write the munged definitions that you actually use, for you from the include files. I've actually got 1 program which calculated a table that it uses itself, which was writen out as C source, since is normally "just" included. What on earth is anyone going to do, if this breaks at some point?
@Rob11311 You are right of course, I wouldn't recommend this to anyone in a production environment either. It was a fun learning exercise though (that was the motivation, in fact).
2

The GCC variadic macro feature, intends to pass the variable argument list at runtime, to a function analogous to printf, say setBits( 3, WGM01, WGM00, COM0A1) to OR n values together. I doubt evaluating this at runtime is acceptable.

If you really want to write BITS as above, I think you could use the macro-processor m4, which allows a recursive definition with shifting of argument list, so you can test $# is 1 or >= 2. The expansions would operate something like :

1 TCCR0A |= BITS( WGM00 , COM0A1 , WGM01 ); 2 TCCR0A |= (1u << WGM00) | BITS( COM0A1 , WGM01 ); # (1u << $1) | BITS( shift($*)) 3 TCCR0A |= (1u << WGM00) | (1u << COM0A1) | BITS( WGM01); # Recusion terminates 4 TCCR0A |= (1u << WGM00) | (1u << COM0A1) | (1u << WGM01); 

Somehow I would not expect thanks for including something like this in C source :

define(`BITS',`ifelse(eval($#<2),1, (1u<<`$1`''), (1u<<`$1`'') | BITS(shift($@))')') 

In the CPP tricks, apparently a first argument could be seperated, but recursive macro's are not supported, nor is there a way of evaluating a termination condition. Perhaps a chain of expansions would be feasible and clear enough :

#define BITS4(m, ...) ((1u<<m) | BITS3(__VA_ARGS__)) #define BITS3(m, ...) ((1u<<m) | BITS2(__VA_ARGS__)) #define BITS2(m, ...) ((1u<<m) | BITS1(__VA_ARGS__)) #define BITS1(m) ( 1u << m) 

Testing this :

printf( "BITS3( 0, 1, 2) %u\n", BITS3( 0,1, 2)); printf( "BITS2( 0, 1) %u\n", BITS2( 0,1)); printf( "BITS1( 0) %u\n", BITS1( 0)); 

Results : BITS3( 0, 1, 2) 7 BITS2( 0, 1) 3 BITS1( 0) 1

Which is those expected. Whilst this is not the general bit set macro hoped for, the solution has clarity so ought be maintainable.

3 Comments

As far as I understand it, your first sentence is not quite correct. The variadic macro arguments ... are just inserted (comma-separated) into the expanded macro where you write __VA_ARGS__. The fact that you can pass these to variadic functions like printf doesn't mean that you have to. Take a look at the second link in the question, there you find multiple examples of variadic macros, none of which are evaluated at runtime. The entire point of that article is to evaluate everything at preprocessing time.
Point taken. But those CPP "tricks", don't show how to write BITS( m1, m2, .. , mn), there's not a Haskel style pattern match in CPP to choose a rule for further expansion by matching according to paramter list.
Okay, it is possible to create a terminating condition for the macro recursion. See my answer.
1

Define you bit macros as actual bits that they set, no magic number hardcoding required, becase if the macros change so do their _B counterparts:

#define WGM00_B (1u<<WGM00) #define COM0A1_B (1u<<COM0A1) #define WGM01_B (1u<<WGM01) ... 

Then simply or them together, no macro needed and the order is irrelevant:

TCCR0A |= WGM00_B | COM0A1_B | WGM01_B; 

Or put it in a macro. The usage is just like you asked, but instead of commas, you use bitwise or operator.

TCCR0A |= BITS( WGM00_B | COM0A1_B | WGM01_B ); 

Macro BITS is defined simply as:

#defined BITS( b ) (b) 

4 Comments

This doesn't really answer the question, because I wasn't asking for best practices but for help with writing a specific macro. Also, it defeats the point of constants like WGM01 (which stands for "Waveform Generation Mode of TImer 0, bit 1"). When I write 1 << WGM01, I don't have to care about the value of WGM01, which is defined in an achitecture-specific header. With your solution I'd essentially hard-code it (same as writing 1 << 6, which is bad because 6 is a "magic number" there).
@Fritz No you would write WGM01_B which would be defined as (1<<WGM01). That way you don't have to care about the value of WGM01.
Thanks. However, the main problem with your approach is that there are several hundred of these predefined constants. Manually going through all header files and defining several hundred additional constants somehow defeats the purpose. In that case it would be much easier to just write 1<<WGM01 etc. That's why I'm looking to write a single macro that does this for me.
Also, your version of the BITS macro seems kinda pointless, right? It does nothing and adds little semantics.
1

I couldn't find a good answer for this a few years later, so I came up with this...

  • A _bits macro that takes 8 bit-position arguments (and accepts but ignores any number of additional ... arguments).
  • A bits(...) macro that passes its arguments to _bits along with 8 x trailing 8s.
  • The 8s result in (uint8_t)(1 << 8) == 0 (or are ignored if they fall into the ... part of the argument list of _bits).

Note 1. The ignored argument x is there to support the empty args case, bits() == 0, which seems nice for consistency. e.g. you might have empty #define OPTION_NO_FOO_DISABLED and want bits(OPTION_NO_FOO) == 0

Note 2. Only the first 8 arguments are used. e.g. bits(1,1,1,1,1,1,1,1,2) == 1. -- thx Fritz

#define _bits(x, i, j, k, l, m, n, o, p, ...) \ ((uint8_t)( (uint8_t)(1U << (i)) | \ (uint8_t)(1U << (j)) | \ (uint8_t)(1U << (k)) | \ (uint8_t)(1U << (l)) | \ (uint8_t)(1U << (m)) | \ (uint8_t)(1U << (n)) | \ (uint8_t)(1U << (o)) | \ (uint8_t)(1U << (p)) ) ) #define bits(...) _bits(x, ## __VA_ARGS__, 8, 8, 8, 8, 8, 8, 8, 8) int main() { assert(bits() == 0); assert(bits(8) == 0); assert(bits(7) == (uint8_t)((1 << 7))); assert(bits(0) == (uint8_t)((1 << 0))); assert(bits(0, 8) == (uint8_t)((1 << 0) | (1 << 8))); assert(bits(8, 0) == (uint8_t)((1 << 8) | (1 << 0))); assert(bits(1,2,3) == (uint8_t)((1 << 1) | (1 << 2) | (1 << 3))); assert(bits(7,7,7) == (uint8_t)((1 << 7) | (1 << 7) | (1 << 7))); assert(bits(6,2,3,4,0,7,5,1) == (uint8_t)((1 << 6) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 0) | (1 << 7) | (1 << 5) | (1 << 1))); assert(bits(6,2,3,4,0,7,5,1,2,2,2) == (uint8_t)((1 << 6) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 0) | (1 << 7) | (1 << 5) | (1 << 1))); } 

1 Comment

That is actually pretty clever and concise, even though it silently fails in cases where the user passes more than 8 relevant bits: bits(1,1,1,1,1,1,1,1,2) == 3 fails. So not fit for production in this stage. Also, you must parenthize the arguments as in: (1U << (k)).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.