bit operations

M

Mark

Hello

I had to make some mechanism to turn on/off debugging messages at run-time
in various modules of a project. This is what I've come up with: I defined
several constants, each with exactly one bit set, and a global variable
which should be defined in a particular module and assigned with one or a
few values TAG_1...TAG_4:

/* debug.h */
#ifndef DEBUG_H
#define DEBUG_H

#include <stdio.h>

#define TAG_1 0x00000001
#define TAG_2 0x00000002
#define TAG_3 0x00000004
#define TAG_4 0x00000008

extern unsigned long debug_mask;

#define DEBUG(tag, fmt, arg) \
do { \
if (0 == ((~debug_mask) & (tag))) { \
printf(fmt, arg); \
} \
} while (0)

#endif /* DEBUG_H */

/* example of usage*/
#include "debug.h"

unsigned long debug_mask;

int main(void)
{
int i=1;
char p='c';

debug_mask = TAG_4 | TAG_2;

DEBUG(TAG_4, "i=%d\n", i);
DEBUG(TAG_2, "p=%c\n", p);
return 0;
}

Right now I need only one argument passed. It appears the macro works as
expected, or I have not spotted bugs yet.
Is there anything wrong with this simple implementation (especially bit
operations)? Any advices are welcome.
 
S

Seebs

#define DEBUG(tag, fmt, arg) \
do { \
if (0 == ((~debug_mask) & (tag))) { \

Why not
if (debug_mask & (tag))

?
Right now I need only one argument passed. It appears the macro works as
expected, or I have not spotted bugs yet.
Is there anything wrong with this simple implementation (especially bit
operations)? Any advices are welcome.

If you feel safe relying on C99, you can modify the macro to pass a variable
argument list.

Me, I generally write this as, roughly:

int debug(int level, char *fmt, ...) {
va_list ap;
int r;
if (level > debuglevel)
return 0;
va_start(ap, fmt);
r = vfprintf(stderr, fmt, ap);
va_end(ap);
return r;
}

I know it's probably more expensive to always make the call, but I have
usually found that I simply don't care much. And yes, tags and bit
masks are probably better, I hadn't bothered yet.

-s
 
M

Mark

Mark said:
I had to make some mechanism to turn on/off debugging messages at run-
time in various modules of a project. This is what I've come up with:
I defined several constants, each with exactly one bit set, and a
global variable which should be defined in a particular module and
assigned with one or a few values TAG_1...TAG_4:


Forgot to mention that 'debug_mask' is controlled from a command-line
interface.
 
M

Mark

Seebs said:
Why not
if (debug_mask & (tag))

This is true only when every bit of 'debug_mask' is set, but it is not
necessary the case. Or I'm missing something ?
 
S

Seebs

This is true only when every bit of 'debug_mask' is set, but it is not
necessary the case. Or I'm missing something ?

Waiiiit. I just realized that what you wrote is... well, it's surprising
to me. See, I'm used to the "mask" being the set of debug bits we care
about. So I'd usually do:

#define BEER 0x1
#define ANIME 0x2
#define PIZZA 0x4

debug(BEER, "we have obtained delicious beer");
debug(ANIME | PIZZA, "we are going to watch bad dubs and eat pizza");

The way this would work, then, you'd set debug_mask to, say, "BEER | PIZZA"
if you were interested only in messages that had to do with pizza and beer.
Then, the test I use would be:

debug_mask = BEER | PIZZA; // 0x05
...
if (debug_mask & (ANIME | PIZZA))

so that'd be 0x5 & 0x6, and since 0x4 is set in both, the result is 0x4,
so it's non-zero, so we execute the code.

It looks like your test is:
if (0 == (~(debug_mask) & (ANIME | PIZZA)))

assuming a 16-bit int to save typing, that's
if (0 == (~(0x5) & (0x6)))
which is
if (0 == (0xfffa & 0x6))

.... which means that you match, not on the things that are set, but on the
things that AREN'T set. So it looks like your intent is that the mask shows
messages which are being suppressed. So for you, "debug_mask = BEER | PIZZA"
means "shut up about the beer and pizza, I just wanna watch Ranma", which
is the opposite of the idiom I'm used to.

-s
 
M

Mark

Seebs wrote:
[snip]
The way this would work, then, you'd set debug_mask to, say, "BEER |
PIZZA" if you were interested only in messages that had to do with
pizza and beer. Then, the test I use would be:

debug_mask = BEER | PIZZA; // 0x05
...
if (debug_mask & (ANIME | PIZZA))

so that'd be 0x5 & 0x6, and since 0x4 is set in both, the result is
0x4, so it's non-zero, so we execute the code.

Yes, you are absolutely right. Thanks a lot.
 
H

Hallvard B Furuseth

Mark said:
(...)
#define TAG_4 0x00000008

This has type int. Neither the 0x nor all the zeroes change that.
Bitmasks should usually have unsigned type, provided you make quite
sure they match the type you mask against:
extern unsigned long debug_mask;

so make that 0x...zeroes if you feel a need for them...8UL.
printf(fmt, arg); \

Error/debug messages should normally go to go to stderr, not stdout.
fprintf(stderr, fmt, arg);
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top