opApply not called for foeach(container)

kenji hara k.hara.pg at gmail.com
Wed Jul 11 08:10:16 PDT 2012


I think the online documentation
(http://dlang.org/statement.html#ForeachStatement) is not sufficient.

foreach (e; aggr) { ...body...}

Current dmd translates above foreach statement like follows.

1. If aggr has opApply or opApplyReverse, it's used.

2. If aggr has empty/front/popFront:
 2a. If aggr has slice operation, it's translated to:

  for (auto __r = aggr[];  // If aggr is a container (e.g. std.container.Array),
                           // foreach will get its range object for
the iteration.
      !__r.empty;
      __r.popFront()) { auto e = __r.front; ...body... }

 2b. If aggr doesn't have slice operation, it's translated to:

  for (auto __r = aggr;  // If aggr is copyable, saves the original range.
      !__r.empty;
      __r.popFront()) { auto e = __r.front; ...body... }

3. If aggr is static or dynamic array, it's translated to:

  for (auto __tmp = aggr[], __key = 0;  // If aggr is static array,
get its slice for iteration.
      !__key < __tmp.length;
      ++__key) { auto e = __tmp[__key]; ...body... }

These come from the dmd source code.
https://github.com/D-Programming-Language/dmd/blob/master/src/opover.c#L1226
https://github.com/D-Programming-Language/dmd/blob/master/src/statement.c#L1522

Bye

Kenji Hara

2012/7/11 monarch_dodra <monarch_dodra at gmail.com>:
> If you create a class/struct that can give you a (forward) range via
> "opSlice()", and that range gives you access to "opApply", then you get two
> different behaviors:
>
> ----
> MyClass arr = ...;
>
> foreach(a; arr)
>    ...
>
> foreach(a; arr[])
>    ...
>
> ----
> In the first case, foreach will call opSlice(), and then walk through the
> resulting arr[] range with the front/popFront/empty troika.
>
> In the second case, foreach will call opApply on the range "arr[]".
>
> ----
> I'm wondering if this is the correct behavior? In particular, since foreach
> guarantees a call to opSlice(), so writing "arr[]" *should* be redundant,
> yet the final behavior is different.
>
> That said, the "issue" *could* be fixed if the base class defines opApply
> as: "return opSlice().opApply(dg)" (or more complex). However:
> a) The implementer of class has no obligation to do this, since he has
> provided a perfectly valid range.
> b) This would force implementers into more generic useless boilerplate code.
>
> What are your thoughts? Which is the "correct" solution? Is it a bug with
> foreach, or should the base struct/class provide an opApply?
>
> PS: Related: "foreach over range (with opApply) should save range."
> http://d.puremagic.com/issues/show_bug.cgi?id=4347
> On this assigned issue, is the conclusion that foreach will eventually save
> the range?


More information about the Digitalmars-d mailing list