Thread overview | ||||||||
---|---|---|---|---|---|---|---|---|
|
February 06, 2013 Automatic dynamic dispatch | ||||
---|---|---|---|---|
| ||||
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.. :) |
February 06, 2013 Re: Automatic dynamic dispatch | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrej Mitrovic | 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 |
February 06, 2013 Re: Automatic dynamic dispatch | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrej Mitrovic | 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 |
February 06, 2013 Re: Automatic dynamic dispatch | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrej Mitrovic | 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.. :)
Wiki entry?
|
February 06, 2013 Re: Automatic dynamic dispatch | ||||
---|---|---|---|---|
| ||||
Posted in reply to Paulo Pinto | On 2/6/13, Paulo Pinto <pjmlp@progtools.org> wrote: > Wiki entry? Yeah, but I'm having a real hard time understanding how the wiki works. For example in the http://wiki.dlang.org/Cookbook page, I click on Edit for the Meta Programming section, and I get this: ==== Meta Programming ==== {{#ask: [[Cookbook/Type::Recipe]] [[Category:Meta Programming]] | ?Cookbook/Status= | ?Level= | format=ul | sep=; }} <!--------></td><td style="vertical-align: top; width: 50%;"> I don't get it, where are the titles Combining structs, Creating a subtype using struct template, etc, which are seen when you read the page? |
February 06, 2013 Re: Automatic dynamic dispatch | ||||
---|---|---|---|---|
| ||||
On 2/6/13, Andrej Mitrovic <andrej.mitrovich@gmail.com> wrote: > On 2/6/13, Paulo Pinto <pjmlp@progtools.org> wrote: >> Wiki entry? Anyway it's up now: http://wiki.dlang.org/Dispatching_an_object_based_on_its_dynamic_type |
Copyright © 1999-2021 by the D Language Foundation