User defined type and foreach

Jim Balter Jim at Balter.name
Thu Jan 25 08:57:56 UTC 2024


On Friday, 19 January 2024 at 18:13:55 UTC, Jonathan M Davis 
wrote:
> On Friday, January 19, 2024 3:49:29 AM MST Jim Balter via 
> Digitalmars-d-learn wrote:
>> On Friday, 17 November 2017 at 17:55:30 UTC, Jonathan M Davis
>>
>> wrote:
>> > When you have
>> >
>> > foreach(e; range)
>> >
>> > it gets lowered to something like
>> >
>> > for(auto r = range; !r.empty; r.popFront())
>> > {
>> >
>> >     auto e = r.front;
>> >
>> > }
>> >
>> > So, the range is copied when you use it in a foreach.
>>
>> Indeed, and the language spec says so, but this is quite wrong 
>> as
>> it violates the specification and design of ranges ... only
>> forward ranges are copyable and only via their `save` 
>> function. I
>> have an input range that can only be iterated once; if you try 
>> to
>> do so again it's empty ... but the foreach implementation 
>> breaks
>> that. You should be able to break out of the foreach statement,
>> then run it again (or another foreach) and it should continue
>> from where it left off, but copying breaks that. I need to know
>> how many elements of my range were consumed; copying breaks 
>> that.
>> I got around this by having a pointer to my state so only the
>> pointer gets copied. I would also note that tutorials such as 
>> Ali
>> Çehreli's "Programming in D – Tutorial and Reference" are 
>> unaware
>> of this breakage:
>>
>> "
>> Those three member functions must be named as empty, popFront,
>> and front, respectively. The code that is generated by the
>> compiler calls those functions:
>>
>>      for ( ; !myObject.empty(); myObject.popFront()) {
>>
>>          auto element = myObject.front();
>>
>>          // ... expressions ...
>>      }
>> "
>

"No spec is being violated, and the behavior is completely 
expected."

This is not true, and your over-long response that is a lecture I 
didn't ask for and don't need misses my point and is largely 
irrelevant. The specification of ranges, which is independent of 
the D language, says that the way to copy a range is to use 
save(). Input ranges cannot be copied or restarted; that's the 
whole point of the difference between input ranges and forward 
ranges. You go on and on about the semantics of copying, which 
has nothing to do with what I wrote, which is that `foreach` 
copies when it shouldn't; the semantics of copying is not 
relevant to *not copying*. Input ranges are one-shots; they yield 
values once and that's it. `foreach` should have been implemented 
to call `save()` when that is available (when it is a forward 
range), and to do no copying when it is not available. Had that 
been done, then people wouldn't have written a bunch of ranges 
with missing save() functions that are reusable when they 
shouldn't be due to `foreach` making a copy, making `foreach` 
impossible to fix now.

"Basic input ranges cannot be value types (otherwise, they could 
implement save)"

This is not true, and again misses the point. My input range, the 
one I described in my original message, is a value type (a 
struct) which, when passed to functions, is passed by reference. 
It doesn't implement save because it's an input range, not a 
forward range. It's not supposed to be copied or restartable 
because it's an input range, not a forward range. Copying it has 
the wrong consequences because calling popFront on a copy doesn't 
advance the one-shot input range. It would work just fine if 
`foreach` didn't copy it. It works fine if I use it without 
`foreach`, e.g., if I use it in a for loop like in Ali's book:

>>    for ( ; !myObject.empty(); myObject.popFront()) {
>>
>>          auto element = myObject.front();
>>
>>          // ... expressions ...
>>    }

That works just fine with an input range, which is a range that 
is not restartable.
The only thing that doesn't work with my input range is 
`foreach`, which copies the input range--which, by the 
specification of input ranges, was never intended.

I managed to make it usable with a `foreach` loop by having the 
struct only store a pointer to its state so the `foreach` copy is 
harmless, in effect turning my value type range into a reference 
type and introducing unnecessary overhead.


More information about the Digitalmars-d-learn mailing list