www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Complex (constant) C macros -> D

reply brad domain.invalid writes:
I'm just looking at a couple of projects, and wondering how I can 
convert them to D.  The point of these macros to to translate a human 
readable value to something that is hardware specific.  The details 
aren't too specific, but the macros convert from one constant form to 
another, here is the C code.
#define INVERT_COMMANDS
#ifdef INVERT_COMMANDS
#define INVERT ~
#else
#define INVERT
#endif

#define FLIP_COMMANDS
#ifdef FLIP_COMMANDS
#define FLIP(x) ( ((x & 0x80) >> 7) | \
                   ((x & 0x40) >> 5) | \
                   ((x & 0x20) >> 3) | \
                   ((x & 0x10) >> 1) | \
                   ((x & 0x08) << 1) | \
                   ((x & 0x04) << 3) | \
                   ((x & 0x02) << 5) | \
                   ((x & 0x01) << 7) )
#else
#define FLIP(x) (x)
#endif
#define SOME_COMMAND              ((INVERT (FLIP (0xE0)) & 0xFF ))

Now, yes I know this is ugly, but for this project we were reverse 
engineering a legacy interface and were not certain of a lot of things, 
being able to transform the value E0, which was what the documentation 
said, to what the hardware expected was essential.  If you notice, a 
decent pre-processor will fold all of those operations into a single new 
constant that is inverted and bit fliped.

Is there anyway to do this with D?  I suspect that the cleanest, and 
most D like way would be along the lines of:

version (flip_commands)
{
static uint flip(uint x) {
            return ((x & 0x80) >> 7) |
                   ((x & 0x40) >> 5) |
                   ((x & 0x20) >> 3) |
                   ((x & 0x10) >> 1) |
                   ((x & 0x08) << 1) |
                   ((x & 0x04) << 3) |
                   ((x & 0x02) << 5) |
                   ((x & 0x01) << 7) )
}
}
else
{
static uint flip (uint x) {
	return x;
}
}

const uint SOME_COMMAND = flip (0xE0);

Where SOME_COMMAND is evalutated once at runtime, before the main 
function gets called.
I really feel like this would be a powerful addition to D (if it doesn't 
already support it).  If it does support it - how can I do it?
Hmm, just thinking about it some more, I can use a static module 
constructor to assign the value, ie
static this ()
{
	SOME_COMMAND = flip (0xE0);
}
But that feels a little ugly.

Cheers
Brad

PS - sorry if the formatting sucks :)
Feb 22 2005
next sibling parent reply Kris <Kris_member pathlink.com> writes:
One way to do it is to use the PP on the D source code. There's nothing stopping
you using a C compiler (with PP output only!) to handle macros within D.

However, you can likely do the equivalent thing using templates:

template Flip (T : int)
{
T swizzle (T i)
{    
// your stuff here ...
return (i << 1) | (i >> 7); 
}
}

void test()
{
int flipped = Flip!(int).swizzle(0xEB30);
}

And then use 'alias' to clean up the syntax a bit.

- Kris


In article <cvg7sd$1d4k$1 digitaldaemon.com>, brad domain.invalid says...
I'm just looking at a couple of projects, and wondering how I can 
convert them to D.  The point of these macros to to translate a human 
readable value to something that is hardware specific.  The details 
aren't too specific, but the macros convert from one constant form to 
another, here is the C code.
#define INVERT_COMMANDS
#ifdef INVERT_COMMANDS
#define INVERT ~
#else
#define INVERT
#endif

#define FLIP_COMMANDS
#ifdef FLIP_COMMANDS

#define FLIP(x) ( ((x & 0x80) >> 7) | \
                   ((x & 0x40) >> 5) | \
                   ((x & 0x20) >> 3) | \
                   ((x & 0x10) >> 1) | \
                   ((x & 0x08) << 1) | \
                   ((x & 0x04) << 3) | \
                   ((x & 0x02) << 5) | \
                   ((x & 0x01) << 7) )
#else
#define FLIP(x) (x)
#endif
#define SOME_COMMAND              ((INVERT (FLIP (0xE0)) & 0xFF ))

Now, yes I know this is ugly, but for this project we were reverse 
engineering a legacy interface and were not certain of a lot of things, 
being able to transform the value E0, which was what the documentation 
said, to what the hardware expected was essential.  If you notice, a 
decent pre-processor will fold all of those operations into a single new 
constant that is inverted and bit fliped.

Is there anyway to do this with D?  I suspect that the cleanest, and 
most D like way would be along the lines of:

version (flip_commands)
{
static uint flip(uint x) {
            return ((x & 0x80) >> 7) |
                   ((x & 0x40) >> 5) |
                   ((x & 0x20) >> 3) |
                   ((x & 0x10) >> 1) |
                   ((x & 0x08) << 1) |
                   ((x & 0x04) << 3) |
                   ((x & 0x02) << 5) |
                   ((x & 0x01) << 7) )
}
}
else
{
static uint flip (uint x) {
	return x;
}
}

const uint SOME_COMMAND = flip (0xE0);

Where SOME_COMMAND is evalutated once at runtime, before the main 
function gets called.
I really feel like this would be a powerful addition to D (if it doesn't 
already support it).  If it does support it - how can I do it?
Hmm, just thinking about it some more, I can use a static module 
constructor to assign the value, ie
static this ()
{
	SOME_COMMAND = flip (0xE0);
}
But that feels a little ugly.

Cheers
Brad

PS - sorry if the formatting sucks :)

Feb 22 2005
parent reply brad domain.invalid writes:
Kris wrote:
 One way to do it is to use the PP on the D source code. There's nothing
stopping
 you using a C compiler (with PP output only!) to handle macros within D.
 
 However, you can likely do the equivalent thing using templates:
 
 template Flip (T : int)
 {
 T swizzle (T i)
 {    
 // your stuff here ...
 return (i << 1) | (i >> 7); 
 }
 }
 
 void test()
 {
 int flipped = Flip!(int).swizzle(0xEB30);
 }
 
 And then use 'alias' to clean up the syntax a bit.
 

Ugh! You're not really suggesting that I re-use C's pre-processor with D? Shame on you! :) I don't think that the template really solves my problem, because it still doesn't let me assign to a const variable, or outside of any functions. Brad
Feb 22 2005
parent reply Kris <Kris_member pathlink.com> writes:
In article <cvgcip$1l9p$1 digitaldaemon.com>, brad domain.invalid says...
Kris wrote:
 One way to do it is to use the PP on the D source code. There's nothing
stopping
 you using a C compiler (with PP output only!) to handle macros within D.
 
 However, you can likely do the equivalent thing using templates:
 
 template Flip (T : int)
 {
 T swizzle (T i)
 {    
 // your stuff here ...
 return (i << 1) | (i >> 7); 
 }
 }
 
 void test()
 {
 int flipped = Flip!(int).swizzle(0xEB30);
 }
 
 And then use 'alias' to clean up the syntax a bit.
 

Ugh! You're not really suggesting that I re-use C's pre-processor with D? Shame on you! :)

It's what one has to do to get _Line_ and _File_ info at this time. Shame on Walter ... :-)
I don't think that the template really solves my problem, because it 
still doesn't let me assign to a const variable, or outside of any 
functions.

Hmm. Walter shows a trick-pony via a recursive "compile time" template example near the bottom of this page: http://digitalmars.com/d/index.html One would hope that beast could be 'persuaded' to do what you need. - Kris
Feb 22 2005
parent reply brad domain.invalid writes:
 
 Hmm. Walter shows a trick-pony via a recursive "compile time" template example
 near the bottom of this page: http://digitalmars.com/d/index.html 
 
 One would hope that beast could be 'persuaded' to do what you need.
 
 - Kris
 
 

presumably could be extended to any other constant folding exercise. template Foo(int n) { const int val = n - 1; } const int test = Foo!(10).val; Though I think the syntax is on the clunky side :) Brad
Feb 22 2005
next sibling parent Kris <Kris_member pathlink.com> writes:
In article <cvgjed$20sn$1 digitaldaemon.com>, brad domain.invalid says...
 
 Hmm. Walter shows a trick-pony via a recursive "compile time" template example
 near the bottom of this page: http://digitalmars.com/d/index.html 
 
 One would hope that beast could be 'persuaded' to do what you need.
 
 - Kris
 
 

presumably could be extended to any other constant folding exercise. template Foo(int n) { const int val = n - 1; } const int test = Foo!(10).val; Though I think the syntax is on the clunky side :) Brad

I tried it with mixins: template Flip(int b) { const int swizzle = (b << 7) | (b >> 2); } void main() { mixin Flip!(0x4566) a; mixin Flip!(0xfee1) b; printf ("%x, %x\n", a.swizzle, b.swizzle); } Would be great to get 'regular' functions evaluated at compile time. Templates are indeed klunky for such things, and terribly non-intuitive. That aside, the biggest issue with templates is that they are nigh impossible to use with a source-code debugger. - Kris
Feb 22 2005
prev sibling parent reply "Aleksey Bobnev" <uw front.ru> writes:
 Though I think the syntax is on the clunky side :)

The better way: template Foo(int n) { const int Foo = n - 1; } const int test = Foo!(10);
Feb 23 2005
parent brad beveridge <brad nowhere.com> writes:
Aleksey Bobnev wrote:
Though I think the syntax is on the clunky side :)

The better way: template Foo(int n) { const int Foo = n - 1; } const int test = Foo!(10);

know the template syntax well enough. Brad
Feb 23 2005
prev sibling next sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
<brad domain.invalid> wrote in message 
news:cvg7sd$1d4k$1 digitaldaemon.com...
 I'm just looking at a couple of projects, and wondering how I can convert 
 them to D.  The point of these macros to to translate a human readable 
 value to something that is hardware specific.

 const uint SOME_COMMAND = flip (0xE0);

 Where SOME_COMMAND is evalutated once at runtime, before the main function 
 gets called.

Does SOME_COMMAND really have to be const? Can it be an inlined function call instead? I'm thinking something like uint flip(uint x){ compute and return flipped x } uint SOME_COMMAND(){ return flip(0xE0); } Then if you compile with -inline -O the call to SOME_COMMAND() and flip() should be inlined and the constant folding logic in the optimizer should evaluate the resulting expression at compile time. The down-sides of this approach are 1) assumes inlining and constant folding are enabled 2) you can't take the address of SOME_COMMAND and get a uint* 3) you have to write SOME_COMMAND() in code instead of SOME_COMMAND I think it's worth a shot, though. -Ben
Feb 22 2005
parent reply brad domain.invalid writes:
Ben Hinkle wrote:
 <brad domain.invalid> wrote in message 
 news:cvg7sd$1d4k$1 digitaldaemon.com...
 
I'm just looking at a couple of projects, and wondering how I can convert 
them to D.  The point of these macros to to translate a human readable 
value to something that is hardware specific.

[snip]
const uint SOME_COMMAND = flip (0xE0);

Where SOME_COMMAND is evalutated once at runtime, before the main function 
gets called.

[snip] Does SOME_COMMAND really have to be const? Can it be an inlined function call instead? I'm thinking something like uint flip(uint x){ compute and return flipped x } uint SOME_COMMAND(){ return flip(0xE0); } Then if you compile with -inline -O the call to SOME_COMMAND() and flip() should be inlined and the constant folding logic in the optimizer should evaluate the resulting expression at compile time. The down-sides of this approach are 1) assumes inlining and constant folding are enabled 2) you can't take the address of SOME_COMMAND and get a uint* 3) you have to write SOME_COMMAND() in code instead of SOME_COMMAND I think it's worth a shot, though. -Ben

case), though it is a little more code than in the C case. It does also make assumptions about the inliner/optimiser - but that is easily verified with an assembler check. I guess I was hoping that I would be able to control constant munging in D just as well as I can in C with the preprocessor. I mean, with the C code I know that it will fold down to just another constant - I'll need to check the assembler output with the D method :) Brad
Feb 22 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
<brad domain.invalid> wrote in message 
news:cvghv0$1u9s$1 digitaldaemon.com...
 Ben Hinkle wrote:
 <brad domain.invalid> wrote in message 
 news:cvg7sd$1d4k$1 digitaldaemon.com...

I'm just looking at a couple of projects, and wondering how I can convert 
them to D.  The point of these macros to to translate a human readable 
value to something that is hardware specific.

[snip]
const uint SOME_COMMAND = flip (0xE0);

Where SOME_COMMAND is evalutated once at runtime, before the main 
function gets called.

[snip] Does SOME_COMMAND really have to be const? Can it be an inlined function call instead? I'm thinking something like uint flip(uint x){ compute and return flipped x } uint SOME_COMMAND(){ return flip(0xE0); } Then if you compile with -inline -O the call to SOME_COMMAND() and flip() should be inlined and the constant folding logic in the optimizer should evaluate the resulting expression at compile time. The down-sides of this approach are 1) assumes inlining and constant folding are enabled 2) you can't take the address of SOME_COMMAND and get a uint* 3) you have to write SOME_COMMAND() in code instead of SOME_COMMAND I think it's worth a shot, though. -Ben

case), though it is a little more code than in the C case. It does also make assumptions about the inliner/optimiser - but that is easily verified with an assembler check. I guess I was hoping that I would be able to control constant munging in D just as well as I can in C with the preprocessor. I mean, with the C code I know that it will fold down to just another constant - I'll need to check the assembler output with the D method :) Brad

Well, technically all that the C version does is the inlining. The constant folding is still the same as D.
Feb 22 2005
parent reply brad domain.invalid writes:
 
 
 Well, technically all that the C version does is the inlining. The constant 
 folding is still the same as D. 
 
 

True, but would it actually work like that with plain functions? For example, the optimiser would need to be clever enough to realise that each call to the function that has a constant input should be evaluated at compile time, and then just that constant injected into the code. And for non-const inputs the actual function code gets called. What I think would happen is this int invert (int i) { return ~i; }; int myConst () { return invert(0xF0); } void code () { ... someFunc(myConst) ... } I think the optimiser would turn this into (with inlining, and constant folding) void code () { ... someFunc ( ~0xF0 ); .. } ie, the call will be inlined, but does the optimiser then further reduce that inlined call to the final optimisation of someFunc (0x0F) ? Doing it with templates (as discussed in this thread) would do it properly. I don't know enough about how the optimiser works to see if both the call will be inlined, and the constant folded. And if I don't know the answer well enough, my first instinct (when time/space is critical) is NOT to trust the compiler to do it right :) Brad
Feb 22 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
<brad domain.invalid> wrote in message 
news:cvgm9s$25ab$1 digitaldaemon.com...
 Well, technically all that the C version does is the inlining. The 
 constant folding is still the same as D.

True, but would it actually work like that with plain functions? For example, the optimiser would need to be clever enough to realise that each call to the function that has a constant input should be evaluated at compile time, and then just that constant injected into the code. And for non-const inputs the actual function code gets called. What I think would happen is this int invert (int i) { return ~i; }; int myConst () { return invert(0xF0); } void code () { ... someFunc(myConst) ... } I think the optimiser would turn this into (with inlining, and constant folding) void code () { ... someFunc ( ~0xF0 ); .. } ie, the call will be inlined, but does the optimiser then further reduce that inlined call to the final optimisation of someFunc (0x0F) ?

It's the same as with C's macros. Let me illustrate. The C code #define invert(x) (~x) #define myConst (invert(0xF0)) ... someFunc( myConst ) gets expanded by the preprocessor to someFunc( (~(0xF0)) ) and then the compiler's constant folding takes it from there. In D the only difference is that there is no preprocessor to inline the "macros" - the compiler does it instead. After inlining the D compiler ends up with the exact same expression tree as the C version and from then on it is the same as C.
 Doing it with templates (as discussed in this thread) would do it 
 properly.  I don't know enough about how the optimiser works to see if 
 both the call will be inlined, and the constant folded.  And if I don't 
 know the answer well enough, my first instinct (when time/space is 
 critical) is NOT to trust the compiler to do it right :)

 Brad

That's ok - trusting the compiler can be risky. Though if dmd's inlining and constant folding can't produce the same result in your case as a C compiler would then Walter needs to spend some time and fix that. See also http://www.digitalmars.com/d/htomodule.html the section about macros and http://www.digitalmars.com/d/pretod.html the section about macros. Inlining is your friend!
Feb 22 2005
parent reply brad beveridge <brad nowhere.com> writes:
 
 It's the same as with C's macros. Let me illustrate. The C code
 
   #define invert(x) (~x)
   #define myConst (invert(0xF0))
   ...
   someFunc( myConst )
 
 gets expanded by the preprocessor to
   someFunc( (~(0xF0)) )
 and then the compiler's constant folding takes it from there. In D the only 
 difference is that there is no preprocessor to inline the "macros" - the 
 compiler does it instead. After inlining the D compiler ends up with the 
 exact same expression tree as the C version and from then on it is the same 
 as C.

 That's ok - trusting the compiler can be risky. Though if dmd's inlining and 
 constant folding can't produce the same result in your case as a C compiler 
 would then Walter needs to spend some time and fix that.
 See also http://www.digitalmars.com/d/htomodule.html the section about 
 macros and http://www.digitalmars.com/d/pretod.html the section about 
 macros.
 Inlining is your friend! 
 

That's good to hear, and it is what I expected. I had thought that what might happen is 1) Constant folding occurs (ie, 0xF0 - this is as reduced as it can get) 2) Inlining occurs, the ~ operation still has to happen on 0xF0. But if constant folding occurs after inlining, then everything is OK. If I am feeling particularly bored I might need to look at the assembler output. I think the template method is closer to what I am wanting. Brad
Feb 22 2005
parent reply brad beveridge <brad nowhere.com> writes:
brad beveridge wrote:
 
 That's ok - trusting the compiler can be risky. Though if dmd's 
 inlining and constant folding can't produce the same result in your 
 case as a C compiler would then Walter needs to spend some time and 
 fix that.
 See also http://www.digitalmars.com/d/htomodule.html the section about 
 macros and http://www.digitalmars.com/d/pretod.html the section about 
 macros.
 Inlining is your friend!

That's good to hear, and it is what I expected. I had thought that what might happen is 1) Constant folding occurs (ie, 0xF0 - this is as reduced as it can get) 2) Inlining occurs, the ~ operation still has to happen on 0xF0. But if constant folding occurs after inlining, then everything is OK. If I am feeling particularly bored I might need to look at the assembler output. I think the template method is closer to what I am wanting. Brad

The following code illustrates both methods 1) Inlining a function call of a constant 2) Generating a constant with a template The bad news is that you were wrong Ben :) (1) doesn't give optimal results (2) does D doesn't inline & then fold constants. Which makes sense - I don't know much about compilers/optimisers, but I expect that an optimiser would need to be very smart to decide "hey, this function has constant inputs, maybe I should evaluate it at compile time and see if I can give it a constant output" Anyhow, here is some sample code. A little grovelling through the assembler output shows what is going on. Brad import std.stdio; int flip(int x) { return ( ((x & 0x80) >> 7) | ((x & 0x40) >> 5) | ((x & 0x20) >> 3) | ((x & 0x10) >> 1) | ((x & 0x08) << 1) | ((x & 0x04) << 3) | ((x & 0x02) << 5) | ((x & 0x01) << 7) ); } template tflip(int x) { const int val = ( ((x & 0x80) >> 7) | ((x & 0x40) >> 5) | ((x & 0x20) >> 3) | ((x & 0x10) >> 1) | ((x & 0x08) << 1) | ((x & 0x04) << 3) | ((x & 0x02) << 5) | ((x & 0x01) << 7) ); } void main() { // comment these lines in/out to show that constant folding happens // before inlining. // sub-optimal behaviour, the flip function is called and 0xFE // is bit flipped at runtime // note, you cannot have this int "const" //int i = flip(0xFE); // optimal method - the template is evaluated at compile time // and i evaluates to 0x7F, as is correct //const int i = tflip!(0xFE).val; writef("%d\n", i); }
Feb 22 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"brad beveridge" <brad nowhere.com> wrote in message 
news:cvhc9r$1vm$1 digitaldaemon.com...
 brad beveridge wrote:
 That's ok - trusting the compiler can be risky. Though if dmd's inlining 
 and constant folding can't produce the same result in your case as a C 
 compiler would then Walter needs to spend some time and fix that.
 See also http://www.digitalmars.com/d/htomodule.html the section about 
 macros and http://www.digitalmars.com/d/pretod.html the section about 
 macros.
 Inlining is your friend!

That's good to hear, and it is what I expected. I had thought that what might happen is 1) Constant folding occurs (ie, 0xF0 - this is as reduced as it can get) 2) Inlining occurs, the ~ operation still has to happen on 0xF0. But if constant folding occurs after inlining, then everything is OK. If I am feeling particularly bored I might need to look at the assembler output. I think the template method is closer to what I am wanting. Brad

following code illustrates both methods 1) Inlining a function call of a constant 2) Generating a constant with a template The bad news is that you were wrong Ben :) (1) doesn't give optimal results (2) does D doesn't inline & then fold constants. Which makes sense - I don't know much about compilers/optimisers, but I expect that an optimiser would need to be very smart to decide "hey, this function has constant inputs, maybe I should evaluate it at compile time and see if I can give it a constant output" Anyhow, here is some sample code. A little grovelling through the assembler output shows what is going on. Brad import std.stdio; int flip(int x) { return ( ((x & 0x80) >> 7) | ((x & 0x40) >> 5) | ((x & 0x20) >> 3) | ((x & 0x10) >> 1) | ((x & 0x08) << 1) | ((x & 0x04) << 3) | ((x & 0x02) << 5) | ((x & 0x01) << 7) ); } template tflip(int x) { const int val = ( ((x & 0x80) >> 7) | ((x & 0x40) >> 5) | ((x & 0x20) >> 3) | ((x & 0x10) >> 1) | ((x & 0x08) << 1) | ((x & 0x04) << 3) | ((x & 0x02) << 5) | ((x & 0x01) << 7) ); } void main() { // comment these lines in/out to show that constant folding happens // before inlining. // sub-optimal behaviour, the flip function is called and 0xFE // is bit flipped at runtime // note, you cannot have this int "const" //int i = flip(0xFE); // optimal method - the template is evaluated at compile time // and i evaluates to 0x7F, as is correct //const int i = tflip!(0xFE).val; writef("%d\n", i); }

I had forgotten about this bug where initializers aren't inlined. Everything works fine if you replace int i = flip(0xFE); with int i; i = flip(0xFE); or if you hadn't used a variable at all and instead just wrote writef("%d\n", flip(0xFE)); Walter, is that bug fixable? It's very natural to want to put inlinable expressions in initializers. -Ben
Feb 23 2005
next sibling parent reply brad beveridge <brad nowhere.com> writes:
 I had forgotten about this bug where initializers aren't inlined. Everything 
 works fine if you replace
      int i = flip(0xFE);
 with
      int i;
      i = flip(0xFE);
 or if you hadn't used a variable at all and instead just wrote
 writef("%d\n", flip(0xFE));
 
 Walter, is that bug fixable? It's very natural to want to put inlinable 
 expressions in initializers.
 
 -Ben 
 
 

parameter 0xFE. This correctly gets converted to 0x7F, but it gets converted at runtime, not compiletime. Brad
Feb 23 2005
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
"brad beveridge" <brad nowhere.com> wrote in message 
news:cvij05$1mdo$1 digitaldaemon.com...
 I had forgotten about this bug where initializers aren't inlined. 
 Everything works fine if you replace
      int i = flip(0xFE);
 with
      int i;
      i = flip(0xFE);
 or if you hadn't used a variable at all and instead just wrote
 writef("%d\n", flip(0xFE));

 Walter, is that bug fixable? It's very natural to want to put inlinable 
 expressions in initializers.

 -Ben

0xFE. This correctly gets converted to 0x7F, but it gets converted at runtime, not compiletime. Brad

Did you pass the -inline and -O flags to the compiler? When I tell the compiler to inline and optimize it does everything at compile time (except with the initiliazer bug). Without those flags it doesn't.
Feb 23 2005
parent brad domain.invalid writes:
Ben Hinkle wrote:
 "brad beveridge" <brad nowhere.com> wrote in message 
 news:cvij05$1mdo$1 digitaldaemon.com...
 
I had forgotten about this bug where initializers aren't inlined. 
Everything works fine if you replace
     int i = flip(0xFE);
with
     int i;
     i = flip(0xFE);
or if you hadn't used a variable at all and instead just wrote
writef("%d\n", flip(0xFE));

Walter, is that bug fixable? It's very natural to want to put inlinable 
expressions in initializers.

-Ben

Not quite fine - the function gets inlined, and called with the parameter 0xFE. This correctly gets converted to 0x7F, but it gets converted at runtime, not compiletime. Brad

Did you pass the -inline and -O flags to the compiler? When I tell the compiler to inline and optimize it does everything at compile time (except with the initiliazer bug). Without those flags it doesn't.

initialiser not inlining/optimising thing is annoying. Brad
Feb 23 2005
prev sibling next sibling parent Kris <Kris_member pathlink.com> writes:
In article <cvi17n$ubp$1 digitaldaemon.com>, Ben Hinkle says...
I had forgotten about this bug where initializers aren't inlined. Everything 
works fine if you replace
     int i = flip(0xFE);
with
     int i;
     i = flip(0xFE);
or if you hadn't used a variable at all and instead just wrote
writef("%d\n", flip(0xFE));

Walter, is that bug fixable? It's very natural to want to put inlinable 
expressions in initializers.

I'll second that ~ such thing can cause much wailing, and gnashing of teeth. - Kris
Feb 23 2005
prev sibling parent "Walter" <newshound digitalmars.com> writes:
"Ben Hinkle" <ben.hinkle gmail.com> wrote in message
news:cvi17n$ubp$1 digitaldaemon.com...
 "brad beveridge" <brad nowhere.com> wrote in message
 news:cvhc9r$1vm$1 digitaldaemon.com...
 brad beveridge wrote:
 That's ok - trusting the compiler can be risky. Though if dmd's




 and constant folding can't produce the same result in your case as a C
 compiler would then Walter needs to spend some time and fix that.
 See also http://www.digitalmars.com/d/htomodule.html the section about
 macros and http://www.digitalmars.com/d/pretod.html the section about
 macros.
 Inlining is your friend!

That's good to hear, and it is what I expected. I had thought that



 might happen is
 1) Constant folding occurs (ie, 0xF0 - this is as reduced as it can



 2) Inlining occurs, the ~ operation still has to happen on 0xF0.
 But if constant folding occurs after inlining, then everything is OK.



 I am feeling particularly bored I might need to look at the assembler
 output.

 I think the template method is closer to what I am wanting.

 Brad

following code illustrates both methods 1) Inlining a function call of a constant 2) Generating a constant with a template The bad news is that you were wrong Ben :) (1) doesn't give optimal results (2) does D doesn't inline & then fold constants. Which makes sense - I don't


 much about compilers/optimisers, but I expect that an optimiser would


 to be very smart to decide "hey, this function has constant inputs,


 I should evaluate it at compile time and see if I can give it a constant
 output"

 Anyhow, here is some sample code.  A little grovelling through the
 assembler output shows what is going on.

 Brad

 import std.stdio;

 int flip(int x)
 {
     return ( ((x & 0x80) >> 7) |
                           ((x & 0x40) >> 5) |
                           ((x & 0x20) >> 3) |
                           ((x & 0x10) >> 1) |
                           ((x & 0x08) << 1) |
                           ((x & 0x04) << 3) |
                           ((x & 0x02) << 5) |
                           ((x & 0x01) << 7) );
 }

 template tflip(int x)
 {
     const int val = ( ((x & 0x80) >> 7) |
                           ((x & 0x40) >> 5) |
                           ((x & 0x20) >> 3) |
                           ((x & 0x10) >> 1) |
                           ((x & 0x08) << 1) |
                           ((x & 0x04) << 3) |
                           ((x & 0x02) << 5) |
                           ((x & 0x01) << 7) );

 }

 void main()
 {
     // comment these lines in/out to show that constant folding happens
     // before inlining.

     // sub-optimal behaviour, the flip function is called and 0xFE
     // is bit flipped at runtime
     // note, you cannot have this int "const"
     //int i = flip(0xFE);

     // optimal method - the template is evaluated at compile time
     // and i evaluates to 0x7F, as is correct
     //const int i = tflip!(0xFE).val;
     writef("%d\n", i);
 }

I had forgotten about this bug where initializers aren't inlined.

 works fine if you replace
      int i = flip(0xFE);
 with
      int i;
      i = flip(0xFE);
 or if you hadn't used a variable at all and instead just wrote
 writef("%d\n", flip(0xFE));

 Walter, is that bug fixable? It's very natural to want to put inlinable
 expressions in initializers.

Yes.
Feb 24 2005
prev sibling next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Wed, 23 Feb 2005 10:23:43 +1300, brad domain.invalid wrote:

 I'm just looking at a couple of projects, and wondering how I can 
 convert them to D.  The point of these macros to to translate a human 
 readable value to something that is hardware specific.  The details 
 aren't too specific, but the macros convert from one constant form to 
 another, here is the C code.
 #define INVERT_COMMANDS
 #ifdef INVERT_COMMANDS
 #define INVERT ~
 #else
 #define INVERT
 #endif
 
 #define FLIP_COMMANDS
 #ifdef FLIP_COMMANDS
 #define FLIP(x) ( ((x & 0x80) >> 7) | \
                    ((x & 0x40) >> 5) | \
                    ((x & 0x20) >> 3) | \
                    ((x & 0x10) >> 1) | \
                    ((x & 0x08) << 1) | \
                    ((x & 0x04) << 3) | \
                    ((x & 0x02) << 5) | \
                    ((x & 0x01) << 7) )
 #else
 #define FLIP(x) (x)
 #endif
 #define SOME_COMMAND              ((INVERT (FLIP (0xE0)) & 0xFF ))
 
 Now, yes I know this is ugly, but for this project we were reverse 
 engineering a legacy interface and were not certain of a lot of things, 
 being able to transform the value E0, which was what the documentation 
 said, to what the hardware expected was essential.  If you notice, a 
 decent pre-processor will fold all of those operations into a single new 
 constant that is inverted and bit fliped.
 
 Is there anyway to do this with D?  I suspect that the cleanest, and 
 most D like way would be along the lines of:
 
 version (flip_commands)
 {
 static uint flip(uint x) {
             return ((x & 0x80) >> 7) |
                    ((x & 0x40) >> 5) |
                    ((x & 0x20) >> 3) |
                    ((x & 0x10) >> 1) |
                    ((x & 0x08) << 1) |
                    ((x & 0x04) << 3) |
                    ((x & 0x02) << 5) |
                    ((x & 0x01) << 7) )
 }
 }
 else
 {
 static uint flip (uint x) {
 	return x;
 }
 }
 
 const uint SOME_COMMAND = flip (0xE0);
 
 Where SOME_COMMAND is evalutated once at runtime, before the main 
 function gets called.
 I really feel like this would be a powerful addition to D (if it doesn't 
 already support it).  If it does support it - how can I do it?
 Hmm, just thinking about it some more, I can use a static module 
 constructor to assign the value, ie
 static this ()
 {
 	SOME_COMMAND = flip (0xE0);
 }
 But that feels a little ugly.
 

constructors are for ... "SOME_COMMAND is evaluated once at runtime, before the main function gets called". In fact, I'd even make the flip() function a nested function within the module constructor if it's not needed elsewhere in the program. -- Derek Melbourne, Australia 23/02/2005 11:06:35 AM
Feb 22 2005
parent reply brad domain.invalid writes:
 It might "feel a little ugly" to you, but that is exactly what module
 constructors are for ... "SOME_COMMAND is evaluated once at runtime, before
 the main function gets called".
 
 In fact, I'd even make the flip() function a nested function within the
 module constructor if it's not needed elsewhere in the program.
 

The only remaining difference is that I can't assign a value to a const variable with the static module constructor method. I'm not particularly concerned, just found a little corner that I think C does better than D, and I'd like D to be better than C everywhere :) Is there a reason why D the following couldn't be valid D? static int foo(int i) { return i/2; }; const int myVar = foo(10); Brad
Feb 22 2005
next sibling parent xs0 <xs0 xs0.com> writes:
 Is there a reason why D the following couldn't be valid D?
 
 static int foo(int i) { return i/2; };
 const int myVar = foo(10);

I think the issue is that foo(10) may not always return the same thing in the general case, so you'd need to do code analysis to see that it does. Requiring code analysis in the spec (meaning every compiler has to do it) is not a good thing. The meta-programming technique (those recursive templates) is somewhat different and doesn't require code analysis (except evaluating constant expressions, but that is easy). Basically, by instantiating (sp?) those templates, a function call is simulated and its "return value" can be used.. xs0
Feb 22 2005
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
On Wed, 23 Feb 2005 13:20:51 +1300, brad domain.invalid wrote:

 It might "feel a little ugly" to you, but that is exactly what module
 constructors are for ... "SOME_COMMAND is evaluated once at runtime, before
 the main function gets called".
 
 In fact, I'd even make the flip() function a nested function within the
 module constructor if it's not needed elsewhere in the program.
 

The only remaining difference is that I can't assign a value to a const variable with the static module constructor method. I'm not particularly concerned, just found a little corner that I think C does better than D, and I'd like D to be better than C everywhere :) Is there a reason why D the following couldn't be valid D? static int foo(int i) { return i/2; }; const int myVar = foo(10);

Oops! I forgot about the need for a 'const'. I've occasionally thought that a write-once type of variable would be a useful addition to a language. I other words, once an assignment to such a variable has been done, all other attempts at assigning something to it would be a run-time error. It could be used for things whose value was not knowable at compile time, but during run time, it should behave as if it was a const. -- Derek Melbourne, Australia 23/02/2005 12:34:28 PM
Feb 22 2005
parent Manfred Nowak <svv1999 hotmail.com> writes:
Derek Parnell <derek psych.ward> wrote:

[...]
 I've occasionally thought that a write-once type of variable
 would be a useful addition to a language.

sather has `once' parameters <cite href="http://www.icsi.berkeley.edu/ ~sather/Documentation/LanguageDescription/webmaker/DescriptionX2Eiter ators-chapte-1.html#HEADING1-36"> Arguments which are marked with the mode 'once' are only evaluated the first time they are encountered during a loop execution. </cite> And "once" variables are easily done in D with properties: <example> private: int m= void; bit assigned= false; public: int i(){ if( assigned) return m; else throw new Exception; } int i( int value){ if( assigned) throw new Exception; else { m= value; assigned= true; } } </example>
Feb 26 2005
prev sibling next sibling parent Georg Wrede <georg iki.fi> writes:
brad domain.invalid wrote:
 I'm just looking at a couple of projects, and wondering how I can 
 convert them to D.  The point of these macros to to translate a human 
 readable value to something that is hardware specific.  The details 
 aren't too specific, but the macros convert from one constant form to 
 another, here is the C code.

That's a bit like the following C++, from src/dmd/parse.c switch (token.value) { #define X(tok,ector) \ case tok: nextToken(); e2 = parseAssignExp(); \ e = new ector(loc,e,e2); continue; X(TOKassign, AssignExp); X(TOKaddass, AddAssignExp); X(TOKminass, MinAssignExp); X(TOKmulass, MulAssignExp); X(TOKdivass, DivAssignExp); X(TOKmodass, ModAssignExp); X(TOKandass, AndAssignExp); X(TOKorass, OrAssignExp); X(TOKxorass, XorAssignExp); X(TOKshlass, ShlAssignExp); X(TOKshrass, ShrAssignExp); X(TOKushrass, UshrAssignExp); X(TOKcatass, CatAssignExp); #undef X default: break; } I've found some ways to rewrite this in D, but none of them "look nice".
Feb 22 2005
prev sibling parent Andy Friesen <andy ikagames.com> writes:
brad domain.invalid wrote:
 I'm just looking at a couple of projects, and wondering how I can 
 convert them to D.  The point of these macros to to translate a human 
 readable value to something that is hardware specific.  The details 
 aren't too specific, but the macros convert from one constant form to 
 another [...]

 I really feel like this would be a powerful addition to D (if it doesn't 
 already support it).  If it does support it - how can I do it?

Sure does. Try this: version = INVERT_COMMANDS; version (INVERT_COMMANDS) { template INVERT(int I) { const int INVERT = ~I; } } else { template INVERT(int I) { const int INVERT = I; } } version = FLIP_COMMANDS; version (FLIP_COMMANDS) { template FLIP(int X) { const int FLIP = ((x & 0x80) >> 7) | ((x & 0x40) >> 5) | ((x & 0x20) >> 3) | ((x & 0x10) >> 1) | ((x & 0x08) << 1) | ((x & 0x04) << 3) | ((x & 0x02) << 5) | ((x & 0x01) << 7) ) ; } } else { template FLIP(int X) { const int FLIP = X; } } const int SOME_COMMAND = INVERT!(FLIP!(0xE0) & 0xFF); -- andy
Feb 25 2005