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