Thread overview
Overloading free functions & run-time dispatch based on parameter types
Feb 05, 2016
Robert M. Münch
Feb 05, 2016
Nicholas Wilson
Feb 05, 2016
Robert M. Münch
Feb 05, 2016
Marc Schütz
Feb 05, 2016
Robert M. Münch
Feb 06, 2016
Marc Schütz
Feb 07, 2016
Robert M. Münch
February 05, 2016
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

February 05, 2016
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
February 05, 2016
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

February 05, 2016
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;
February 05, 2016
On 2016-02-05 15:23:53 +0000, Marc Schütz said:

> Does the following help?
> ...

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();
}


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?

I read this here: https://github.com/D-Programming-Language/phobos/pull/1266#issuecomment-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

-- 
Robert M. Münch
http://www.saphirion.com
smarter | better | faster

February 06, 2016
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#issuecomment-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.
February 07, 2016
On 2016-02-06 14:33:57 +0000, Marc Schütz said:

> I don't see why this wouldn't work, if you've in fact covered all combinations.

It works, the problem was that castSwitch returns something and I didn't "catch" it.

> 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);
>      }
> }

Yes, thanks. Was on my list.

> (Again, this can be automated.)

How? Do you mean by castSwitch?


>> I read this here: https://github.com/D-Programming-Language/phobos/pull/1266#issuecomment-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.

Well... yes, "won't be implemented in the near future" Anyway, it's not available at the moment, so looking at other ways.

-- 
Robert M. Münch
http://www.saphirion.com
smarter | better | faster