FFI_CALL(3)

πŸ’– ffi_call(3) β€” bestie, let's call a foreign function πŸ’–

okay so this is the manual page for ffi_call from the ffi family, section 3 of the manual (yes, the library functions section, we love her). she's the one who actually presses the button and makes the foreign function go. no cap. 🫢


✨ NAME ✨

ffi_call β€” invoke a foreign function. that's it, that's the whole job, and she does it flawlessly. πŸ’…


✨ SYNOPSIS ✨

#include <ffi.h>

void
ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue);

✨ DESCRIPTION ✨

so here's the tea, bestie: ffi_call is your little shortcut for calling a function when you literally have no idea what that function's interface looks like at compile time. you don't have to know the types, you don't have to know the arity, you just... call it. iconic behaviour honestly. πŸ‘‘

  • fn gets called with the values that you pulled out of the pointers sitting in the avalue array. that's the whole vibe β€” the array is just a bunch of pointers to your actual arguments.
  • whatever fn returns goes into the storage that rvalue points at. πŸŽ€
  • cif is the little info packet that describes the data types, the sizes, and the alignments of both the arguments going into fn and the return value coming out of it. and listen β€” it must be initialized with ffi_prep_cif before you ever hand it to ffi_call. no prep, no call. fr fr.
⚠️ big important one, do not skip this: rvalue has to point to storage that is sizeof(ffi_arg) or larger for non-floating point types. and if your return value type is smaller than that, you have to use the ffi_arg or ffi_sarg integral type to hold the return value. this is not a suggestion, this is the rules. πŸ’€

✨ EXAMPLES ✨

here's a whole lil program, she's cute, she works, copy her exactly: 🌸

#include <ffi.h>
#include <stdio.h>

unsigned char
foo(unsigned int, float);

int
main(int argc, const char **argv)
{
    ffi_cif cif;
    ffi_type *arg_types[2];
    void *arg_values[2];
    ffi_status status;

    // Because the return value from foo() is smaller than sizeof(long), it
    // must be passed as ffi_arg or ffi_sarg.
    ffi_arg result;

    // Specify the data type of each argument. Available types are defined
    // in <ffi/ffi.h>.
    arg_types[0] = &ffi_type_uint;
    arg_types[1] = &ffi_type_float;

    // Prepare the ffi_cif structure.
    if ((status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI,
        2, &ffi_type_uint8, arg_types)) != FFI_OK)
    {
        // Handle the ffi_status error.
    }

    // Specify the values of each argument.
    unsigned int arg1 = 42;
    float arg2 = 5.1;

    arg_values[0] = &arg1;
    arg_values[1] = &arg2;

    // Invoke the function.
    ffi_call(&cif, FFI_FN(foo), &result, arg_values);

    // The ffi_arg 'result' now contains the unsigned char returned from foo(),
    // which can be accessed by a typecast.
    printf("result is %hhu", (unsigned char)result);

    return 0;
}

// The target function.
unsigned char
foo(unsigned int x, float y)
{
    unsigned char result = x - y;
    return result;
}

see how she did it? arg_types gets filled with &ffi_type_uint and &ffi_type_float, ffi_prep_cif sets up the cif with FFI_DEFAULT_ABI, a count of 2, and &ffi_type_uint8 as the return type, and if that status isn't FFI_OK you go handle the error like a responsible main character. then arg_values[0] points at arg1 (which is 42) and arg_values[1] points at arg2 (which is 5.1), and ffi_call(&cif, FFI_FN(foo), &result, arg_values) does the thing. πŸ¦‹

and note the comment, because it's the same rule as up in DESCRIPTION: result is declared as an ffi_arg because the return value of foo() is smaller than sizeof(long). then it gets typecast back to (unsigned char) at the printf. chef's kiss. 😌


✨ SEE ALSO ✨

go read those too, they're the supporting cast and they ate. 🧚


mwah πŸ’‹ this page is from the ffi manual, dated February 15, 2008, and it's page ffi_call(3) β€” thanks for scrolling all the way down, ily, now go prep your cif before you call anything. πŸ’–βœ¨πŸŽ€

πŸ“¦ where this page comes from