Skip to content

Commit

Permalink
Fix generate to be correctly implemented range.
Browse files Browse the repository at this point in the history
  • Loading branch information
schveiguy committed Jul 7, 2016
1 parent c4b3973 commit 5271e13
Showing 1 changed file with 60 additions and 21 deletions.
81 changes: 60 additions & 21 deletions std/range/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -3156,31 +3156,27 @@ function, delegate, struct type defining static $(D opCall)... ) or
a run-time value argument (delegate, function object... ).
The result range models an InputRange
($(REF isInputRange, std,range,primitives)).
The resulting range will call $(D fun()) on every call to $(D front),
and only when $(D front) is called, regardless of how the range is
iterated.
It is advised to compose generate with either
$(REF cache, std,algorithm,iteration) or $(REF array, std,array), or to use it in a
foreach loop.
A by-value foreach loop means that the loop value is not $(D ref).
The resulting range will call $(D fun()) on construction, and every call to
$(D popFront), and the cached value returned when $(D front) is called.
Params:
fun = is the $(D isCallable) that gets called on every call to front.
Returns: an $(D inputRange) that returns a new value generated by $(D Fun) on
any call to $(D front).
Returns: an $(D inputRange) that returns a range where each element represents
another call to fun.
*/
auto generate(Fun)(Fun fun)
if (isCallable!fun)
{
return Generator!(Fun)(fun);
auto gen = Generator!(Fun)(fun);
gen.popFront(); // prime the first element
return gen;
}

/// ditto
auto generate(alias fun)()
if (isCallable!fun)
{
return Generator!(fun)();
auto gen = Generator!(fun)();
gen.popFront(); // prime the first element
return gen;
}

///
Expand Down Expand Up @@ -3219,7 +3215,6 @@ unittest
format("%(%s %)", r);
}

//private struct Generator(bool onPopFront, bool runtime, Fun...)
private struct Generator(Fun...)
{
static assert(Fun.length == 1);
Expand All @@ -3231,18 +3226,42 @@ private:
else
alias fun = Fun[0];

import std.traits: ReturnType, functionAttributes, FunctionAttribute;
enum returnByRef_ = (functionAttributes!fun & FunctionAttribute.ref_) ? true : false;
static if(returnByRef_)
ReturnType!fun *elem_;
else
ReturnType!fun elem_;
public:
/// Range primitives
enum empty = false;

/// ditto
auto ref front() @property
static if(returnByRef_)
{
return fun();
/// ditto
ref front() @property
{
return *elem_;
}
/// ditto
void popFront()
{
elem_ = &fun();
}
}
else
{
/// ditto
auto front() @property
{
return elem_;
}
/// ditto
void popFront()
{
elem_ = fun();
}
}

/// ditto
void popFront() {}
}

@safe unittest
Expand Down Expand Up @@ -3270,6 +3289,26 @@ public:
assert(equal(generate(op).take(10), repeat(5).take(10)));
}

// verify ref mechanism works
@system unittest
{
int[10] arr;
int idx;

ref int fun() {
auto x = idx++;
idx %= arr.length;
return arr[x];
}
int y = 1;
foreach(ref x; generate!(fun).take(20))
{
x += y++;
}
import std.algorithm.comparison : equal;
assert(equal(arr[], iota(12, 32, 2)));
}

/**
Repeats the given forward range ad infinitum. If the original range is
infinite (fact that would make $(D Cycle) the identity application),
Expand Down

0 comments on commit 5271e13

Please sign in to comment.