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