Add ProxyOf and TypeDef #300

Merged
merged 7 commits into from Mar 4, 2012
View
@@ -2736,6 +2736,261 @@ unittest
}
/**
+Make proxy for $(D a).
+
+Example:
+----
+struct MyInt
+{
+ private int value;
+ mixin Proxy!value;
+
+ this(int n){ value = n; }
+}
+
+MyInt n = 10;
+
+// Enable operations that original type has.
+++n;
+assert(n == 11);
+assert(n * 2 == 22);
+
+void func(int n) { }
+
+// Disable implicit conversions to original type.
+//int x = n;
+//func(n);
+----
+ */
+mixin template Proxy(alias a)
+{
+ auto ref opEquals(this X, B)(auto ref B b) { return a == b; }
+
+ auto ref opCmp(this X, B)(auto ref B b)
+ if (!is(typeof(a.opCmp(b))) || !is(typeof(b.opCmp(a))))
+ {
+ static if (is(typeof(a.opCmp(b))))
+ return a.opCmp(b);
+ else static if (is(typeof(b.opCmp(a))))
+ return -b.opCmp(a);
+ else
+ return a < b ? -1 : a > b ? +1 : 0;
+ }
+
+ auto ref opCall(this X, Args...)(auto ref Args args) { return a(args); }
+
+ auto ref opCast(T, this X)() { return cast(T)a; }
+
+ auto ref opIndex(this X, D...)(auto ref D i) { return a[i]; }
+ auto ref opSlice(this X )() { return a[]; }
+ auto ref opSlice(this X, B, E)(auto ref B b, auto ref E e) { return a[b..e]; }
+
+ auto ref opUnary (string op, this X )() { return mixin(op~"a"); }
+ auto ref opIndexUnary(string op, this X, D...)(auto ref D i) { return mixin(op~"a[i]"); }
+ auto ref opSliceUnary(string op, this X )() { return mixin(op~"a[]"); }
+ auto ref opSliceUnary(string op, this X, B, E)(auto ref B b, auto ref E e) { return mixin(op~"a[b..e]"); }
+
+ auto ref opBinary (string op, this X, B)(auto ref B b) { return mixin("a "~op~" b"); }
+ auto ref opBinaryRight(string op, this X, B)(auto ref B b) { return mixin("b "~op~" a"); }
+
+ auto ref opAssign (this X, V )(auto ref V v) { return a = v; }
+ auto ref opIndexAssign(this X, V, D...)(auto ref V v, auto ref D i) { return a[i] = v; }
+ auto ref opSliceAssign(this X, V )(auto ref V v) { return a[] = v; }
+ auto ref opSliceAssign(this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) { return a[b..e] = v; }
+
+ auto ref opOpAssign (string op, this X, V )(auto ref V v) { return mixin("a " ~op~"= v"); }
+ auto ref opIndexOpAssign(string op, this X, V, D...)(auto ref V v, auto ref D i) { return mixin("a[i] " ~op~"= v"); }
+ auto ref opSliceOpAssign(string op, this X, V )(auto ref V v) { return mixin("a[] " ~op~"= v"); }
+ auto ref opSliceOpAssign(string op, this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) { return mixin("a[b..e] "~op~"= v"); }
+
+ template opDispatch(string name)
+ {
+ static if (is(typeof(__traits(getMember, a, name)) == function))
+ {
+ // non template function
+ auto ref opDispatch(this X, Args...)(Args args) { return mixin("a."~name~"(args)"); }
+ }
+ else static if (is(typeof(mixin("a."~name))) || __traits(getOverloads, a, name).length != 0)
+ {
+ // field or property function
+ @property auto ref opDispatch(this X)() { return mixin("a."~name); }
+ @property auto ref opDispatch(this X, V)(auto ref V v) { return mixin("a."~name~" = v"); }
+ }
+ else
+ {
+ // member template
+ template opDispatch(T...)
+ {
+ auto ref opDispatch(this X, Args...)(Args args){ return mixin("a."~name~"!T(args)"); }
+ }
+ }
+ }
+}
+unittest
+{
+ static struct MyInt
+ {
+ private int value;
+ mixin Proxy!value;
+ this(int n){ value = n; }
+ }
+
+ MyInt m = 10;
+ static assert(!__traits(compiles, { int x = m; }));
+ static assert(!__traits(compiles, { void func(int n){} func(m); }));
+ assert(m == 10);
+ assert(m != 20);
+ assert(m < 20);
+ assert(+m == 10);
+ assert(-m == -10);
+ assert(++m == 11);
+ assert(m++ == 11); assert(m == 12);
+ assert(--m == 11);
+ assert(m-- == 11); assert(m == 10);
+ assert(cast(double)m == 10.0);
+ assert(m + 10 == 20);
+ assert(m - 5 == 5);
+ assert(m * 20 == 200);
+ assert(m / 2 == 5);
+ assert(10 + m == 20);
+ assert(15 - m == 5);
+ assert(20 * m == 200);
+ assert(50 / m == 5);
+ m = 20; assert(m == 20);
+}
+unittest
+{
+ static struct MyArray
+ {
+ private int[] value;
+ mixin Proxy!value;
+ this(int[] arr){ value = arr; }
+ }
+
+ MyArray a = [1,2,3,4];
+ assert(a == [1,2,3,4]);
+ assert(a != [5,6,7,8]);
+ assert(+a[0] == 1);
+ version (LittleEndian)
+ assert(cast(ulong[])a == [0x0000_0002_0000_0001, 0x0000_0004_0000_0003]);
+ else
+ assert(cast(ulong[])a == [0x0000_0001_0000_0002, 0x0000_0003_0000_0004]);
+ assert(a ~ [10,11] == [1,2,3,4,10,11]);
+ assert(a[0] == 1);
+ //assert(a[] == [1,2,3,4]);
+ //assert(a[2..4] == [3,4]);
+ a = [5,6,7,8]; assert(a == [5,6,7,8]);
+ a[0] = 0; assert(a == [0,6,7,8]);
+ a[] = 1; assert(a == [1,1,1,1]);
+ a[0..3] = 2; assert(a == [2,2,2,1]);
+ a[0] += 2; assert(a == [4,2,2,1]);
+ a[] *= 2; assert(a == [8,4,4,2]);
+ a[0..2] /= 2; assert(a == [4,2,4,2]);
+}
+unittest
+{
+ class Foo
+ {
+ int field;
+
+ @property const int val1(){ return field; }
+ @property void val1(int n){ field = n; }
+
+ @property ref int val2(){ return field; }
+
+ const int func(int x, int y){ return x; }
+
+ T opCast(T)(){ return T.init; }
+
+ T tempfunc(T)() { return T.init; }
+ }
+ class Hoge
+ {
+ Foo foo;
+ mixin Proxy!foo;
+ this(Foo f) { foo = f; }
+ }
+
+ auto h = new Hoge(new Foo());
+ int n;
+
+ // field
+ h.field = 1; // lhs of assign
+ n = h.field; // rhs of assign
+ assert(h.field == 1); // lhs of BinExp
+ assert(1 == h.field); // rhs of BinExp
+ assert(n == 1);
+
+ // getter/setter property function
+ h.val1 = 4;
+ n = h.val1;
+ assert(h.val1 == 4);
+ assert(4 == h.val1);
+ assert(n == 4);
+
+ // ref getter property function
+ h.val2 = 8;
+ n = h.val2;
+ assert(h.val2 == 8);
+ assert(8 == h.val2);
+ assert(n == 8);
+
+ // member function
+ assert(h.func(2,4) == 2);
+
+ // bug5896 test
+ assert(h.opCast!int() == 0);
+ assert(cast(int)h == 0);
+ immutable(Hoge) ih = new immutable(Hoge)(new Foo());
+ static assert(!__traits(compiles, ih.opCast!int()));
+ static assert(!__traits(compiles, cast(int)ih));
+
+ // template member function
+ assert(h.tempfunc!int() == 0);
+
+ //assert(h.TempFunc2!int.tempfunc2!double("a") == tuple(0, double.nan, "a"));
+}
+
+/**
+Library typedef.
+ */
+template Typedef(T)
+{
+ alias .Typedef!(T, T.init) Typedef;
+}
+
+/// ditto
+struct Typedef(T, T init, string cookie=null)
+{
+ private T Typedef_payload = init;
+
+ this(T init)
+ {
+ Typedef_payload = init;
+ }
+
+ mixin Proxy!Typedef_payload;
+}
+
+unittest
+{
+ Typedef!int x = 10;
+ static assert(!__traits(compiles, { int y = x; }));
+ static assert(!__traits(compiles, { long z = x; }));
+
+ Typedef!int y = 10;
+ assert(x == y);
+
+ Typedef!(float, 1.0) z; // specifies the init
+ assert(z == 1.0);
+
+ alias Typedef!(int, 0, "dollar") Dollar;
+ alias Typedef!(int, 0, "yen") Yen;
+ static assert(!is(Dollar == Yen));
+}
+
+
+/**
Allocates a $(D class) object right inside the current scope,
therefore avoiding the overhead of $(D new). This facility is unsafe;
it is the responsibility of the user to not escape a reference to the