Skip to content

Instantly share code, notes, and snippets.

Created July 16, 2012 16:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/3123593 to your computer and use it in GitHub Desktop.
Save anonymous/3123593 to your computer and use it in GitHub Desktop.
Go interface in D
/*
* 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