Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ProxyOf and TypeDef #300

Merged
merged 7 commits into from Mar 4, 2012
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
255 changes: 255 additions & 0 deletions std/typecons.d
Expand Up @@ -2735,6 +2735,261 @@ unittest
alias RefCounted!S SRC;
}

/**
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;
Expand Down