module test;
import std.stdio;
import std.traits;
import std.string;
struct TrackChanges(T)
{
T payload;
alias payload this;
void delegate()[] callbacks;
void opAssign(T)(T rhs)
{
auto oldState = payload;
this.payload = rhs;
if (payload != oldState)
notify();
}
void opOpAssign(string op, T)(T rhs)
{
auto oldState = payload;
this.payload.opOpAssign!op(rhs);
if (payload != oldState)
notify();
}
auto opDispatch(string method, Args...)(Args args)
{
auto oldState = payload;
static if (mixin("is(ReturnType!(T." ~ method ~ ") == void)"))
{
mixin("payload." ~ method ~ "(args);");
if (payload != oldState)
notify();
}
else
{
mixin("auto result = payload." ~ method ~ "(args);");
if (payload != oldState)
notify();
return result;
}
}
void addListener(void delegate() callback)
{
this.callbacks ~= callback;
}
void notify()
{
foreach (fun; callbacks)
{
fun();
}
}
}
struct Point
{
void modify(int val)
{
x += val;
y += val;
}
string toString() { return format("Point(%s %s)", x, y); }
int x, y;
}
class Widget
{
this()
{
position.addListener({ writefln("Position changed to: %s", position); });
}
TrackChanges!Point position;
}
void main()
{
auto widget = new Widget;
widget.position = Point(10, 10);
widget.position = Point(10, 10);
widget.position.modify(+1);
}