Automatic dynamic dispatch
Zhenya
zheny at list.ru
Wed Feb 6 04:19:18 PST 2013
On Wednesday, 6 February 2013 at 04:37:17 UTC, 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.. :)
Hi!
When I read your post I remembered,that some time ago I had
written in D
an n-dimensional dispatcher simular to the one,described by
Andrei in Modern C++ Design.But my dispatcher allow casting of
arguments:
import std.stdio;
class Foo {}
class Bar : Foo {}
class Gun {}
void main()
{
Dispatcher!2 d;
d.add((Foo f,Gun g) {writeln("FooXGun")});
d(new Bar,new Gun);//prints FooXGun,because Bar can be
converted to Foo
}
Maybe my code has more mixin's than needed,but it works,and it
has some disadvantages,but
I would be glad if it was useful:
http://dpaste.dzfl.pl/2ff50a12
More information about the Digitalmars-d
mailing list