The Final(ize) Challenge

Jarrett Billingsley jarrett.billingsley at gmail.com
Mon May 18 14:12:30 PDT 2009


On Mon, May 18, 2009 at 12:12 PM, Andrei Alexandrescu
<SeeWebsiteForEmail at erdani.org> wrote:
> Ok, now with the advent (or rediscovery) of allMembers which I had no idea
> existed, we're ready to start some serious butt-kick reflection facilities.
>
> For starters, I'd like to present you with the following challenge. Given
> any class C, e.g.:
>
> class C
> {
>    void foo(int) { ... }
>    int bar(string) { ... }
> }
>
> define a template class Finalize(T) such that Finalize!(C) is the same as
> the following hand-written class:
>
> final class FinalizeC : C
> {
>    final void foo(int a) { return super.foo(a); }
>    final int bar(string a) { return super.bar(a); }
> }
>
> Finalize is cool when you need some functionality from an exact class and
> you don't want to pay indirect calls throughout. All calls through
> Finalize!(C)'s methods will resolve to static calls.
>
>
> Have at it!

module foo;

import std.conv;
import std.bind;
import std.stdio;
import std.traits;
import std.metastrings;

string ParamTypes(func)()
{
	auto s = func.stringof;

	int begin = s.length - 1;
	int nesting = 0;

	for(; begin > 0; begin--)
	{
		if(s[begin] == ')')
			nesting++;
		else if(s[begin] == '(')
		{
			nesting--;

			if(nesting == 0)
				break;
		}
	}

	return s[begin .. $];
}

string ParamList(func)()
{
	auto s = ParamTypes!(func)()[1 .. $ - 1]; // strip off parens
	string ret = " ";

	// could have weird spaces in the type, so look for a comma and get
the text from the last space until it
	size_t lastSpace = 0;

	foreach(i, c; s)
	{
		if(c == ' ')
			lastSpace = i;
		else if(c == ',')
			ret ~= s[lastSpace + 1 .. i] ~ ",";
	}

	if(s.length - lastSpace > 0)
		ret ~= s[lastSpace + 1 .. $] ~ ",";

	return ret[0 .. $ - 1]; // chop off trailing comma
}

template VirtualOverloadImpl(string methodName, alias method)
{
	enum VirtualOverloadImpl = "override " ~ ReturnType!(method).stringof
~ " " ~ methodName ~ ParamTypes!(typeof(&method))() ~
	"{ return super." ~ methodName ~ "(" ~ ParamList!(typeof(&method))() ~ "); }";
}

string VirtualOverloads(T, string method)()
{
	string ret = "";

	foreach(overload; __traits(getVirtualFunctions, T, method))
	{
		ret ~= "override " ~ ReturnType!(overload).stringof ~ " " ~ method ~
ParamTypes!(typeof(&overload))() ~
		"{ return super." ~ method ~ "(" ~ ParamList!(typeof(&overload))() ~ "); }\n";
	}

	return ret;
}

template FinalizeMembers(T, size_t idx = 0)
{
	static if(idx >= __traits(allMembers, T).length)
		alias void FinalizeMembers;
	else
	{
		static if(__traits(isVirtualFunction, __traits(getMember, T,
__traits(allMembers, T)[idx])) &&
			!__traits(isFinalFunction, __traits(getMember, T,
__traits(allMembers, T)[idx])))
		{
			mixin(VirtualOverloads!(T, __traits(allMembers, T)[idx])());
		}

		mixin FinalizeMembers!(T, idx + 1);
	}
}

final class Finalize(T) : T
{
	// this(T...)(T args) if(is(typeof(new T(args)))) { super(args); }
	mixin FinalizeMembers!T;
}

class C
{
// 	this(int x) { writefln("new C: %s", x); }
	void foo(float x) { writeln(x); }
	void foo(char c) { writeln(c); }
	int bar(string s) { return to!int(s); }
}

void main()
{
	auto c = new C();
	c.foo(4.9);
	writeln(c.bar("5"));

	auto c2 = new Finalize!(C)();
	c2.foo(4.9);
	writeln(c2.bar("5"));
}


Weird bug: if one of your methods takes a single int parameter (no
more, no less, and it has to be 'int', no other basic type triggers
it), DMD inserts a spurious pair of parens in the .stringof, as in
"void function((int))", causing the parameter type and name extraction
to fail.



More information about the Digitalmars-d mailing list