Clarifications on function pointers

The example in the language reference:

// The pub specifier allows the function to be visible when importing.
// Another file can use @import and call sub2
pub fn sub2(a: i8, b: i8) i8 {
    return a - b;
}

// Function pointers are prefixed with `*const `.
const Call2Op = *const fn (a: i8, b: i8) i8;
fn doOp(fnCall: Call2Op, op1: i8, op2: i8) i8 {
    return fnCall(op1, op2);
}

test "function" {
    try expect(doOp(add, 5, 6) == 11);
    try expect(doOp(sub2, 5, 6) == -1);
}

  1. Must the function pointer have the same argument names as the function body?
  2. What are the constraints I must satisfy to use a function pointer? Only the number of arguments, type of arguments, return type, and order of arguments must be satisfied?
1 Like

No, you can even omit them:

const Call2Op = *const fn (i8, i8) i8; // ok

And I would add

  1. Why both
try std.testing.expect(doOp(&sub2, 5, 6) == -1);
try std.testing.expect(doOp(sub2, 5, 6) == -1);

work?

1 Like
  • The destination type and source function must have the same number of arguments of the same types in the same order.
  • The destination and source must have the same calling convention.
    • With the recent changes to calling conventions on master it is okay for the source function to have a different incoming stack alignment if it is a multiple of the destination.
  • The destination and source must have the same destination type, unless the source function is noreturn (in which case any destination return type is okay).

The expression sub2 returns a fn (i8, i8) i8, a function body (which is a comptime-only type). Function bodies can implicitly coerce to function pointers. The expression &sub2 explicitly returns *const fn (i8, i8) i8, a function pointer.

The distinction between sub2 and &sub2 only really matters when the destination is not known (e.g. if you do var s = sub2 you might get a compile error about storing comptime-only type in a runtime variable).

7 Likes

ok, I got it, thanks.