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?

Fritz
  • 1,293
  • 15
  • 27
  • 2
    You shouldn't use names that start with underscore, since they are [reserved](http://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html). – e0k Jan 31 '16 at 23:24
  • @e0k You are proably right. I called it `_BITS` to be consistent with the `_BV` macro from the avr-libc and because it would be in my own "private library". – Fritz Jan 31 '16 at 23:28
  • 1
    `(1 << bit) ` -->> `(1u << bit)` . It helps. Sometimes. – wildplasser Jan 31 '16 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? – Jonathan Leffler Jan 31 '16 at 23:44
  • 1
    This avoids the question, but personally I think it's easier to read if it's explicit `(1< – e0k Jan 31 '16 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). – too honest for this site Feb 01 '16 at 00:16
  • Agree with @Olaf. How about `#define BM(n) (1u << (n)) TCCR0A |= BM(WGM01) | BM(WGM00) | BM(COM0A1);`? – chux - Reinstate Monica Feb 01 '16 at 03:51
  • @JonathanLeffler The point is that the constants `WGM01` etc. are defined in the architecture-specific header files of the micro controller I am using. I *could* define the enums you suggest, but that would mean doing that for the entire avr-libc. – Fritz Feb 01 '16 at 09:11
  • @chux That macro is already defined as `_BV(WGM01)` in avr-libc. But in my humble opinion it serves neither readability nor is it easier to type. – Fritz Feb 01 '16 at 09:12
  • @chux: I actually proposed not to use a function-macro at all. Just constants (e.g. an `enum`) with the bitmasks. Although using the bit-numbers and a macro to shift was also ok. But then the mask should be appropriate for the target register, e.g. `UINT32_C`. – too honest for this site Feb 01 '16 at 14:45
  • @Olaf Show me a micro-controller that defines various macro constants that would require a integer wider than `unsigned`. – chux - Reinstate Monica Feb 01 '16 at 15:27
  • @chux: I know some ARMv7R MCUs with 64 bit peripheral registers. Don't you agree that would require `uint64_t`? And even for 32 bit peripherals, it is better to use `uintN_t` for documentation purposes. Which is exactrly what most headers use one way (`stdint.h`) or the other (since 17 years obsolete homebrew `typedef`s or macros). – too honest for this site Feb 01 '16 at 15:57

4 Answers4

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;
}
Community
  • 1
  • 1
Fritz
  • 1,293
  • 15
  • 27
  • Wow, that is insane. – 2501 Feb 02 '16 at 20:31
  • @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. – Fritz Feb 02 '16 at 22:09
  • 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 Feb 02 '16 at 22:57
  • @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). – Fritz Feb 02 '16 at 23:22
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.

Rob11311
  • 1,396
  • 8
  • 10
  • 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. – Fritz Feb 01 '16 at 13:50
  • 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. – Rob11311 Feb 01 '16 at 17:02
  • Okay, it is possible to create a terminating condition for the macro recursion. See my answer. – Fritz Feb 02 '16 at 22:05
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)
2501
  • 25,460
  • 4
  • 47
  • 87
  • 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 Feb 01 '16 at 09:18
  • @Fritz No you would write WGM01_B which would be defined as (1< – 2501 Feb 01 '16 at 09:56
  • 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< – Fritz Feb 01 '16 at 11:37
  • Also, your version of the BITS macro seems kinda pointless, right? It does nothing and adds little semantics. – Fritz Feb 01 '16 at 11:39
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)));
}
notinaboat
  • 61
  • 1
  • 2
  • 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))`. – Fritz Aug 29 '21 at 09:12