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