On Wed, Oct 31, 2012 at 12:00:50AM +0100, deadalnix wrote: [...] > It appears to me that invalidating .front is done for performances > reasons most of the time. As this is the unsafe face of the coin, I > strongly think that this shouldn't be the default behavior. > > To me the best solution is to provide a function or a property on > ranges that provide the unsafe version of the range. > > It is easy to provide a default implementation as UFCS that is a > NOOP so no code is being broken. > > With that design, it is possible to provide an always safe interface > while allowing algorithm that can handle non persistent .front to > run at full speed.
Hmm. I like this idea! This is much better than my proposal. So let's say we change byLine() to always return a copy of the buffer, so that .front is never invalidated. Then for algorithms that don't care about .front being invalidated, they can do something like this: auto myAlgo(R)(R inputRange) { auto r = inputRange.fastRange; while (!r.empty) { auto x = r.front; // make use of x r.popFront(); // this invalidates x } return result; } void main() { myAlgo(stdin.byLine()); } We can use UFCS so that fastRange is always defined: // Default implementation auto fastRange(R)(R range) { return range; } For byLine(), then, we have: struct ByLine(...) { // safe implementation @property auto fastRange() { struct UnsafeByLine(...) { ... } return UnsafeByLine(this); } } I like this. Very clean, and doesn't break backward compatibility, and allows select algorithms to be optimized for speed without affecting everything else. [...] > If I use your permuting example, it should be done as follow : > > struct InvalidatingAllPermutations(T) { > T[] current, first; > bool done; > > this(T[] initialBuf) { > first = initialBuf; > current = first.dup; > } > > void popFront() { > nextPermutation(current); > if (equal(current, first)) > done = true; > } > > @property bool empty() { > return !done; > } > > @property T[] front() { > return current; > } > > auto save() { > AllPermutations!T copy; > copy.front = this.front; > copy.first = this.first; > copy.done = this.done; > return copy; > } > } > > struct AllPermutations(T) { > InvalidatingAllPermutations!T innerRange; > alias innerRange this; > > T[] current; > > this(T[] initialBuf) { > innerRange = InvalidatingAllPermutations!T(initialBuf); > current = innerRange.front.dup; > } > > @property T[] front() { > return current; > } > > void popFront() { > innerRange.popFront(); > current = innerRange.front.dup; > } > } > > I do think this is the way that conciliate safe as default but still > allow to be fast when needed, without adding much on most code. +1. I like this better than my proposal. T -- Doubtless it is a good thing to have an open mind, but a truly open mind should be open at both ends, like the food-pipe, with the capacity for excretion as well as absorption. -- Northrop Frye