www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Automatic dynamic dispatch

reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
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.. :)
Feb 05 2013
next sibling parent Jacob Carlborg <doob me.com> writes:
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
Feb 05 2013
prev sibling next sibling parent "Zhenya" <zheny list.ru> writes:
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
Feb 06 2013
prev sibling parent reply "Paulo Pinto" <pjmlp progtools.org> writes:
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?
Feb 06 2013
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
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?
Feb 06 2013
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
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
Feb 06 2013