Last active
May 25, 2016 21:30
-
-
Save atilaneves/b40c4d030c70686ffa3b8543018f6a7e to your computer and use it in GitHub Desktop.
Create an object of a voldemort struct type that implements an interface
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import std.traits; | |
template Identity(alias T) { | |
alias Identity = T; | |
} | |
template InterfaceToStruct(T) if(is(T == interface)) { | |
string mixinStr() { | |
import std.conv: to; | |
string str = "struct Impl {\n"; | |
str ~= q{ | |
@property void returnValue(string memberStr, V)(V value) if(!isDelegate!V) { | |
alias member = Identity!(__traits(getMember, T, memberStr)); | |
mixin(memberStr ~ `Impl = ` ~ Parameters!member.stringof ~ `{ return value; };`); | |
} | |
@property void returnValue(string memberStr, V)(V value) if(isDelegate!V) { | |
mixin(memberStr ~ "Impl = value;"); | |
} | |
}; | |
str ~= "\n"; | |
foreach(memberStr; __traits(allMembers, T)) { | |
alias member = Identity!(__traits(getMember, T, memberStr)); | |
alias R = ReturnType!member; | |
static if(isAbstractFunction!member) { | |
// this is the name of the delegate that implements the functionality | |
enum implName = memberStr ~ "Impl"; | |
// special case for return type void | |
static if(is(R == void)) { | |
enum prefix = " if(" ~ implName ~ " !is null) "; | |
} | |
else { | |
enum prefix = " return " ~ | |
implName ~ " is null ? " ~ R.stringof ~ ".init : "; | |
} | |
// body of the "real" function (e.g. popFront) | |
string body_ = implName ~ "("; | |
foreach(i, _; Parameters!member) { | |
body_ ~= "arg" ~ i.to!string ~ ", "; | |
} | |
body_ ~= ");\n"; | |
// declare a delegate member variable to hold the implementation | |
str ~= " " ~ R.stringof ~ ` delegate` ~ Parameters!member.stringof ~ | |
" " ~ implName ~ ";\n"; | |
// declare the "real" function (e.g. popFront) | |
str ~= " " ~ R.stringof ~ ` ` ~ memberStr ~ "("; | |
foreach(i, param; Parameters!member) { | |
str ~= param.stringof ~ " arg" ~ i.to!string ~ ", "; | |
} | |
// fill in the body, forwarding to the implementation delegate | |
// if not null, else return T.init | |
str ~= ") {\n" ~ prefix ~ body_ ~ " }\n"; | |
} | |
} | |
str ~= "}\n"; | |
return str; | |
} | |
enum _str = mixinStr; | |
mixin(_str); // horcrux Voldemort type | |
auto InterfaceToStruct() { | |
return Impl(); | |
} | |
} | |
unittest { | |
import std.range.interfaces: InputRange; | |
import std.range: take, isInputRange; | |
import std.algorithm: equal; | |
auto r = InterfaceToStruct!(InputRange!int); | |
static assert(isInputRange!(typeof(r))); //it's an InputRange alright... | |
static assert(is(typeof(r) == struct)); // and a struct, not an interface | |
// r is now a default InputRange of int, so an infinite range of 0 | |
// front: 0 (int.init) | |
// popFront: does nothing | |
// empty: false (bool.init) | |
assert(equal(r.take(5), [0, 0, 0, 0, 0])); // need `take` since r is infinite | |
r.returnValue!"front" = 42; | |
assert(equal(r.take(5), [42, 42, 42, 42, 42])); // need `take` since r is infinite | |
int i; | |
r.returnValue!"empty" = () { return i++ == 3; }; | |
assert(equal(r, [42, 42, 42])); // r is no longer infinite | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment