www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Downgrading ranges

reply "Lars T. Kyllingstad" <public kyllingen.net> writes:
A recent pull request discussion got me thinking:  The ability to 
"downgrade" a range to a less featureful one -- wrapping a random 
access range in an input range, say -- can be very useful 
sometimes, particularly for testing.  The pull request in 
question was Walter's LZ77 module, where he has added an input 
range type solely for the purpose of ensuring that all code paths 
are exercised in unittests.  Coincidentally, I currently find 
myself in need of the exact same functionality for a piece of 
range code I'm working on.

I think this would be a generally useful thing, enough so to 
warrant its inclusion in std.range.  It's also rather trivial to 
implement.  The question is, what is a good API?  I'm thinking a 
single type which can be instantiated differently based on 
template parameters.  It is important that the range never 
aliases itself away to the wrapped type, as that would somewhat 
defeat its purpose.

Example:

enum RangeFeatures
{
     input,
     forward,
     randomAccess,
     ...
}

struct RestrictedRange(Range, RangeFeatures features)
{
     private Range m_range;

     static if (features == RangeFeatures.input)
     {
         auto front()  property { return m_range.front() }
     }
}
Jun 09 2013
next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Sunday, 9 June 2013 at 12:19:47 UTC, Lars T. Kyllingstad wrote:
 A recent pull request discussion got me thinking:  The ability 
 to "downgrade" a range to a less featureful one -- wrapping a 
 random access range in an input range, say -- can be very 
 useful sometimes, particularly for testing.

"Particularly for testing", or *only* for testing? Is there any other use? (I can't think of any non-contrived use case beyond testing). If it's just for testing, would it not be better to just supply a variety of test ranges that implement all combinations of traits?
Jun 09 2013
prev sibling next sibling parent "Lars T. Kyllingstad" <public kyllingen.net> writes:
On Sunday, 9 June 2013 at 12:25:45 UTC, Peter Alexander wrote:
 On Sunday, 9 June 2013 at 12:19:47 UTC, Lars T. Kyllingstad 
 wrote:
 A recent pull request discussion got me thinking:  The ability 
 to "downgrade" a range to a less featureful one -- wrapping a 
 random access range in an input range, say -- can be very 
 useful sometimes, particularly for testing.

"Particularly for testing", or *only* for testing? Is there any other use? (I can't think of any non-contrived use case beyond testing).

I was kind of hoping someone else would come up with examples. :) Jokes aside, maybe it's only good for testing. But testing is important enough.
 If it's just for testing, would it not be better to just supply 
 a variety of test ranges that implement all combinations of 
 traits?

I'm not sure I see the difference.
Jun 09 2013
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 6/9/13, Lars T. Kyllingstad <public kyllingen.net> wrote:
 Example:

 enum RangeFeatures
 {
      input,
      forward,
      randomAccess,
      ...
 }

Well, those are range *types*, not features. So name it RangeType perhaps. Alternatively: enum RangeFeatures { emptyEnum, // e.g. enum bool empty = true; emptyProperty, // e.g. property bool empty() { } indexable, // e.g. range[0] sliceable, // e.g. range[] hasFront, hasPopFront, hasBack, hasPopBack, hasSave, } And then autogenerate all of those somehow. Or just provide simple wrappers ala "toInputRange", "toBidirectionalRange", etc. Long story-short I do think we need these simulation ranges in std.range, exactly for unittesting purposes. There is a lot of throwaway range implementations in unittest blocks in Phobos (and many other libraries) used for the sole purpose of unittesting. It would be much simpler to be able to use "testMyFunction([1, 2, 3].toInputRange)" or something to that effect.
Jun 09 2013
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, June 09, 2013 14:25:44 Peter Alexander wrote:
 On Sunday, 9 June 2013 at 12:19:47 UTC, Lars T. Kyllingstad wrote:
 A recent pull request discussion got me thinking:  The ability
 to "downgrade" a range to a less featureful one -- wrapping a
 random access range in an input range, say -- can be very
 useful sometimes, particularly for testing.

"Particularly for testing", or *only* for testing? Is there any other use? (I can't think of any non-contrived use case beyond testing). If it's just for testing, would it not be better to just supply a variety of test ranges that implement all combinations of traits?

I was working on a good solution for this which would allow you to create a test range with pretty much any combination of range capabilities you wanted (as well as providing a default set of ranges to test with), but some compiler bugs were blocking me at the time, and I haven't gotten back to it yet. I really should finish that up. - Jonathan M Davis
Jun 09 2013
prev sibling next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Sunday, 9 June 2013 at 12:42:10 UTC, Lars T. Kyllingstad wrote:
 On Sunday, 9 June 2013 at 12:25:45 UTC, Peter Alexander wrote:
 If it's just for testing, would it not be better to just 
 supply a variety of test ranges that implement all 
 combinations of traits?

I'm not sure I see the difference.

Ignore me. I'm not thinking.
Jun 09 2013
prev sibling parent "Lars T. Kyllingstad" <public kyllingen.net> writes:
On Sunday, 9 June 2013 at 12:19:47 UTC, Lars T. Kyllingstad wrote:
 A recent pull request discussion got me thinking:  The ability 
 to "downgrade" a range to a less featureful one -- wrapping a 
 random access range in an input range, say -- can be very 
 useful sometimes, particularly for testing.  The pull request 
 in question was Walter's LZ77 module,
 [...]

One solution which has come up in the aforementioned pull request discussion (thanks to Daniel Murphy) is to downcast the result of std.range.inputRangeObject() to the desired interface. Intf!(ElementEncodingType!R) downgrade(alias Intf, R)(R rng) { return inputRangeObject(rng); } auto someInputRange = downgrade!InputRange(someRandAccRange); Maybe this is good enough for testing purposes?
Jun 10 2013