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