Setting a flag in C as elegantly as in assemby language
Asked Answered
C

6

5

Flags handling in C feels cumbersome, compared to assembly.

I am looking for a way to make the C code as readable as assembly.

In Assembly:

#define powerOn flagsByte,0
... 
bsf powerOn ; Turn on the power
bcf powerOn ; Turn off the power
btfsc powerOn ; If the power is on...

In C:

flagsByte |= (1 << 0) ; // Turn on the power
flagsByte &= ~(1 << 0) ; // Turn off the power
if (flagsByte & (1 << 0)); // If the power is on...

In C, with a macro:

#define BIT_SET(var,bitNo) (var |= (1<<(bitNo)))
BIT_SET(flagsByte,0) ; // Turn on the power

That works, but it's still not as clean as in assembly.

I'd love to do:

#define powerOn flagsByte,0
BIT_SET(powerOn) ; // Turn on the power

But that doesn't work, because it expands to:

flagsByte,0 |= (1<<())

instead of:

flagsByte |= (1<<(0))

Question:

Is there an elegant way in C to set, clear or test a flag that is defined as follows?

#define powerOn flagsByte,0
Carnal answered 18/4, 2014 at 23:28 Comment(2)
I'm not sure hiding the target variable or register for "elegance" scales when there are hundreds of registers exposed by the CPU and peripherals. E.g., consider a reset bit. Is that reset for the wifi chip? The AES encryption engine? The tx data path? The rx data path? The DCM clock? The serial port? The watchdog? The CPU? And so on.Ventage
I believe that CLR(serRxInterFlg ) (with a #define serRxInterFlg PIF1,U1RxIF, somewhere else) is far more readable than PIF1 &= !(1<<U1RxIF). I would not call a flag simply "reset".Carnal
A
4

Personally, I prefer the bit-field syntax, and without macros since my flags are almost always inside structs anyway. However, if you insist on writing assembler in C, here's how:

/* We need to indirect the macro call so that the pair of arguments get expanded */
#define BITSET_(f,i) do{f|= 1<<(i);}while(0)
#define BITCLR_(f,i) do{f&=~(1<<(i));}while(0)
#define BITCHK_(f,i) ((f)&(1<<(i)))

#define BITSET(fi) BITSET_(fi)
#define BITCLR(fi) BITCLR_(fi)
#define BITCHK(fi) BITCHK_(fi)

/* Define the flag container and bit number as per OP */
#define poweron flags1,0
#define warnuser flags7,4

/* Sample uses */
BITSET(poweron);
BITCLR(warnuser);
/* Since BITCHK expands to a parenthesized expression, I can get away with
 * leaving out the parentheses in the if statement. Not saying that's a good
 * idea or anything.
 */
if BITCHK(poweron) BITSET(warnuser);

If you have gcc, you can verify this with gcc -E flag_macros.c

Approximal answered 19/4, 2014 at 0:1 Comment(3)
Great! That will do it. Your approach appears to be the same as the one from jthill, right? That is, the trick is to do two macro expansions.Carnal
@DavideAndrea: Apparently so. I didn't see his answer.Approximal
Well, @rici, yours came first, so I flagged it as the accepted solution.Carnal
M
3

Here's a set of macros closely matching your assembly example:

#define powerOn        0
#define someotherfield 1

#define BITMASK(field) (1u << (field))
#define SET(field)  do { flagsByte |= BITMASK(field); } while(0)
#define CLR(field)  do { flagsByte &= ~BITMASK(field); } while(0)
#define TEST(field) (flagsByte & BITMASK(field))

/* Use examples */
SET(powerOn);
CLEAR(powerOn);
if (TEST(powerOn)) {
    // Danger!
}

Here's a variant that allows you to include the variable in the particular field definition. It's a bit tricky as it involves argument prescan

#define powerOn        flagsByte,0                                                                                                                                                      
#define someotherfield flagsByte,1                                                                                                                                                      

#define BITMASK(field) (1u << (field))                                                                                                                                                  
#define _SET(var, field)  do { var |= BITMASK(field); } while(0)                                                                                                                        
#define SET(x) _SET(x)

/* Use examples */                                                                                                                                                                      
SET(powerOn);
Manicurist answered 18/4, 2014 at 23:53 Comment(4)
With your approach, I would need 3 separate macros for every variable whose bits I want to manipulate. That includes all sorts of Special Function Registers, in addition to various bytes of flags. That would be dozens of macros. I would not look forward to that.Carnal
I do like your "BITMASK(field) (1u << (field))" macro, though. I think I'll use it. Thanks.Carnal
I've updated my answer with a variant of the macro that allows using variable name in the field definition. It's borders on preprocessor abuse, though, IMHO.Manicurist
I checked your update, and, yes, it now does answer my question completely; thanks. I see that you're using the same solution that jthill and rici gave: 2 macro expansions.Carnal
W
2

You could #define powerOn flagsByte |= (1 << 0); and then just use it like a statement. As in

// do stuff...
powerOn; // Turn power on.
// do stuff...
Wholehearted answered 18/4, 2014 at 23:35 Comment(2)
Interesting. Thanks. But I have dozens of flags, and I can't really imagine writing dozens of macros, three macros (one to set it, one to clear it, one to test it) for each flag.Carnal
Ah, I didn't know. ArtemB seems to have an elegant solution that would work for that.Wholehearted
W
2

With GCC you can define so-called bit fields and manipulate them like struct members:

struct flagsByte
  {
    unsigned int powerOn: 1;  /* single bit */
  };

flagsByte.powerOn = 0;
flagsByte.powerOn = 1;

Building upon this, it is possibile to define a couple of trivial macros, reminiscent of Assembly:

#define bsf(X) flagsByte.(X) = 1
#define bcf(X) flagsByte.(X) = 0

and simply write

bsf(powerOn);  /* set flag   */
bcf(powerOn);  /* clear flag */

Unfortunately, this is not applicable to every C compiler.

Whitford answered 18/4, 2014 at 23:38 Comment(5)
Thank you. It doesn't quite answer my question, which assumes #define powerOn flagsByte,0. In your solution, "flagsByte.powerOn" does include the word "flagsByte", which is less elegant than assembly. Still, very nice, thank you.Carnal
bit fields are not a gcc extension. They are part of standard C, so they should be available on any conformant compiler.Approximal
"#define bsf(X) flagsByte.(X) = 1" means that I need a separate macro for every variable whose bits I want to manipulate. That includes all sorts of Special Function Registers, in addition to various bytes of flags. I would not look forward to that.Carnal
"struct flagsByte" would work for flag bytes, which I would define as I need them. But would not work for Special Function Registers, which are already defined by the processor files included in the project.Carnal
If you want to set many bits in many registers (as you said in a comment on ArtemB's answer), you will inevitably have to refer to both the register/variable and the bit you want to set.Wholehearted
R
2

You do this with a second expansion.

~/sandbox/20$ cat >t.c
#define BITSET_INNER(a,b) a |= (1<<b)
#define BITSET(spec) BITSET_INNER(spec)
#define f1 flagword,3

    BITSET(f1)

~/sandbox/20$ cc -E t.c
# 1 "t.c"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "t.c"




    flagword |= (1<<3)

Add token pasting and a with a strong gorge you can get some extremely gratifying results out of the C preprocessor.

Reba answered 18/4, 2014 at 23:56 Comment(2)
Wow! I thank you! Your approach appears to be the same as the one from rici, right?Carnal
De nada, hf, and thanks back atcha :-) Boosted my mood. (edit: yup.)Reba
K
1

You could just define some constants, not using the preprocessor but enums for fewer surprises:

enum flags{
    powerOn = 1<<0,
    powerMask = ~powerOn,
    /*...*/
};

And use them like this:

flagsByte |= power;
flagsByte &= ~power;
flagsByte &= powerMask; /* same as previous line */

A good rule of thumb in C (and C++): Do not use the preprocessor if you can avoid it.

Anyway, if you can live with the inherent implementation-definedness of bitfields, use bitfields as Roberto Reale proposes.

Kettledrum answered 18/4, 2014 at 23:56 Comment(1)
I see that your solution is more elegant that the standard C solution, though maybe not quite as elegant as assembly. I do thank you.Carnal

© 2022 - 2024 — McMap. All rights reserved.