Created
July 16, 2012 16:22
-
-
Save anonymous/3123593 to your computer and use it in GitHub Desktop.
Go interface in D
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
/* | |
* Author: Li Jie | |
* Email: cpunion@gmail.com | |
*/ | |
module gointerface; | |
import std.traits; | |
import std.variant; | |
import std.typecons; | |
import std.conv; | |
import std.stdio; | |
private | |
string generateArgs(int count) { | |
if (count == 0) | |
return ""; | |
if (count == 1) | |
return "v1"; | |
return "v" ~ to!(string)(count) ~ ", " ~ generateArgs(count - 1); | |
} | |
private | |
template generateArgsWithType(Types ...) { | |
static if (Types.length == 0) { | |
const char[] generateArgsWithType = ""; | |
} | |
else static if (Types.length == 1) { | |
const char[] generateArgsWithType = Types[0].stringof ~ " v1"; | |
} | |
else { | |
const char[] generateArgsWithType = Types[0].stringof ~ " v" ~ to!(string)(Types.length) ~ ", " ~ generateArgsWithType!(Types[1 .. $]); | |
} | |
} | |
private | |
template defineMember(string className, string fnName, type) { | |
const char[] argsWithType = generateArgsWithType!(ParameterTypeTuple!(type)); | |
const char[] args = generateArgs(ParameterTypeTuple!(type).length); | |
const char[] returnType = (ReturnType!(type)).stringof; | |
const char[] funcDef = returnType ~ " " ~ fnName ~ "(" ~ argsWithType ~ ")"; | |
const char[] defineMember = funcDef ~ "{ return m_value." ~ fnName ~ "(" ~ args ~ ");}"; | |
//pragma(msg, "defineMember: " ~ className ~ "::" ~ defineMember); | |
} | |
private | |
template defineDynamicProxyMember(string className, string fnName, type) { | |
const char[] argsWithType = generateArgsWithType!(ParameterTypeTuple!(type)); | |
const char[] args = generateArgs(ParameterTypeTuple!(type).length); | |
const char[] returnType = (ReturnType!(type)).stringof; | |
const char[] funcDef = returnType ~ " " ~ fnName ~ "(" ~ argsWithType ~ ")"; | |
static if (is(ReturnType!(type) == void)) { | |
const char[] defineDynamicProxyMember = funcDef ~ "{ assert(false, \"not implemented\"); }"; //m_value." ~ fnName ~ "(" ~ args ~ ");}"; | |
} | |
else { | |
const char[] defineDynamicProxyMember = funcDef ~ "{ assert(false, \"not implemented\"); return " ~ returnType ~ ".init;}"; //m_value." ~ fnName ~ "(" ~ args ~ ");}"; | |
} | |
pragma(msg, "defineDynamicProxyMember: " ~ className ~ "::" ~ defineDynamicProxyMember); | |
} | |
private | |
mixin template generateOverloads(alias generateTemplate, string className, string fnName, types...) { | |
//pragma(msg, "==== do generate overloads " ~ fnName ~ ", " ~ typeof(types).stringof); | |
static if (types.length > 0) { | |
//pragma(msg, "#######" ~ defineMember!(fnName, typeof(types[0]))); | |
mixin(generateTemplate!(className, fnName, typeof(types[0]))); | |
static if (types.length > 1) { // FIX compile error | |
mixin generateOverloads!(generateTemplate, className, fnName, types[1 .. $]); | |
} | |
} | |
} | |
private | |
mixin template generateMembers(T, alias generateTemplate, members ...) { | |
//pragma(msg, "== do generate members for " ~ T.stringof ~ ", members: " ~ members.stringof); | |
static if (members.length > 0) { | |
mixin generateOverloads!(generateTemplate, T.stringof, members[0], MemberFunctionsTuple!(T, members[0])); | |
mixin generateMembers!(T, generateTemplate, members[1 .. $]); | |
} | |
} | |
private | |
template isMemberCompatibleWithOtherOverloadMembers(Lhs, Rhs, string member, memberType, otherMemberTypes ...) { | |
static if (otherMemberTypes.length > 0) { | |
const bool isMemberCompatibleWithOtherOverloadMembers = | |
is(memberType == typeof(otherMemberTypes[0])) || | |
isMemberCompatibleWithOtherOverloadMembers!(Lhs, Rhs, member, memberType, otherMemberTypes[1 .. $]); | |
} | |
else { | |
const bool isMemberCompatibleWithOtherOverloadMembers = false; | |
} | |
} | |
private | |
template isMembersCompatible(Lhs, Rhs, string member, memberTypes ...) { | |
static if (memberTypes.length > 0) { | |
static if (__traits(hasMember, Lhs, member)) { | |
const bool isMembersCompatible = | |
isMemberCompatibleWithOtherOverloadMembers!(Lhs, Rhs, member, typeof(memberTypes[0]), MemberFunctionsTuple!(Lhs, member)) && | |
isMembersCompatible!(Lhs, Rhs, member, memberTypes[1 .. $]); | |
} | |
else { | |
const bool isMembersCompatible = false; | |
} | |
} | |
else { | |
const bool isMembersCompatible = true; | |
} | |
} | |
private | |
template isOverloadMemberCompatible(Lhs, Rhs, string member) | |
if (is(Lhs == interface) && is(Rhs == interface)) { | |
const bool isOverloadMemberCompatible = | |
isMembersCompatible!(Lhs, Rhs, member, MemberFunctionsTuple!(Rhs, member)); | |
} | |
private | |
template isOverloadMembersCompatible(Lhs, Rhs, members ...) | |
if (is(Lhs == interface) && is(Rhs == interface)) { | |
static if (members.length > 0) { | |
const bool isOverloadMembersCompatible = | |
isOverloadMemberCompatible!(Lhs, Rhs, members[0]) && | |
isOverloadMembersCompatible!(Lhs, Rhs, members[1 .. $]); | |
} | |
else { | |
const bool isOverloadMembersCompatible = true; | |
} | |
} | |
private | |
template isInterfaceConvertible(Lhs, Rhs) | |
if (is(Lhs == interface) && is(Rhs == interface)) { | |
const bool isInterfaceConvertible = | |
isOverloadMembersCompatible!(Lhs, Rhs, __traits(allMembers, Rhs)); | |
} | |
// FIXME: ugly!! | |
private | |
template isGoInterface(T) { | |
const bool isGoInterface = __traits(compiles, T._isGoInterface); | |
} | |
private | |
interface ValueGetter { | |
void* getValue(); | |
} | |
public | |
struct GoInterface(T) if (is(T == interface)) { | |
private enum {_isGoInterface}; | |
private | |
class StaticProxy(V) : T, ValueGetter { | |
this(V v) { | |
m_value = v; | |
} | |
mixin generateMembers!(T, defineMember, __traits(allMembers, T)); | |
void* getValue() { | |
return cast(void*)m_value; | |
} | |
private V m_value; | |
} | |
private | |
class DynamicProxy(V) : T, ValueGetter { | |
this(V v) { | |
m_value = v; | |
} | |
mixin generateMembers!(T, defineDynamicProxyMember, __traits(allMembers, T)); | |
void* getValue() { | |
return cast(void*)m_value; | |
} | |
private V m_value; | |
} | |
this(V)(GoInterface!(V) v) { | |
this.opAssign!(V)(v); | |
} | |
this(V)(V v) if (!isGoInterface!(V)) { | |
this.opAssign!(V)(v); | |
} | |
//FIXME: MUST add !isGoInterface!(V) to avoid conflit, but isGoInterface implemented sucks. | |
void opAssign(V)(V v) if (!isGoInterface!(V)) { | |
//pragma(msg, "assign for non GoInterface"); | |
/* FIXME: free function not supported | |
* e.g.: | |
* interface IFoo { | |
* void foo(); | |
* } | |
* struct Foo {} | |
* void foo(Foo* foo) {} | |
* GoInterface!IFoo f = new Foo; | |
*/ | |
m_impl = new StaticProxy!(V)(v); | |
} | |
void opAssign(V)(GoInterface!(V) v) if (is(V == interface)) { | |
//pragma(msg, "assign for GoInterface"); | |
static if (isImplicitlyConvertible!(V, T)) { | |
//pragma(msg, V.stringof ~ " can implicitly convert to " ~ T.stringof); | |
m_impl = v.m_impl; | |
} | |
else static if (isImplicitlyConvertible!(T, V)) { | |
//pragma(msg, T.stringof ~ " can implicitly convert to " ~ V.stringof ~ ", try dynamic cast"); | |
if (v.m_impl is null) { | |
m_impl = null; | |
} | |
else { | |
m_impl = cast(T)(v.m_impl); | |
if (m_impl is null) { | |
// dynamic cast failed, try dynamic proxy | |
m_impl = buildDynamicProxy!(V)(v); | |
} | |
} | |
} | |
else { | |
//pragma(msg, "cannot implicitly between " ~ V.stringof ~ " and " ~ T.stringof); | |
static if (isInterfaceConvertible!(V, T)) { | |
//pragma(msg, "generate static proxy to convert " ~ V.stringof ~ " to " ~ T.stringof); | |
m_impl = new StaticProxy!(V)(v.m_impl); | |
} | |
else { | |
//pragma(msg, V.stringof ~ " not compatible " ~ T.stringof ~ ", must dynamic build call proxy"); | |
m_impl = buildDynamicProxy!(V)(v); | |
} | |
} | |
} | |
private | |
T buildDynamicProxy(V)(GoInterface!(V) v) { | |
if (v.m_impl is null) { | |
return null; | |
} | |
auto valueGetter = cast(ValueGetter)v.m_impl; | |
V value = cast(V)valueGetter.getValue(); | |
if (value is null) { | |
return null; | |
} | |
writeln("typeinfo: ", (cast(TypeInfo_Interface)typeid(V)).info.m_offTi); | |
return new DynamicProxy!(V)(v.m_impl); | |
} | |
auto opDispatch(string fn, Args ...)(Args args) { | |
mixin("return m_impl." ~ fn ~ "(args);"); | |
} | |
private T m_impl; | |
} | |
//FIXME: change implementation? | |
public | |
template GoInterface() { | |
alias Variant GoInterface; | |
} | |
void main() { | |
interface IWriter { | |
int Write(byte[] bytes); | |
} | |
interface IReadWriter : IWriter { | |
int Read(byte[] bytes); | |
} | |
class ReadWriter { | |
int Read(byte[] bytes) { | |
writeln("ReadWriter.Read"); | |
return 0; | |
} | |
int Write(byte[] bytes) { | |
writeln("ReadWriter.Write"); | |
return 0; | |
} | |
} | |
class ReadWriterImpl : IReadWriter { | |
int Read(byte[] bytes) { | |
writeln("ReadWriter2.Read"); | |
return 0; | |
} | |
int Write(byte[] bytes) { | |
writeln("ReadWriter2.Write"); | |
return 0; | |
} | |
} | |
{ | |
writeln("=== test basic types"); | |
GoInterface!() p = 3; | |
writeln(p); | |
p = "abc"; | |
writeln(p); | |
} | |
{ | |
writeln("=== test interface basic"); | |
interface IFoo { | |
void foo(int a, string b, float c); | |
} | |
struct Foo { | |
void foo(int a, string b, float c) { | |
writeln("Foo.foo: ", a, ", ", b, ", ", c); | |
} | |
} | |
struct FooFoo { | |
void foo(int a, string b, float c) { | |
writeln("FooFoo.foo: ", a, ", ", b, ", ", c); | |
} | |
} | |
GoInterface!IFoo f = new Foo; | |
f.foo(3, "abc", 2.2); | |
f = new FooFoo; | |
f.foo(5, "def", 7.7); | |
} | |
{ | |
writeln("=== test interface downcast"); | |
GoInterface!IReadWriter readWriter = new ReadWriter; | |
byte[] buf; | |
readWriter.Read(buf); | |
readWriter.Write(buf); | |
GoInterface!IWriter writer = readWriter; | |
writer.Write(buf); | |
assert(!__traits(compiles, writer.Read(buf))); | |
writer = new ReadWriter; | |
writer.Write(buf); | |
assert(!__traits(compiles, writer.Read(buf))); | |
readWriter = new ReadWriterImpl; | |
readWriter.Read(buf); | |
readWriter.Write(buf); | |
writer = readWriter; | |
writer.Write(buf); | |
assert(!__traits(compiles, writer.Read(buf))); | |
writer = new ReadWriterImpl; | |
writer.Write(buf); | |
assert(!__traits(compiles, writer.Read(buf))); | |
} | |
{ | |
writeln("=== test interface downcast"); | |
GoInterface!IReadWriter readWriter = new ReadWriter; | |
byte[] buf; | |
readWriter.Read(buf); | |
readWriter.Write(buf); | |
GoInterface!IWriter writer = readWriter; | |
writer.Write(buf); | |
assert(!__traits(compiles, writer.Read(buf))); | |
writer = new ReadWriter; | |
writer.Write(buf); | |
assert(!__traits(compiles, writer.Read(buf))); | |
} | |
{ | |
writeln("=== test interface dynamic cast"); | |
GoInterface!IWriter writer = new ReadWriter; | |
byte[] buf; | |
writer.Write(buf); | |
assert(!__traits(compiles, writer.Read(buf))); | |
GoInterface!IReadWriter readWriter = writer; | |
//readWriter.Read(buf); | |
//readWriter.Write(buf); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment