www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - D: Unexpected output when using a delegate and EnumMembers

reply Travis Gockel <travis gockelhut.com> writes:
I have some rather odd behavior in my D program that I've narrowed down to this:

    import std.algorithm;
    import std.stdio;
    import std.traits;

    enum E { a, b, c };

    struct S { E e; };

    void main()
    {
        immutable(S)[] source = [ S(E.a), S(E.a), S(E.b) ];
        foreach (e; EnumMembers!E)
        {
            size_t c = count!(x => x.e == e)(source);
            writeln(e, " -> ", c);
        }
    }

I would expect the output of this program to be something along the lines of:

    a -> 2
    b -> 1
    c -> 0

But the actual result is:

    a -> 2
    b -> 2
    c -> 2

Curiously, changing the for loop to foreach (e; [ E.a, E.b, E.c ]) produces my
expected output. Using foreach (e; [ EnumMembers!E ]) also produces my
expected result, so clearly my use of the range from EnumMemebers is the
problem here...I just don't know why.

By moving the count call to a separate function:

    size_t counte(Range)(E e, Range src)
    {
        return count!(x => x.e == e)(src);
    }

and changing c's initialization to size_t c = counte(e, source);, the program
works as I would expect.

I am clearly doing something wrong, but I have no idea what and would
appreciate some insight.

My compiler is DMD64 D Compiler v2.059 on Linux.
Jun 19 2012
next sibling parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 06/19/12 16:44, Travis Gockel wrote:
     import std.algorithm;
     import std.stdio;
     import std.traits;
 
     enum E { a, b, c };
 
     struct S { E e; };
 
     void main()
     {
         immutable(S)[] source = [ S(E.a), S(E.a), S(E.b) ];
         foreach (e; EnumMembers!E)
         {
             size_t c = count!(x => x.e == e)(source);
             writeln(e, " -> ", c);
         }
     }
 
 I would expect the output of this program to be something along the lines of:
 
     a -> 2
     b -> 1
     c -> 0
 
 But the actual result is:
 
     a -> 2
     b -> 2
     c -> 2
 
 Curiously, changing the for loop to foreach (e; [ E.a, E.b, E.c ]) produces my
 expected output. Using foreach (e; [ EnumMembers!E ]) also produces my
 expected result, so clearly my use of the range from EnumMemebers is the
 problem here...I just don't know why.
 
 By moving the count call to a separate function:
 
     size_t counte(Range)(E e, Range src)
     {
         return count!(x => x.e == e)(src);
     }
 
 and changing c's initialization to size_t c = counte(e, source);, the program
 works as I would expect.
 
 I am clearly doing something wrong, but I have no idea what and would
 appreciate some insight.  

Yes, it can be surprising, but I'm not convinced it's actually wrong behavior (the bug is http://d.puremagic.com/issues/show_bug.cgi?id=2043) Just do this: size_t c = count!(function(x, e) { return x.e == e;} )(source, e); and it will work. [1] artur [1] I don't do that new kinky lambda syntax, sorry. ;)
Jun 19 2012
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 06/19/2012 05:08 PM, Artur Skawina wrote:
 Yes, it can be surprising, but I'm not convinced it's actually wrong
 behavior (the bug is http://d.puremagic.com/issues/show_bug.cgi?id=2043)

It is not this bug. (And what is listed there is clearly wrong behaviour, because it can be used to break the type system.)
 Just do this:

     size_t c = count!(function(x, e) { return x.e == e;} )(source, e);

 and it will work. [1]

 artur

 [1] I don't do that new kinky lambda syntax, sorry. ;)

Your embarrassment about this issue is justifiable.
Jun 19 2012
prev sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 06/19/12 17:32, Timon Gehr wrote:
 On 06/19/2012 05:08 PM, Artur Skawina wrote:
 Yes, it can be surprising, but I'm not convinced it's actually wrong
 behavior (the bug is http://d.puremagic.com/issues/show_bug.cgi?id=2043)

It is not this bug. (And what is listed there is clearly wrong behaviour, because it can be used to break the type system.)

It's not that simple. I remember considering the alternatives when I originally ran into this, and they have problems too. The "static foreach" case may be special, possibly.
 [1] I don't do that new kinky lambda syntax, sorry. ;)

Your embarrassment about this issue is justifiable.

my compiler doesn't support them... artur
Jun 19 2012
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 06/19/2012 04:44 PM, Travis Gockel wrote:
 I am clearly doing something wrong,

You are not.
 but I have no idea what and would
 appreciate some insight.

You have found a bug in DMD. Reduced test case that should compile: template Seq(T...){alias T Seq;} auto exec(alias a)(){return a();} void main(){ foreach(e; Seq!(0, 1)) static assert(exec!(()=>e)()==e); } You can report the bug here: http://d.puremagic.com/issues/ The 'exec' template is instantiated only once instead of two times.
Jun 19 2012
parent Travis Gockel <travis gockelhut.com> writes:
== Quote from Timon Gehr (timon.gehr gmx.ch)'s article
 You can report the bug here: http://d.puremagic.com/issues/
 The 'exec' template is instantiated only once instead of two times.

Reported: http://d.puremagic.com/issues/show_bug.cgi?id=8267
Jun 19 2012