Automatic dynamic dispatch

Jacob Carlborg doob at me.com
Tue Feb 5 23:23:13 PST 2013


On 2013-02-06 05:37, Andrej Mitrovic wrote:
> Someone asked about how to invoke a function with the dynamic type of
> an object. Essentially the user wanted to implement functions external
> to a class without touching the class vtable (he might not have access
> to it if it's in another library), but he explicitly wanted to work on
> the derived type and not the base type, for example:
>
> class A { }
> class B : A { }
> class C : B { }
>
> void foo(B b) { }  // requires B or derived from B, not A
> void foo(C c) { }  // requires C or derived from C, not A
>
> Since all classes have a TypeInfo_Class associated with them, we can
> create a few helper templates which figure out the entire class tree
> from a set of leaf classes, and then tries to dynamically dispatch to
> the appropriate function at runtime.
>
> Here's the code to do just that: http://dpaste.dzfl.pl/8338067b
>
> And pasted here for convenience:
>
> import std.stdio;
> import std.typetuple;
> import std.traits;
> import std.string;
>
> class A { }
> class B : A { }
> class C : B { }
> class D : B { }
>
> template ClassTreeImpl(Leaves...)
> {
>      static if (Leaves.length > 1)
>      {
>          alias TypeTuple!(Leaves[0], BaseClassesTuple!(Leaves[0]),
>                           ClassTreeImpl!(Leaves[1..$])) ClassTreeImpl;
>      }
>      else
>      static if (Leaves.length == 1)
>      {
>          alias TypeTuple!(Leaves[0], BaseClassesTuple!(Leaves[0])) ClassTreeImpl;
>      }
>      else
>      {
>          alias TypeTuple!() ClassTreeImpl;
>      }
> }
>
> template ClassTree(Leaves...)
> {
>      alias DerivedToFront!(NoDuplicates!(ClassTreeImpl!(Leaves))) ClassTree;
> }
>
> void callFunc(alias func, Args...)(Args args)
>      if (Args.length >= 1 && is(Args[0] == class))
> {
>      auto objInfo = typeid(args[0]);
>      foreach (Base; ClassTree!(C, D))
>      {
>          if (objInfo == Base.classinfo)
>          {
>              static if (__traits(compiles,  // avoid CT errors due to
> unrolled static foreach
>                  { return func(cast(Base)(cast(void*)args[0]),
> args[1..$]); }() ))
>              {
>                  return func(cast(Base)(cast(void*)args[0]), args[1..$]);
>              }
>          }
>      }
>
>      assert(0, format("function '%s' is not callable with object of
> dynamic type '%s'",
>                       __traits(identifier, func), objInfo.toString()));
> }
>
> void foo(C c, int x) { writefln("foo(C) : received %s", x); }
> void foo(D d, int x, int y) { writefln("foo(D) : received %s %s", x, y); }
>
> void main()
> {
>      A c = new C;
>      A d = new D;
>      A a = new A;
>
>      callFunc!foo(c, 1);    // ok
>      callFunc!foo(d, 2, 3); // ok
>      callFunc!foo(a, 3);    // will assert at runtime
> }
>
> It would have been a good blog entry, but I don't blog so.. :)

That's pretty cool.

-- 
/Jacob Carlborg


More information about the Digitalmars-d mailing list