More tricky range semantics

H. S. Teoh via Digitalmars-d digitalmars-d at puremagic.com
Thu Jan 15 16:46:24 PST 2015


On Thu, Jan 15, 2015 at 03:24:29PM -0800, Andrei Alexandrescu via Digitalmars-d wrote:
> On 1/15/15 12:53 PM, H. S. Teoh via Digitalmars-d wrote:
> >Passing a range to a function may invalidate it unless you use .save.
> >Therefore, one should *always*  use .save.
> 
> That's right. To simplify the problem space we might decree that
> forward (or better) ranges with reference semantics are not allowed.
> -- Andrei

That's not workable. It instantly makes InputRangeObject useless.

InputRangeObject is required in situations where run-time polymorphism
of ranges is needed, for example, if a particular function must return
one of multiple possible range types depending on a runtime parameter.
Simple example:

	auto wrapMyRange(R)(R range) {
		if (someRuntimeCondition) {
			return range.map!(a => a*2);
		} else {
			return range.map!(a => a*2 + 1)
				    .filter!(a > 10);
		}
	}

This code doesn't compile, of course, because the return types of map()
and filter() are incompatible. The only current way to make it work is
to wrap the return value in an InputRangeObject:

	auto wrapMyRange(R)(R range) {
		if (someRuntimeCondition) {
			return inputRangeObject(range.map!(a => a*2));
		} else {
			return inputRangeObject(range.map!(a => a*2 + 1)
				.filter!(a > 10));
		}
	}

This works because inputRangeObject returns an instance of a subclass of
InputRangeObject, which serves as the common return type. The concrete
type is specialized for the actual type being wrapped; in this case
either the return type of map(), or the return type of filter(). Note
that methods like .save, .popBack, etc., are forwarded, so the returned
range retains forward or higher range functionality. This is, of course,
necessary, since otherwise you couldn't do anything with the returned
wrapped range except iterate over it once.

Forcing reference type ranges to be non-forward completely breaks this
important use case. The only way to work around it would be to wrap the
class object in a struct wrapper that calls .save in its postblit --
which introduces a prohibitive performance overhead.


T

-- 
Let's not fight disease by killing the patient. -- Sean 'Shaleh' Perry


More information about the Digitalmars-d mailing list