Dynamic language
Adam D. Ruppe
destructionator at gmail.com
Fri Mar 16 07:23:18 PDT 2012
On Friday, 16 March 2012 at 09:12:57 UTC, F i L wrote:
> Alright I give up dammit! How do you use opCall() to make
> a.cool() work?
I was a little unclear... but you'll have to modify
std.variant and/or wrap it.
Here's a solution that wraps it:
==
import std.traits;
import std.variant;
// we want to give a common interface to all functions,
// which is what this wrapper tries to do.
// note it is kinda buggy; this is just a quick example,
// not a finished product
Variant delegate(Variant[]) wrap(alias t)() {
return delegate Variant(Variant[] args) {
ParameterTypeTuple!(typeof(t)) ptt;
foreach(i, param; ptt) {
if(i == args.length)
break; // or throw if you want strictness
// BTW, you can do default params and names too,
// but it takes a fair amount of code. Check
// out my web.d generateWrapper() for this if
// you are interested.
// or get for strictness
ptt[i] = args[i].coerce!(typeof(param));
}
Variant returned;
static if(is(ReturnType!(typeof(t)) == void))
t(ptt);
else
returned = t(ptt);
return returned;
};
}
// here's our variant wrapper...
struct MyVariant {
Variant delegate(Variant[]) callable;
Variant value;
alias value this;
// the main trick here is when we construct or assign,
// we have compile time info about the function. So, we
// use this opportunity to build a delegate to call it
dynamically.
this(T)(T t) {
value = t;
static if(isCallable!T) {
callable = wrap!(t)();
} else {
callable = null;
}
}
// and here, we use the delegate made above
Variant opCall(T...)(T t) {
if(!callable)
throw new Exception("not callable");
Variant[] args;
foreach(i, v; t)
args ~= Variant(v);
return callable(args);
}
MyVariant opAssign(T)(T t) {
// FIXME: copy/paste from constructor because otherwise it
// tries to call a simple delegate instead of passing the
delegate itself..
value = t;
static if(isCallable!T) {
callable = wrap!(t)();
} else {
callable = null;
}
return this;
}
}
// let's test it
import std.stdio;
void main () {
MyVariant v = 10;
//v(); // would throw
v = { writefln("Hello, world!"); };
v(); // says hello!
}
==
One of the big bugs is that the compile conflates
instance opCall, static opCall, and constructors in
such a way that it sometimes calls the wrong one.
This makes calling things with parameters a pain
in the ass, and I hope dmd eventually fixes this.
For instance, add this to the end:
v = function(int a) { writeln("whoa: ", a); };
v(10);
And the stupid compiler thinks v(10) is calling the
struct's constructor again.
Why would you /ever/ want that on an instance?
It also complains if you declare opCall and static opCall
separately. Why?
You can work around this by making MyVariant a class. No
constructor (that apparently doesn't work right either. This
is bug city!), just opAssign.
Then you can do:
==
auto v = new MyVariant();
v = { writefln("Hello, world!"); };
v(); // says hello
v = function(int a) { writeln("whoa: ", a); };
v(10); // says whoa: 10
// look, a std.variant bug too while we're at it
// fix this in std.variant tho, and it will work too
// v("12"); // Type immutable(char)[] does not convert
to int
==
This isn't something I use every day, but even in bug city,
it isn't /too/ hard to make it work in D, including weak
typing and variable argument lists, just like Javascript if
we want to. Or, we can go tougher with minor changes.
D rox despite bugs.
More information about the Digitalmars-d
mailing list