0

Assume that there are flag definitions such as:

SHF_WRITE     0x1
SHF_ALLOC     0x2
SHF_EXECINSTR 0x4
SHF_MASKPROC  0xf0000000

Given a flag, I need to output SHF_WRITE|SHF_ALLOC if the bits 0x1 and 0x2 is on.

How to do the trick in C?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
R__
  • 1,441
  • 5
  • 16
  • 22
  • 1
    Make an array with the bit flags, make an array of the flag names, loop over the bit flags, if `(value & flag) == flag` then `printf` the associated flag name. – zneak Jul 03 '11 at 03:47
  • @zneak ,but I need to output something like `printf("sh_flags:\t%d(SHF_WRITE|SHF_ALLOC)\n", psh64->sh_flags);`,should not output `()` if none flags are on. – R__ Jul 03 '11 at 03:52
  • Well, check that flags are set before calling `printf`. – zneak Jul 03 '11 at 04:00
  • @zneak,it doesn't seems possible to output correctly with `printf`,as I can't determine whether `()` or `|` is needed. – R__ Jul 03 '11 at 04:03
  • You really like ____, I bet... – DigitalRoss Jul 03 '11 at 04:10
  • What's the requirement for `SHF_MASKPROC`? Is it `(flag & SHF_MASKPROC) != 0` or `(flag & SHF_MASKPROC) == SHF_MASKPROC`? – Jonathan Leffler Jul 03 '11 at 04:30
  • `flag & SHF_MASKPROC) == SHF_MASKPROC` is the right one. – R__ Jul 03 '11 at 04:34

3 Answers3

5
#define V(n) { n, #n }

struct Code {
  int  value;
  char *name;
} decode[] = {
  V(SHF_WRITE),
  V(SHF_ALLOC),
  { 0, NULL },
};

void f(const int x) {
  struct Code *c = decode;
  int beenthere = 0;

  for(; c->name; ++c)
    if(x & c->value)
      printf("%s%s", beenthere++ ? "|" : "", c->name);
  if(beenthere)
    printf("\n");
}
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
  • Neat. On a 64-bit machine in particular, but even on a 32-bit machine, you'd save space by using `char name[12];` in place of a pointer. For the two filled entries, on a 32-bit machine, you have 2*8=16 bytes in the array, and 2*10=20 bytes in the strings for 36 bytes total; on a 64-bit machine, you might well have 32 bytes (and at least 24 bytes) in the array. With the character arrays, you'd have 2*(4+12)=32 bytes. I would probably not use the sentinel row but define: `enum { NUM_CODES = sizeof(decode)/sizeof(decode[0]) };` and would then run a loop `for (int i = 0; i < NUM_CODES; i++)`, etc. – Jonathan Leffler Jul 03 '11 at 04:45
  • @DigitalRoss,I think this will only work if `V()` will always expand before `SHF_WRITE`,otherwise `V(SHF_WRITE)` won't work as expected,is that guaranteed? – R__ Jul 03 '11 at 04:46
  • @Jonathan Leffler ,so it implies `V()` will always expand before `SHF_WRITE`,right?Otherwise it'll become `V(0x1)` – R__ Jul 03 '11 at 04:48
  • @R__: yes. See: [C Preprocessor and Concatenation](http://stackoverflow.com/questions/1489932/c-preprocessor-and-concatenation/1489985#1489985) for a description of how macro expansion occurs (in the context of token concatenation, but a lot applies to stringizing too), and also [How to make a char string from a c macro's value](http://stackoverflow.com/questions/195975/how-to-make-a-char-string-from-a-c-macros-value/196018#196018) for stringizing operations. – Jonathan Leffler Jul 03 '11 at 04:53
3

Just create a character buffer with enough space to hold all possible combinations of strings and add to it the appropriate strings for each applicable bit set. (or you could ditch the buffer and write straight to stdout, your choice) Here's a naive implementation of how you could do such a thing:

void print_flags(int flag)
{
#define BUFLEN (9+9+13+12+3+1)
                  /* for the text, pipes and null terminator*/
#define PAIRLEN 4
static struct { int value; const char *string; } pair[] =
{
    { SHF_WRITE,     "SHF_WRITE" },
    { SHF_ALLOC,     "SHF_ALLOC" },
    { SHF_EXECINSTR, "SHF_EXECINSTR" },
    { SHF_MASKPROC,  "SHF_MASKPROC" },
};

    char buf[BUFLEN];  /* declare the buffer */
    char *write = buf;    /* and a "write" pointer */
    int i;
    for (i = 0; i < PAIRLEN; i++)
    {
        if ((flag & pair[i].value) == pair[i].value) /* if flag is set... */
        {
            size_t written = write - buf;
            write += _snprintf(write, BUFLEN-written, "%s%s",
                written > 0 ? "|" : "",
                pair[i].string); /* write to the buffer */
        }
    }
    if (write != buf) /* if any of the flags were set... */
    {
        *write = '\0'; /* null terminate (just in case) */
        printf("(%s)", buf); /* print out the buffer */
    }
#undef PAIRLEN
#undef BUFLEN
}
Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
  • Mercado,how to output flags in `()` ,but only if at least one flag exists? – R__ Jul 03 '11 at 04:13
  • @R__: What do you mean? You want it printed out like: `(SHF_WRITE|SHF_ALLOC)` and blank if there's nothing set? – Jeff Mercado Jul 03 '11 at 04:16
  • @R__: I've updated one more time, see the end of the function for the changes. – Jeff Mercado Jul 03 '11 at 04:18
  • I think `snprintf(write, BUFLEN, strings[i]); `,here should be `snprintf(write, strlen(strings[i])-1, strings[i]); `,what's your opinion? – R__ Jul 03 '11 at 04:20
  • @R__: Ah, you're right, you got me there. :) I'll update again, there's a better way to express that. – Jeff Mercado Jul 03 '11 at 04:21
  • Would you be better off with a single array of structures instead of parallel arrays of integers and strings? There's less danger of mismapping if you have each number parallel to its corresponding string: `struct { int bit; char str[16]; } masks[] = { { SHF_WRITE, "SHF_WRITE" }, { SHF_ALLOC, "SHF_ALLOC" }, ... };`. You could also play fancy tricks with the preprocessor to generate those initializers: `#define SHF_INIT(name) { name, #name }` and then: `...masks[] = { SHF_INIT(SHF_WRITE), SHF_INIT(SHF_ALLOC), ... };`. – Jonathan Leffler Jul 03 '11 at 04:27
  • @Jonathan: Yeah, that would definitely be an improvement. At least here they're isolated to this function so it wouldn't be too much of a problem. I just haven't gotten into the habit of using macros that way (as DigitalRoss did in his answer). – Jeff Mercado Jul 03 '11 at 04:30
  • seems there's no such macro `#undefine`: `error: invalid preprocessing directive #undefine` – R__ Jul 03 '11 at 04:33
  • @R__: Sorry, it should have been `#undef`. It's been a while since I've played with C code or the preprocessor. – Jeff Mercado Jul 03 '11 at 04:35
  • The macro is not a clear cut benefit; it definitely reduces the chance of misspelling at the cost of some obfuscation of the code. That's why I wrote 'could' rather than 'should'. As ever, the problem specification was incomplete in terms of the expected outputs for none of the bits set, and for what is required for the multi-bit flag `SHF_MASKPROC`. – Jonathan Leffler Jul 03 '11 at 04:35
0

PROBLEM:

"SHF_WRITE|SHF_ALLOC" says "bit 0x1 OR bit 0x2", not "bits 0x1 AND 02x".

Nevertheless, if you wanted to print "SOME MSG" if the bits 0x1 and 0x2 were both "on" in some value "flag", here's how:

  if (flag & SHF_WRITE & SHF_ALLOC)
    printf ("SOME MSG, flag= 0x%x\n", flag);

If you wanted to print the text represtation of ANY bits that were "on" in the value, you might do something like this:

  char buf[80] = '\0';
  if (flag & SHF_WRITE)
    strcpy (buf, " SHF_WRITE");
  if (flag & SHF_ALLOC)
    strcpy (buf, " SHF_ALLOC");
  ...
  printf ("SOME MSG, flag= %s\n", buf);

And finally, if you DON'T want to print if NO bit is set, just do this:

  if (flag)
  {
    ... do printing ...
  }
  else
  {
    ... do nothing? ...
  }

paulsm4
  • 114,292
  • 17
  • 138
  • 190
  • I see no sign of the necessary '|' character. You have two `strcpy()` operations; if both flags are set, you will only see the second. – Jonathan Leffler Jul 03 '11 at 04:19
  • Your last comment won't work unfortunately. It could be possible to be given trash values that doesn't map to any known flag. So simply checking for a non-zero value wouldn't be _that_ easy. – Jeff Mercado Jul 03 '11 at 04:33