0

I'm looking through this project klib, and in one of the files (kseq.h, 75-77), there is macros this function:

#ifndef kroundup32

#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))

#endif

How does this function work? Does it return 7 things? I have an idea of the basic operations inside, I just don't get what is the form of its operation or output.

dbush
  • 205,898
  • 23
  • 218
  • 273
Chris
  • 153
  • 7
  • 1
    I doesn't return 7 things, it uses the [comma operator](https://en.cppreference.com/w/c/language/operator_other#Comma_operator) to perform several actions, having the value of the last action performed. – Thomas Jager Aug 25 '20 at 21:46
  • Yes that does! Thank you. I had not thought to look into a comma operator – Chris Aug 25 '20 at 21:51

1 Answers1

2

Normally you'd define this as a function and let the compiler figure out the rest, but if you're implementing it as a macro you need to consider the context.

Remember macros get expanded in the source, so they need to be syntactically valid in the context they appear. Within a function call you can't use ;, so , is used instead as a substitute.

Like this function might be called:

int v = 5 + 3 << 2;

if (other_fn(kroundup(v)) { ... }

Where using ; there would obviously break things badly. It needs ,:

if (other_fn((--(v), (v)|=(v)>>1, (v)|=(v)>>2, (v)|=(v)>>4, (v)|=(v)>>8, (v)|=(v)>>16, ++(v))) { ... }

Now the (x) part is a tradition to handle complex expressions:

if (other_fn(kroundup(5 + 3 << 2)) { ... }

Yet it doesn't handle those correctly due to using operators like -- that make no sense on anything but variables:

if (other_fn((--(5 + 3 << 2), (5 + 3 << 2)|=(5 + 3 << 2)>>1, ..., ++(5 + 3 << 2))) { ... }

It should be just x in the macro to catch problems like this.

In all honesty this macro shouldn't exist, the macro is just a terrible idea because it's buggy, it impedes understanding, and you should just let the compiler inline it as a regular function it if it thinks it can, like this:

int kroundup32(x) {
  --x;
  x |= x>>1;
  x |= x>>2;
  x |= x>>4;
  x |= x>>8;
  x |= x>>16;
  ++x;

  return x;
}

Where that is way more readable.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • I'm not sure everywhere it is used, but it comes up in some the generic data structures in this project, if that is any help – Chris Aug 25 '20 at 21:59
  • 1
    Some programmers are paranoid about the ability of the compiler to properly optimize and they write awful macros like this that actually make your code perform poorly because it's harder for the compiler to optimize. Modern C compilers are really, really good at identifying patterns if you express your code in the simplest possible form and avoid silly tricks like this. The days where you had to teach your compiler about optimization are long gone. Now there's so much accumulated knowledge in the compiler you can't possibly understand it all. – tadman Aug 25 '20 at 22:00