Thread overview
How does calling function pointers work?
Nov 12, 2018
helxi
Nov 12, 2018
helxi
Nov 12, 2018
Rene Zwanenburg
Nov 12, 2018
helxi
Nov 12, 2018
Alex
Nov 12, 2018
Mike Parker
Nov 12, 2018
Rene Zwanenburg
November 12, 2018
As far as I understand, calling a function pointer with an argument in D looks like:

    call(&fnptr, argTofn0, argTofn1, argTofn3);

This immediately struck me a very weak syntax to me so I decided to explore my concerns.
I made a function pointer that takes an indefinite number of arguments.

     1  import std.stdio;
     2
     3  int total(int[] numbers ...) {
     4      int result;
     5      for(ulong i = 0; i < numbers.length; result += numbers[i++]){}
     6      return result;
     7  }
     8
     9
    10  void main() {
    11      writeln(total(1000, 200, 30, 4)); // 1234
    12      writeln(&total, 1000, 200, 30, 4); // 55CA386877AC1000200304
    13      writeln((&total, 1000, 200, 30), 4); // error lmao
    14  }

How do you guys make writeln distinguish between an arg meant for writeln from an arg meant for &total?
FYI
Line 12 was meant to print 1234.
Line 13 was meant to print 1234 too, but for a different reason.
November 12, 2018
On Monday, 12 November 2018 at 16:08:28 UTC, helxi wrote:
> Line 12 was meant to print 1234.
> Line 13 was meant to print 1234 too, but for a different reason.

Correction, it was meant to print 12304. My bad.

November 12, 2018
On Monday, 12 November 2018 at 16:08:28 UTC, helxi wrote:
> As far as I understand, calling a function pointer with an argument in D looks like:
>
>     call(&fnptr, argTofn0, argTofn1, argTofn3);

Idk where you got that syntax from, but there's no syntactic difference between calling normal functions and function pointers:

https://run.dlang.io/is/I6u0rg

November 12, 2018
On Monday, 12 November 2018 at 16:25:13 UTC, Rene Zwanenburg wrote:
> Idk where you got that syntax from, but there's no syntactic difference between calling normal functions and function pointers:

import std.stdio;
import std.concurrency;
import core.thread;

void worker(int firstNumber) {
    foreach (i; 0 .. 4) {
        Thread.sleep(500.msecs);
        writeln(firstNumber + i);
    }
}

void main() {
    foreach (i; 1 .. 3) {
        spawn(&worker, i * 10);
    }
}


Looks like worker needs an int and spawn(&worker, i * 10) seems to feed it's second arg to worker(?)
November 12, 2018
On Monday, 12 November 2018 at 16:29:24 UTC, helxi wrote:
> On Monday, 12 November 2018 at 16:25:13 UTC, Rene Zwanenburg wrote:
>> Idk where you got that syntax from, but there's no syntactic difference between calling normal functions and function pointers:
>
> import std.stdio;
> import std.concurrency;
> import core.thread;
>
> void worker(int firstNumber) {
>     foreach (i; 0 .. 4) {
>         Thread.sleep(500.msecs);
>         writeln(firstNumber + i);
>     }
> }
>
> void main() {
>     foreach (i; 1 .. 3) {
>         spawn(&worker, i * 10);
>     }
> }
>
>
> Looks like worker needs an int and spawn(&worker, i * 10) seems to feed it's second arg to worker(?)

Yes, seems so. Accordingly to
https://dlang.org/library/std/concurrency/spawn.html

However, there are more restrictions on input params in the notes section.
November 12, 2018
On Monday, 12 November 2018 at 16:29:24 UTC, helxi wrote:

>
>
> Looks like worker needs an int and spawn(&worker, i * 10) seems to feed it's second arg to worker(?)

spawn is a template that takes a function pointer and a variable number of parameters.  Both the pointer and the parameters are passed on to an internal _spawn function.

https://github.com/dlang/phobos/blob/master/std/concurrency.d#L446

_spawn is has the same template parameters as spawn. It has an internal function that actually makes the call to the function pointer (fn):

    void exec()
    {
        thisInfo.ident = spawnTid;
        thisInfo.owner = ownerTid;
        fn(args);
    }

https://github.com/dlang/phobos/blob/master/std/concurrency.d#L538

A few lines down from there, a pointer to exec is passed to either scheduler.spawn or the Thread constructor. When it's ultimately called, your function will be called in turn.

At any rate, the actual call to the function pointer is fn(args).
November 12, 2018
On Monday, 12 November 2018 at 16:29:24 UTC, helxi wrote:
> On Monday, 12 November 2018 at 16:25:13 UTC, Rene Zwanenburg wrote:
>> Idk where you got that syntax from, but there's no syntactic difference between calling normal functions and function pointers:
>
> import std.stdio;
> import std.concurrency;
> import core.thread;
>
> void worker(int firstNumber) {
>     foreach (i; 0 .. 4) {
>         Thread.sleep(500.msecs);
>         writeln(firstNumber + i);
>     }
> }
>
> void main() {
>     foreach (i; 1 .. 3) {
>         spawn(&worker, i * 10);
>     }
> }
>
>
> Looks like worker needs an int and spawn(&worker, i * 10) seems to feed it's second arg to worker(?)

That's right. spawn() is a function in the standard library that takes a function pointer, and all the arguments to pass to that function. It's a bit unusual in that regard: normally when using function pointers the arguments are provided by the code that receives the function pointer. Internally, spawn will call the function pointer just like I did in my example, but on another thread.

Here's an example where a function pointer is passed around with the arguments provided by the callee:
https://run.dlang.io/is/ArCN5t