Jump to page: 1 2
Thread overview
writing iterators without code duplication. inout?
Dec 21, 2011
pompei2
Dec 21, 2011
Trass3r
Dec 21, 2011
pompei2
Dec 21, 2011
Timon Gehr
Dec 21, 2011
pompei2
Dec 21, 2011
Jakob Ovrum
Dec 21, 2011
Jakob Ovrum
Dec 21, 2011
Christophe
Dec 22, 2011
pompei2
Dec 22, 2011
Andrew Wiley
December 21, 2011
Hello.

I want to add the option to iterate objects of my class using foreach. I need them to be iterable as view-only const and as mutable too. I would prefer to iterate using the "return a delegate" but if that's not possible, ranges are fine too. Also, I'd prefer a template-less solution over a templated one.


This is what I have, which works but has severe code duplication. I hoped inout would help me here, but I just can't figure it out. I also gave a try to ranges, but same thing again: I can only get it to work if I define my things twice.

(syntax highlight for the coming month: http://pastebin.com/TNmWWgsj)


   import std.conv, std.stdio;

   class Container
   {
       this(int from, int to)
       {
           while(from <= to) {
               _arr ~= from;
               from++;
           }
       }

       // FIXME: severe code duplication for const/nonconst/ref
       //        and I'm not even trying immutable yet
       int delegate(int delegate(ref int)) doIter()
       {
           writeln("calling non-const");

           int doTheIter(int delegate(ref int) dg)
           {
               int result = 0;
               foreach(ref i ; this._arr) {
                   result = dg(i);
                   if(result)
                       break;
               }
               return result;
           }

           return &doTheIter;
       }

       int delegate(int delegate(ref int)) doIter() const
       {
           writeln("calling const");

           int doTheIter(int delegate(ref int) dg)
           {
               int result = 0;
               foreach(i ; this._arr) {
                   result = dg(i);
                   if(result)
                       break;
               }
               return result;
           }

           return &doTheIter;
       }

       int[] _arr;
   }

   int main(string[] args)
   {
       Container c = new Container(1, 9);
       const Container cc = c;

       foreach(ref e ; c.doIter()) {
           writeln(e);
           e++;
       }

       foreach(e ; cc.doIter()) {
           writeln(e);
       }

       return 0;
   }

December 21, 2011
Can't really answer your original question, but
1. Why don't you use opApply?
2. Why do you use ref int even in the const version?
3. You could also use alias this to allow iteration, don't know if that's what you want in general though.
December 21, 2011
On 12/21/2011 04:54 PM, pompei2 wrote:
> Hello.
>
> I want to add the option to iterate objects of my class using foreach. I
> need them to be iterable as view-only const and as mutable too. I would
> prefer to iterate using the "return a delegate" but if that's not
> possible, ranges are fine too. Also, I'd prefer a template-less solution
> over a templated one.
>
>
> This is what I have, which works but has severe code duplication. I
> hoped inout would help me here, but I just can't figure it out. I also
> gave a try to ranges, but same thing again: I can only get it to work if
> I define my things twice.
>
> (syntax highlight for the coming month: http://pastebin.com/TNmWWgsj)
>
>
> import std.conv, std.stdio;
>
> class Container
> {
> this(int from, int to)
> {
> while(from <= to) {
> _arr ~= from;
> from++;
> }
> }
>
> // FIXME: severe code duplication for const/nonconst/ref
> // and I'm not even trying immutable yet
> int delegate(int delegate(ref int)) doIter()
> {
> writeln("calling non-const");
>
> int doTheIter(int delegate(ref int) dg)
> {
> int result = 0;
> foreach(ref i ; this._arr) {
> result = dg(i);
> if(result)
> break;
> }
> return result;
> }
>
> return &doTheIter;
> }
>
> int delegate(int delegate(ref int)) doIter() const
> {
> writeln("calling const");
>
> int doTheIter(int delegate(ref int) dg)
> {
> int result = 0;
> foreach(i ; this._arr) {
> result = dg(i);
> if(result)
> break;
> }
> return result;
> }
>
> return &doTheIter;
> }
>
> int[] _arr;
> }
>
> int main(string[] args)
> {
> Container c = new Container(1, 9);
> const Container cc = c;
>
> foreach(ref e ; c.doIter()) {
> writeln(e);
> e++;
> }
>
> foreach(e ; cc.doIter()) {
> writeln(e);
> }
>
> return 0;
> }
>

Just remove the non-const overload. const member functions work with mutable, immutable and const receivers.
December 21, 2011
> Just remove the non-const overload. const member functions work with mutable, immutable and const receivers.

Doing this, it compiles but it doesn't do what it should in two ways:

1. I leave the code as-is. It compiles but "e" in the first foreach loop in "main" is a copy, not a reference. This shows by the second loop still displaying the original values, not the +1 values.

2. To fix 1., I can change the "i" in the foreach loop in "doTheIter" to "ref i", which will allow the first foreach loop in "main" to successfully modify the array elements. But now, add the line "e++;" into the second loop and it still compiles! Change it to "ref e" (still in the second loop) and add a third loop and it actually does modify the elements! It shouldn't be allowed to, as "cc" is const and const is supposed to be transitive. I'm not even using a cast.

So either this is not the solution, or I'm doing it wrong.
December 21, 2011
On Wednesday, 21 December 2011 at 16:07:55 UTC, Timon Gehr wrote:
> Just remove the non-const overload. const member functions work with mutable, immutable and const receivers.

The const version does not allow using 'ref' when iterating.
December 21, 2011
On Wednesday, 21 December 2011 at 16:05:24 UTC, Trass3r wrote:
> Can't really answer your original question, but
> 1. Why don't you use opApply?
> 2. Why do you use ref int even in the const version?
> 3. You could also use alias this to allow iteration, don't know if that's what you want in general though.

1&3: Because there are different things in my class to iterate over. Think foreach(p ; obj.properties()) and foreach(c ; obj.components()). (I know, I can make those @property so I don't need the ().)

2. Because if not, it says: Error: cannot implicitly convert expression (__foreachbody1315) of type int delegate(ref int) to int delegate(int)

December 21, 2011
On Wednesday, 21 December 2011 at 16:31:01 UTC, Jakob Ovrum wrote:
> On Wednesday, 21 December 2011 at 16:07:55 UTC, Timon Gehr wrote:
>> Just remove the non-const overload. const member functions work with mutable, immutable and const receivers.
>
> The const version does not allow using 'ref' when iterating.

In light of pompei2's recent reply;

the const version *should not* allow using 'ref' when iterating.
December 21, 2011
"pompei2" , dans le message (digitalmars.D.learn:31164), a écrit :
> This is what I have, which works but has severe code duplication. I hoped inout would help me here, but I just can't figure it out. I also gave a try to ranges, but same thing again: I can only get it to work if I define my things twice.


It's not optimal, and there is an ugly cast, but maybe this is a suitable workarrond for you :

  int delegate(int delegate(ref int)) doIter() const
    {
      return (int delegate(ref int) dg)
        {
          cast(typeof(this))(this).doIter()((ref int i) { int copy = i; dg(copy); });
        }
    }


Until
  int delegate(ref inout int) opApply() inout;
and
  int delegate(int delegate(ref inout int)) doIter() inout;
are made to work. (I actually don't know if there is any obstacles to do
this).
December 22, 2011
> int delegate(int delegate(ref int)) doIter() const
>   {
>74      return (int delegate(ref int) dg)
>       {
>         cast(typeof(this))(this).doIter()
>77            (
>78              (ref int i)
>               {
>                 int copy = i; dg(copy);
>               }
>           );
>       }	
>   }

I see what you are doing there. Unfortunately, the innermost delegate (the one doing the copy trick) somehow got @system attribute, which leads to the following compile errors (I added line numbers and reordered your code in the citation above):

constiter.d(78): Error: cannot implicitly convert expression (__dgliteral2) of type void delegate(ref int i) @system to int delegate(ref int)
constiter.d(77): Error: cast has no effect in expression (cast(const(Container))this.doIter()((__error)))
constiter.d(74): Error: cannot implicitly convert expression (__dgliteral1) of type void delegate(int delegate(ref int) dg) @system to int delegate(int delegate(ref int))

Where does this @system come from? How do I get rid of it?

> Until
> int delegate(ref inout int) opApply() inout;
> and
> int delegate(int delegate(ref inout int)) doIter() inout;
> are made to work. (I actually don't know if there is any obstacles to do this).

Can you point me to the bugreport so I can vote for it? I'm not sure if it is #4838 or if they are unrelated.
December 22, 2011
On Thu, Dec 22, 2011 at 12:04 AM, pompei2 <pompei2@gmail.com> wrote:
>> int delegate(int delegate(ref int)) doIter() const
>>  {
>> 74      return (int delegate(ref int) dg)
>>      {
>>        cast(typeof(this))(this).doIter()
>> 77            (
>> 78              (ref int i)
>>
>>              {
>>                int copy = i; dg(copy);
>>              }
>>          );
>>      }
>>  }
>
>
> I see what you are doing there. Unfortunately, the innermost delegate (the one doing the copy trick) somehow got @system attribute, which leads to the following compile errors (I added line numbers and reordered your code in the citation above):
>
> constiter.d(78): Error: cannot implicitly convert expression (__dgliteral2)
> of type void delegate(ref int i) @system to int delegate(ref int)
> constiter.d(77): Error: cast has no effect in expression
> (cast(const(Container))this.doIter()((__error)))
> constiter.d(74): Error: cannot implicitly convert expression (__dgliteral1)
> of type void delegate(int delegate(ref int) dg) @system to int delegate(int
> delegate(ref int))
>
> Where does this @system come from? How do I get rid of it?

Actually, it looks like your issue is here:
constiter.d(78): Error: cannot implicitly convert expression
(__dgliteral2) of type *void* delegate(ref int i) @system to int
delegate(ref int)

void delegate(ref int) != int delegate(ref int)
« First   ‹ Prev
1 2