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