www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Overloading free functions & run-time dispatch based on parameter types

reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
From the docs:

class A { }
class B : A { }
class C : B { }
void foo(A);
void foo(B);

void test()
{
    C c;
    foo(c); // calls foo(B)
}


I need the other way around. So, at runtime I get an A and depending on 
it's dynamic type, I would like to get the correct foo() called.

class A { }
class B : A { }
class C : A { }
void foo(B);
void foo(C);

void test()
{
    B b;
    A a = b:

    foo(a); // should call foo(B)
}

When doing something like this in my code I get the following compile 
error (message adapted to match pseudo-code):

source/app.d(751): Error: None of the overloads of 'foo' are callable 
using argument types (A), candidates are:
source/app.d(742):        app.foo(B)
source/app.d(746):        app.foo(C)

I hope the problem is understandable.

Is there a way to dispatch a call to the correct free functions based 
on the run-time type of the arguments without writing a switch or so?

-- 
Robert M. Münch
http://www.saphirion.com
smarter | better | faster
Feb 05 2016
next sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Friday, 5 February 2016 at 10:54:27 UTC, Robert M. Münch wrote:
 From the docs:

 class A { }
 class B : A { }
 class C : B { }
 void foo(A);
 void foo(B);

 [...]
sounds like foo should just be a method in the class rather than a free function
Feb 05 2016
parent =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2016-02-05 11:10:36 +0000, Nicholas Wilson said:

 sounds like foo should just be a method in the class rather than a free 
 function
In my particular case I want to keep some stuff outside of claases. -- Robert M. Münch http://www.saphirion.com smarter | better | faster
Feb 05 2016
prev sibling parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
Does the following help?

import std.algorithm.comparison : castSwitch;
import std.stdio;

class A { }
class B : A { }
class C : A { }

auto foo_impl(B b) {
     writeln("called foo(B)");
}
auto foo_impl(C c) {
     writeln("called foo(C)");
}

auto foo(A a) {
     return a.castSwitch!(
         (B b) => foo_impl(b),
         (C c) => foo_impl(c),
     );
}

void main()
{
    B b = new B();
    A a = b;

    foo(a); // called foo(B)
}

With a bit of template magic, you can make it DRY, e.g.

alias foo = buildSwitch!foo_impl;
Feb 05 2016
parent reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Friday, 5 February 2016 at 19:48:45 UTC, Robert M. Münch wrote:
 I thought about it too, but I need it to work with more then 
 one parameter, so I tried this which doesn't work:

 Value nativePlus(Value a, Value b){
  //    not working, runtime exception
  castSwitch!(
      (IntV a) {
        castSwitch!(
            (IntV b) {return new IntV(a.num + b.num);}
        )(b);
      },

      (StringV a) {return new StringV("string plus");},
  )(a);

  // to keep compiler happy when using castSwitch (has no return 
 statement)
  return new UnsetV();
 }
I don't see why this wouldn't work, if you've in fact covered all combinations. One thing that tripped my up was a null references - these are never castable to anything, not even Object. If not all combinations are valid, you can add a lambda that accept Value (or even Object), which is called as a "default" branch.
 and ended with this, which works and is straight forward but 
 maybe not that elegant:

 Value nativePlus(Value a, Value b){

  if(cast(IntV) a && cast(IntV) b)
    return new IntV((cast(IntV)a).num + (cast(IntV)b).num);

  if(cast(StringV) a && cast(StringV) b)
    return new StringV((cast(StringV)a).str ~ 
 (cast(StringV)b).str);

  // if no case was hit (could throw)
  return new UnsetV();
 }

 Can this be written simpler or more elegant?
It's similar to how castSwitch is implemented, though the double casts are inefficient. You could use: if(auto inta = cast(IntV) a) { if(auto intb = cast(IntV) b) { return new IntV(inta.num + intb.num); } } (Again, this can be automated.)
 I read this here: 
 https://github.com/D-Programming-Language/phobos/pull/1266#i
suecomment-53507509 (functional pattern matching) but it seems it won't be
implemented... at the end of the day what I simulate are poor-mans-multimethods
As I read the discussion, it was just decided to defer the more complex version of castSwitch for later, but it wasn't rejected.
Feb 06 2016
parent =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes: