Structs implementing interfaces in D1

Tom S h3r3tic at remove.mat.uni.torun.pl
Tue Feb 10 15:36:28 PST 2009


Steven Schveighoffer wrote:
> "Tom S" wrote
>> /**
>> The concept is pretty simple. The mixin creates a vtable which points to a 
>> set of generated functions of the form { adjust the 'this' ptr; jump to 
>> the real function; }. Finally, the "InterfaceName asInterfaceName()" 
>> functions generated inside the struct return pointers to the corresponding 
>> vtables.
>>
>> Now this would be so much nicer if there was a way to iterate the 
>> functions of an interface at compile time in D1... Can we have D1 plus 
>> __traits? Pleaaaase? Pretty please with a cherry on top?
>>
>> Thanks to downs, Hxal and Jarrett!
>> */
> 
> very, very cool.

Thanks :)



> However, I don't like the vtable-per-instance penalty.

It's only a vtable-pointer-per-instance cost, but I guess that's what 
you meant.


> Since structs are generally stack variables, why not make the interface a 
> separate stack variable?  Most of the time, you are interested in passing a 
> struct to a function via an interface so the function can use the interface 
> but not save a reference to it.
> 
> (snip)

Great idea! :D I couldn't stop myself from implementing it:


module structIface;

extern (C) int printf(char*, ...);



// ---- evil implementation
char[] structImplementInterface(char[] strname, char[] iface, char[][] 
funcs) {
	char[] res = "private {";
	res ~= \n"alias typeof(*this) _iface_"~iface~"_ThisType;";

	foreach (func; funcs) {
		res ~= \n"static assert (is(typeof(&"~iface~".init."~func~")),
			`The interface "~iface~" doesn't declare a '"~func~"' function, `
			`thus struct `~_iface_"~iface~"_ThisType.stringof~` cannot implement 
it`);";
		res ~= \n"static void _iface_" ~ iface ~ "_func_" ~ func ~ "() {";
		res ~= \n\t"asm { naked; ";
		version (GNU) {
			res ~= "push EAX; mov EAX, dword ptr 8[ESP]; ";
		}
		res ~= "sub EAX, _iface_" ~ iface ~ "_vtbl.offsetof; mov EAX, [EAX]; ";
		version (GNU) {
			res ~= "mov dword ptr 8[ESP], EAX; pop EAX; ";
		}
		res ~= "jmp " ~ strname ~ "." ~ func ~ "; }";
		res ~= \n"}";
	}
	res ~= \n ~ iface ~ " as" ~ iface ~ "() {";
	res ~= \n\t"return cast("~iface~")cast(void*)&_iface_" ~ iface ~ "_vtbl;";
	res ~= \n"}";

	res ~= \n"void** _iface_" ~ iface ~ "_vtbl = cast(void**)([";
	res ~= \n\t"null";		// for classinfo
	foreach (func; funcs) {
		res ~= ",\n\tcast(void*)&_iface_" ~ iface ~ "_func_" ~ func;
	}
	res ~= \n"]).ptr;";
	res ~= "}";

	return res;
}
// ---- end of the evil implementation


interface IFoo {
	void func1();
	void func2();
}

interface IBar {
	void func2();
	void func3(int, char[]);
}


char[][] splitFuncs(char[] str) {
	char[][] res;
	while (str.length > 0) {
		while (str.length > 0 && (' ' == str[0] || ',' == str[0])) {
			str = str[1..$];
		}
		int to = 0;
		for (; to < str.length && str[to] != ' ' && str[to] != ','; ++to) {}
		if (to > 0) {
			res ~= str[0..to];
			str = str[to..$];
		}
	}
	return res;
}


struct Impl_(Intf, Struct, char[] funcs) {
	Struct* thisptr;
	mixin(structImplementInterface(Struct.stringof, Intf.stringof, 
splitFuncs(funcs)));

	static Impl_ opCall(ref Struct s) {
		Impl_ res;
		res.thisptr = &s;
		return res;
	}

	Intf opCast() {
		mixin("return as"~Intf.stringof~"();");
	}
}


template Impl(Intf, char[] funcs) {
	Impl_!(Intf, Struct, funcs) Impl(Struct)(ref Struct s) {
		return Impl_!(Intf, Struct, funcs)(s);
	}
}


struct Foo {
	float val1;
	char[] val2;

	void func1() {
		printf("Foo.func1 called ; val1 = %f, val2 = %.*s"\n, val1, val2);
	}

	void func2() {
		printf("Foo.func2 called ; val1 = %f, val2 = %.*s"\n, val1, val2);
	}

	void func3(int p1, char[] p2) {
		printf("Foo.func3 called ; val1 = %f, val2 = %.*s ; p1 = %d, p2 = 
%.*s"\n, val1, val2, p1, p2);
	}
}



// ---- Steven's example
interface I
{
	int x();
}

int foo(I i)
{
	return i.x;
}

struct S
{
	int x() { return 123456; }
}

void bar()
{
	S s;
	auto si = Impl!(I, "x")(s);
	int res = foo(cast(I)si);
	printf("foo(si) returned %d"\n, res);
}
// ----


void main() {
	printf("Entering main"\n);

	Foo f;
	f.val1 = 3.14159f;
	f.val2 = "Hello, world!";

	auto sfi = Impl!(IFoo, "func1, func2")(f);
	auto fi = cast(IFoo)sfi;

	fi.func1();
	fi.func2();

	auto sbi = Impl!(IBar, "func2, func3")(f);
	auto bi = cast(IBar)sbi;

	bi.func2();
	bi.func3(123, "some parameter");

	bar();

	printf("Leaving main"\n);
}



-- 
Tomasz Stachowiak
http://h3.team0xf.com/
h3/h3r3tic on #D freenode



More information about the Digitalmars-d mailing list