www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Convert this C macro kroundup32 to D mixin?

reply biocyberman <biocyberman gmail.com> writes:
What is the D mixin version equivalent to this macro:

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

The macro looks cryptic. What the macro does has been explained 
here: 
http://stackoverflow.com/questions/3384852/could-someone-help-explain-what-this-c-one-liner-does

But I still don't know how to convert that to D mixin. I would 
like 'mixin' instead of a function is to avoid function call 
overhead. Also because it is short, so I think a mixin is enough, 
not a 'template mixin'.
Apr 08
parent reply Mike Parker <aldacron gmail.com> writes:
On Saturday, 8 April 2017 at 09:53:47 UTC, biocyberman wrote:
 What is the D mixin version equivalent to this macro:

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

 The macro looks cryptic. What the macro does has been explained 
 here: 
 http://stackoverflow.com/questions/3384852/could-someone-help-explain-what-this-c-one-liner-does

 But I still don't know how to convert that to D mixin. I would 
 like 'mixin' instead of a function is to avoid function call 
 overhead. Also because it is short, so I think a mixin is 
 enough, not a 'template mixin'.
I would expect if you implement it as a function the compiler will inline it. You can always use the pragma(inline, true) [1] with -inline to verify. [1] https://dlang.org/spec/pragma.html#inline
Apr 08
next sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Saturday, 8 April 2017 at 10:02:01 UTC, Mike Parker wrote:

 I would expect if you implement it as a function the compiler 
 will inline it. You can always use the pragma(inline, true) [1] 
 with -inline to verify.

 [1] https://dlang.org/spec/pragma.html#inline
This gives me no error, so it does inline it. T kroundup32(T)(T x) { pragma(inline, true); --(x); (x)|=(x)>>1; (x)|=(x)>>2; (x)|=(x)>>4; (x)|=(x)>>8; (x)|=(x)>>16; return ++(x); }
Apr 08
parent reply biocyberman <biocyberman gmail.com> writes:
On Saturday, 8 April 2017 at 10:09:47 UTC, Mike Parker wrote:
 T kroundup32(T)(T x) {
     pragma(inline, true);
     --(x);
     (x)|=(x)>>1;
     (x)|=(x)>>2;
     (x)|=(x)>>4;
     (x)|=(x)>>8;
     (x)|=(x)>>16;
     return ++(x);
 }
I also came up with this: import std.stdio; pragma( inline, true ): static int kroundup32( int x){ --(x); writeln("X: ",x); (x)|=(x)>>1; writeln("X: ",x); (x)|=(x)>>2; writeln("X: ",x); (x)|=(x)>>4; writeln("X: ",x); (x)|=(x)>>8; writeln("X: ",x); (x)|=(x)>>16; writeln("X: ",x); ++(x); writeln("X: ",x); return x; } int main(){ int num = 31; num = kroundup32(num); writeln("Num:", num); return 0; } Is this way of using pragma the same as your way? I am still new to this so I want to understand more. And is it a good idea to do manipulate 'num' directly so I can omit 'return' and avoid re-assigning statement? That's what C version does.
Apr 08
parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Saturday, 8 April 2017 at 11:01:34 UTC, biocyberman wrote:
 On Saturday, 8 April 2017 at 10:09:47 UTC, Mike Parker wrote:
 T kroundup32(T)(T x) {
     pragma(inline, true);
     --(x);
     (x)|=(x)>>1;
     (x)|=(x)>>2;
     (x)|=(x)>>4;
     (x)|=(x)>>8;
     (x)|=(x)>>16;
     return ++(x);
 }
I also came up with this: import std.stdio; pragma( inline, true ): static int kroundup32( int x){ --(x); writeln("X: ",x); (x)|=(x)>>1; writeln("X: ",x); (x)|=(x)>>2; writeln("X: ",x); (x)|=(x)>>4; writeln("X: ",x); (x)|=(x)>>8; writeln("X: ",x); (x)|=(x)>>16; writeln("X: ",x); ++(x); writeln("X: ",x); return x; } int main(){ int num = 31; num = kroundup32(num); writeln("Num:", num); return 0; } Is this way of using pragma the same as your way? I am still new to this so I want to understand more.
The ':' means that it applies to everything that follows it, so while it doesn't matters in this example if you had pragma( inline, true ): int kroundup32( int x) { ... } auto someVeryLargeFunction( Args args) { // ... } and then you used someVeryLargeFunction in a bunch of places then that would cause a lot of binary bloat.
 And is it a good idea to do manipulate 'num' directly so I can 
 omit 'return' and avoid re-assigning statement? That's what C 
 version does.
if you want the the function to affect the variable use a 'ref' as in void kroundup32(T)(ref T x) { pragma(inline, true); --(x); (x)|=(x)>>1; (x)|=(x)>>2; (x)|=(x)>>4; (x)|=(x)>>8; (x)|=(x)>>16; return ++(x); } int main(){ int num = 31; writeln("Before: ",num); // 31 kroundup32(num); writeln("After: ", num); //32 return 0; } is it a good idea? I would not think it is necessary. As an aside the C version has parentheses around the "x" because it is a macro and it is substituted as text not symbolically, they are not needed in D.
Apr 08
parent biocyberman <biocyberman gmail.com> writes:
On Saturday, 8 April 2017 at 11:24:02 UTC, Nicholas Wilson wrote:

 The ':' means that it applies to everything that follows it, so 
 while it doesn't matters in this example if you had
 pragma( inline, true ):
 int kroundup32( int x) { ... }

 auto someVeryLargeFunction( Args args)
 {
     // ...
 }

 and then you used someVeryLargeFunction in a bunch of places 
 then that would cause a lot of binary bloat.
That's big difference! Thank you for pointing this out for me.
 if you want the the function to affect the variable use a 'ref' 
 as in

  void kroundup32(T)(ref T x) {
      pragma(inline, true);
      --(x);
      (x)|=(x)>>1;
      (x)|=(x)>>2;
      (x)|=(x)>>4;
      (x)|=(x)>>8;
      (x)|=(x)>>16;
      return ++(x);
  }

  int main(){
    int num = 31;
    writeln("Before: ",num); // 31
    kroundup32(num);
    writeln("After: ", num);   //32
    return 0;
  }

 is it a good idea? I would not think it is necessary.

 As an aside the C version has parentheses around the "x" 
 because it is a macro and it is substituted as text not 
 symbolically, they are not needed in D.
This thing now is clear and settled while I try to navigate my mind around many new things. Really appreciate your help, Nicolas.
Apr 08
prev sibling parent reply biocyberman <biocyberman gmail.com> writes:
On Saturday, 8 April 2017 at 10:02:01 UTC, Mike Parker wrote:
 I would expect if you implement it as a function the compiler 
 will inline it. You can always use the pragma(inline, true) [1] 
 with -inline to verify.

 [1] https://dlang.org/spec/pragma.html#inline
Thanks for mentioning pragma. However, anyway to do it with mixin? It's so cool so I want to do more stuffs with it :)
Apr 08
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 04/08/2017 03:11 AM, biocyberman wrote:
 On Saturday, 8 April 2017 at 10:02:01 UTC, Mike Parker wrote:
 I would expect if you implement it as a function the compiler will
 inline it. You can always use the pragma(inline, true) [1] with
 -inline to verify.

 [1] https://dlang.org/spec/pragma.html#inline
Thanks for mentioning pragma. However, anyway to do it with mixin? It's so cool so I want to do more stuffs with it :)
You can mixin declarations with a template but I don't see how it can help here. A string mixin would work but it's really ugly at the use site: string roundUp(alias x)() if (is (typeof(x) == uint)) { import std.string : format; return format(q{ --%1$s; %1$s |= %1$s >> 1; %1$s |= %1$s >> 2; %1$s |= %1$s >> 4; %1$s |= %1$s >> 8; %1$s |= %1$s >> 16; ++%1$s; }, x.stringof); } void main() { uint i = 42; mixin (roundUp!i); // <-- Ugly assert(i == 64); } Compare that to the following natural syntax that a function provides: void roundUp(ref uint x) { // ... } void main() { uint i = 42; i.roundUp(); // <-- Natural } Ali
Apr 08
parent biocyberman <biocyberman gmail.com> writes:
On Saturday, 8 April 2017 at 21:34:30 UTC, Ali Çehreli wrote:
 You can mixin declarations with a template but I don't see how 
 it can help here. A string mixin would work but it's really 
 ugly at the use site:

 string roundUp(alias x)()
 if (is (typeof(x) == uint)) {

     import std.string : format;
     return format(q{
         --%1$s;
         %1$s |= %1$s >>  1;
         %1$s |= %1$s >>  2;
         %1$s |= %1$s >>  4;
         %1$s |= %1$s >>  8;
         %1$s |= %1$s >> 16;
         ++%1$s;
         }, x.stringof);
 }

 void main() {
     uint i = 42;
     mixin (roundUp!i);    // <-- Ugly

     assert(i == 64);
 }

 Compare that to the following natural syntax that a function 
 provides:

 void roundUp(ref uint x) {
     // ...
 }

 void main() {
     uint i = 42;
     i.roundUp();    // <-- Natural
 }

 Ali
You made the point, it looks really ugly :). However, sometimes if this ugliness offer better performance, I would - in a desperate mood - use it. That's only 'if'. I put two other variant to a test, and this ugly version does worst as well. You can check out here: https://gist.github.com/biocyberman/0ad27721780e66546cbb6a39c0770d99 Maybe it is because string formating cost. Moving the import statement out of the function does not speed things up.
Apr 09