Skip to content

Commit

Permalink
Refactor lockstep and simplify opApply string mixin generator.
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrejMitrovic committed Feb 8, 2013
1 parent eec793e commit c0713f1
Showing 1 changed file with 52 additions and 82 deletions.
134 changes: 52 additions & 82 deletions std/range.d
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ module std.range;
public import std.array;
import core.bitop, core.exception;
import std.algorithm, std.conv, std.exception, std.functional,
std.traits, std.typecons, std.typetuple;
std.traits, std.typecons, std.typetuple, std.string;

// For testing only. This code is included in a string literal to be included
// in whatever module it's needed in, so that each module that uses it can be
Expand Down Expand Up @@ -4607,87 +4607,58 @@ unittest
assert(equal(z2, [tuple(7, 0L)]));
}

/* CTFE function to generate opApply loop for Lockstep.*/
private string lockstepApply(Ranges...)(bool withIndex) if (Ranges.length > 0)
/*
Generate lockstep's opApply function as a mixin string.
If withIndex is true prepend a size_t index to the delegate.
*/
private string lockstepMixin(Ranges...)(bool withIndex)
{
// Since there's basically no way to make this code readable as-is, I've
// included formatting to make the generated code look "normal" when
// printed out via pragma(msg).
string ret = "int opApply(scope int delegate(";

if (withIndex)
{
ret ~= "size_t, ";
}

foreach (ti, Type; Ranges)
{
static if(hasLvalueElements!Type)
{
ret ~= "ref ";
}

ret ~= "ElementType!(Ranges[" ~ to!string(ti) ~ "]), ";
}

// Remove trailing ,
ret = ret[0..$ - 2];
ret ~= ") dg) {\n";

// Shallow copy _ranges to be consistent w/ regular foreach.
ret ~= "\tauto ranges = _ranges;\n";
ret ~= "\tint res;\n";
string[] params;
string[] emptyChecks;
string[] dgArgs;
string[] popFronts;

if (withIndex)
{
ret ~= "\tsize_t index = 0;\n";
}

// Check for emptiness.
ret ~= "\twhile("; //someEmpty) {\n";
foreach(ti, Unused; Ranges)
{
ret ~= "!ranges[" ~ to!string(ti) ~ "].empty && ";
params ~= "size_t";
dgArgs ~= "index";
}
// Strip trailing &&
ret = ret[0..$ - 4];
ret ~= ") {\n";

// Create code to call the delegate.
ret ~= "\t\tres = dg(";
if (withIndex)
foreach (idx, Range; Ranges)
{
ret ~= "index, ";
params ~= format("ref ElementType!(Ranges[%s])", idx);
emptyChecks ~= format("!ranges[%s].empty", idx);
dgArgs ~= format("ranges[%s].front", idx);
popFronts ~= format("ranges[%s].popFront();", idx);
}

return format(
q{
int opApply(scope int delegate(%s) dg)
{
auto ranges = _ranges;
int res;
%s

foreach(ti, Range; Ranges)
{
ret ~= "ranges[" ~ to!string(ti) ~ "].front, ";
}

// Remove trailing ,
ret = ret[0..$ - 2];
ret ~= ");\n";
ret ~= "\t\tif(res) break;\n";
foreach(ti, Range; Ranges)
{
ret ~= "\t\tranges[" ~ to!(string)(ti) ~ "].popFront();\n";
}

if (withIndex)
{
ret ~= "\t\tindex++;\n";
}

ret ~= "\t}\n";
ret ~= "\tif(_s == StoppingPolicy.requireSameLength) {\n";
ret ~= "\t\tforeach(range; ranges)\n";
ret ~= "\t\t\tenforce(range.empty);\n";
ret ~= "\t}\n";
ret ~= "\treturn res;\n}";
while (%s)
{
res = dg(%s);
if (res) break;
%s
%s
}

return ret;
if (_stoppingPolicy == StoppingPolicy.requireSameLength)
{
foreach(range; ranges)
enforce(range.empty);
}
return res;
}
}, params.join(", "), withIndex ? "size_t index = 0;" : "",
emptyChecks.join(" && "), dgArgs.join(", "),
popFronts.join("\n "),
withIndex ? "index++;" : "").outdent();
}

/**
Expand Down Expand Up @@ -4726,22 +4697,21 @@ private string lockstepApply(Ranges...)(bool withIndex) if (Ranges.length > 0)
struct Lockstep(Ranges...)
if (Ranges.length > 1 && allSatisfy!(isInputRange, Ranges))
{
private:
alias R = Ranges;
R _ranges;
StoppingPolicy _s;

public:
this(R ranges, StoppingPolicy s = StoppingPolicy.shortest)
this(R ranges, StoppingPolicy sp = StoppingPolicy.shortest)
{
_ranges = ranges;
enforce(s != StoppingPolicy.longest,
enforce(sp != StoppingPolicy.longest,
"Can't use StoppingPolicy.Longest on Lockstep.");
this._s = s;
_stoppingPolicy = sp;
}

mixin(lockstepApply!(Ranges)(false));
mixin(lockstepApply!(Ranges)(true));
mixin(lockstepMixin!Ranges(false));
mixin(lockstepMixin!Ranges(true));

private:
alias R = Ranges;
R _ranges;
StoppingPolicy _stoppingPolicy;
}

// For generic programming, make sure Lockstep!(Range) is well defined for a
Expand Down

0 comments on commit c0713f1

Please sign in to comment.