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.

Reply via email to