summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorNathan Sashihara <21227491+n8sh@users.noreply.github.com>2019-03-23 23:20:26 -0400
committerNathan Sashihara <21227491+n8sh@users.noreply.github.com>2019-03-23 23:41:20 -0400
commit10afe47baec7e9ce8c30f6df39c960d472677e5d (patch)
treefab70d2ac78fd8730c9af0398e9ac040877a8721 /source
parent76f2cd7080c948206d5a1b7486a5dfb95476f684 (diff)
downloadtanya-10afe47baec7e9ce8c30f6df39c960d472677e5d.tar.gz
In tanya.algorithm.iteration.take & retro preserve const/inout for `empty`/`front`/etc.
Diffstat (limited to 'source')
-rw-r--r--source/tanya/algorithm/iteration.d128
1 files changed, 87 insertions, 41 deletions
diff --git a/source/tanya/algorithm/iteration.d b/source/tanya/algorithm/iteration.d
index 907aa1a..422edf2 100644
--- a/source/tanya/algorithm/iteration.d
+++ b/source/tanya/algorithm/iteration.d
@@ -27,6 +27,50 @@ import tanya.meta.transform;
import tanya.range;
import tanya.typecons;
+// These predicates are used to help preserve `const` and `inout` for
+// ranges built on other ranges.
+
+private enum hasInoutFront(T) = is(typeof((inout ref T a) => a.front));
+private enum hasInoutBack(T) = is(typeof((inout ref T a) => a.back));
+private enum hasInoutIndex(T) = is(typeof((inout ref T a, size_t i) => a[i]));
+
+private enum hasConstEmpty(T) = is(typeof(((const T* a) => (*a).empty)(null)) : bool);
+private enum hasConstLength(T) = is(typeof(((const T* a) => (*a).length)(null)) : size_t);
+private enum hasConstSave(T) = is(typeof(((const T* a) => (*a).save())(null)) : T);
+private enum hasConstSlice(T) = is(typeof(((const T* a) => (*a)[0 .. $])(null)) : T);
+
+unittest
+{
+ // Test the definitions.
+ static assert(hasInoutFront!string);
+ static assert(hasInoutBack!string);
+ static assert(hasInoutIndex!string);
+ static assert(hasConstEmpty!string);
+ static assert(hasConstLength!string);
+ static assert(hasConstSave!string);
+ static assert(hasConstSlice!string);
+
+ // Test that Take propagates const/inout correctly.
+ alias TakeString = Take!(string, false);
+ static assert(hasInoutFront!TakeString);
+ static assert(hasInoutBack!TakeString);
+ static assert(hasInoutIndex!TakeString);
+ static assert(hasConstEmpty!TakeString);
+ static assert(hasConstLength!TakeString);
+ static assert(hasConstSave!TakeString);
+ static assert(hasConstSlice!TakeString);
+
+ // Test that Retro propagates const/inout correctly.
+ alias RetroString = Retro!string;
+ static assert(hasInoutFront!RetroString);
+ static assert(hasInoutBack!RetroString);
+ static assert(hasInoutIndex!RetroString);
+ static assert(hasConstEmpty!RetroString);
+ static assert(hasConstLength!RetroString);
+ static assert(hasConstSave!RetroString);
+ static assert(hasConstSlice!RetroString);
+}
+
private struct Take(R, bool exactly)
{
private R source;
@@ -47,11 +91,11 @@ private struct Take(R, bool exactly)
}
}
- @property auto ref front()
- in (!empty)
+ mixin(`@property auto ref front() ` ~ (hasInoutFront!R ? `inout ` : ``) ~
+ `in (!empty)
{
return this.source.front;
- }
+ }`);
void popFront()
in (!empty)
@@ -60,8 +104,8 @@ private struct Take(R, bool exactly)
--this.length_;
}
- @property bool empty()
- {
+ mixin(`@property bool empty() ` ~ (exactly || isInfinite!R || hasConstEmpty!R ? `const ` : ``) ~
+ `{
static if (exactly || isInfinite!R)
{
return length == 0;
@@ -70,11 +114,11 @@ private struct Take(R, bool exactly)
{
return this.length_ == 0 || this.source.empty;
}
- }
+ }`);
static if (exactly || hasLength!R)
{
- @property size_t length()
+ @property size_t length() const
{
return this.length_;
}
@@ -97,18 +141,18 @@ private struct Take(R, bool exactly)
static if (isForwardRange!R)
{
- typeof(this) save()
- {
+ mixin(`typeof(this) save() ` ~ (hasConstSave!R ? `const ` : ``) ~
+ `{
return typeof(this)(this.source.save(), length);
- }
+ }`);
}
static if (isRandomAccessRange!R)
{
- @property auto ref back()
- in (!empty)
+ mixin(`@property auto ref back() ` ~ (hasInoutBack!R ? `inout ` : ``) ~
+ `in (!empty)
{
return this.source[this.length - 1];
- }
+ }`);
void popBack()
in (!empty)
@@ -116,11 +160,11 @@ private struct Take(R, bool exactly)
--this.length_;
}
- auto ref opIndex(size_t i)
- in (i < length)
+ mixin(`auto ref opIndex(size_t i) ` ~ (hasInoutIndex!R ? `inout ` : ``) ~
+ `in (i < length)
{
return this.source[i];
- }
+ }`);
static if (hasAssignableElements!R)
{
@@ -152,12 +196,14 @@ private struct Take(R, bool exactly)
static if (!exactly && hasSlicing!R)
{
- auto opSlice(size_t i, size_t j)
- in (i <= j)
+ static if (is(typeof(length))) alias opDollar = length;
+
+ mixin(`auto opSlice(size_t i, size_t j) ` ~ (hasConstSlice!R ? `const ` : ``) ~
+ `in (i <= j)
in (j <= length)
{
return typeof(this)(this.source[i .. j], length);
- }
+ }`);
}
version (unittest) static assert(isInputRange!Take);
@@ -379,16 +425,16 @@ private struct Retro(Range)
this.source = source;
}
- Retro save()
- {
- return this;
- }
+ mixin(`Retro save() ` ~ (hasConstSave!Range ? `const ` : ``) ~
+ `{
+ return Retro(source.save());
+ }`);
- @property auto ref front()
- in (!empty)
+ mixin(`@property auto ref front() ` ~ (hasInoutBack!Range ? `inout ` : ``) ~
+ `in (!empty)
{
return this.source.back;
- }
+ }`);
void popFront()
in (!empty)
@@ -396,11 +442,11 @@ private struct Retro(Range)
this.source.popBack();
}
- @property auto ref back()
- in (!empty)
+ mixin(`@property auto ref back() ` ~ (hasInoutFront!Range ? `inout ` : ``) ~
+ `in (!empty)
{
return this.source.front;
- }
+ }`);
void popBack()
in (!empty)
@@ -408,38 +454,38 @@ private struct Retro(Range)
this.source.popFront();
}
- @property bool empty()
- {
+ mixin(`@property bool empty() ` ~ (hasConstEmpty!Range ? `const ` : ``) ~
+ `{
return this.source.empty;
- }
+ }`);
static if (hasLength!Range)
{
- @property size_t length()
- {
+ mixin(`@property size_t length() ` ~ (hasConstLength!Range ? `const ` : ``) ~
+ `{
return this.source.length;
- }
+ }`);
}
static if (isRandomAccessRange!Range && hasLength!Range)
{
- auto ref opIndex(size_t i)
- in (i < length)
+ mixin(`auto ref opIndex(size_t i) ` ~ (hasInoutIndex!Range ? `inout ` : ``) ~
+ `in (i < length)
{
return this.source[$ - ++i];
- }
+ }`);
}
static if (hasLength!Range && hasSlicing!Range)
{
alias opDollar = length;
- auto opSlice(size_t i, size_t j)
- in (i <= j)
+ mixin(`auto opSlice(size_t i, size_t j) ` ~ (hasConstSlice!Range ? `const ` : ``) ~
+ `in (i <= j)
in (j <= length)
{
return typeof(this)(this.source[$-j .. $-i]);
- }
+ }`);
}
static if (hasAssignableElements!Range)