dynamic classes and duck typing

Adam D. Ruppe destructionator at gmail.com
Tue Dec 1 06:05:43 PST 2009


On Tue, Dec 01, 2009 at 03:55:31PM +0300, Denis Koroskin wrote:
> I believe there will soon be a library type that would allow that.

Here's my first try. I don't have the the new compiler handy and am in a
rush, so I'm doing it hacky.

With the svn compiler, you should be able to almost run this like you'd
expect.

Running it prints:
	Running statically defined: test(10)
	Running dynamically defined test(0)
	object.Exception: no such method text

My vararg code apparently is broken, but meh.


========

import std.stdio;
import std.variant;
import std.stdarg;

class A { // this is our dynamic class
	void test(int a) {
		writefln("Running statically defined: test(%d)", a);
	}

	// Just like in javascript...
	A delegate(...)[string] dynamicFunctions;

	// Return value tells if we should forward to static methods
	bool dynamicCall(string name, out A ret, ...) {
		if(auto fun = name in dynamicFunctions) {
			ret = (*fun)(_arguments);
			return true;
		}

		return false;
	}

	void dynamicBind(string name, A delegate(...) fun) {
		dynamicFunctions[name] = fun;
	}

	A opDispatch(string a)(...) {
		// If we're assigning a delegate, bind it as a member
		if(_arguments[0] == typeid(A delegate(...))) {
			dynamicBind(a, *(cast(A delegate(...)*)(_argptr)));
			return null;
		}

		// If it is in the dynamic list, run that
		A ret;
		if(dynamicCall(a, ret, _arguments))
			return ret;

		// If not, we'll look it up in our static table

		int arg = va_arg!(int)(_argptr);
		static if(__traits(hasMember, this, a)) {
			A var;


			// gah, I wish auto var = fun() worked when fun returns
			// void.
			static if(__traits(compiles,
			    var = __traits(getMember, this, a)(arg)))
				return __traits(getMember, this, a)(arg);
			else {
				// Could be improved by trying to construct a
				// dynamic instance from the return value,
				// whatever it is
				__traits(getMember, this, a)(arg);
				return null;
			}
		}
		else
			throw new Exception("no such method " ~ a);
	}
}

void main() {
	A a = new A;

	// no dynamically defined members, so this should call the static
	a.opDispatch!("test")(10);

	// dynamically define a member to override the static one
	a.opDispatch!("test")(delegate A(int num) { 
		writefln("Running dynamically defined test(%d)", num);
		return null;
	});

	// see what runs
	a.opDispatch!("test")(20);

	// should throw method not defined
	a.opDispatch!("text")(30);
}

=========

If you have the svn compiler, you should be able to replace those opDispatchs
with
	a.test = 10;
and stuff like that.



There's one thing though: I think the patch checks static stuff first, then
if none of that matches, it forwards to opDispatch.

For this to work like in Javascript, it will need a small change.


1) If opDispatch is defined, forward the method do it
2) If this compiles, do nothing more -- assume the opDispatch handled it
3) If not, do a normal static member lookup

If opDispatch is not defined for the class, do nothing special - treat it
like you normally do in D.


The downside is you must either put a static constraint on what your opDispatch
does (easy - static assert(0); if you don't handle it) or forward to your
static members yourself, but the upside is it lets dynamic method overriding
like I do here.

I think it would be a net positive. Assuming this doesn't work already, of
course.

-- 
Adam D. Ruppe
http://arsdnet.net



More information about the Digitalmars-d mailing list