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. π
fngets called with the values that you pulled out of the pointers sitting in theavaluearray. that's the whole vibe β the array is just a bunch of pointers to your actual arguments.- whatever
fnreturns goes into the storage thatrvaluepoints at. π cifis the little info packet that describes the data types, the sizes, and the alignments of both the arguments going intofnand the return value coming out of it. and listen β it must be initialized withffi_prep_cifbefore you ever hand it toffi_call. no prep, no call. fr fr.
β οΈ big important one, do not skip this:rvaluehas to point to storage that issizeof(ffi_arg)or larger for non-floating point types. and if your return value type is smaller than that, you have to use theffi_argorffi_sargintegral 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
- alpine 3.22: libffi-doc 3.4.8-r0 Β· MIT
- alpine 3.23: libffi-doc 3.5.2-r0 Β· MIT
- alpine 3.24: libffi-doc 3.5.2-r1 Β· MIT
- arch rolling: libffi 3.8.0-1 Β· MIT
- centos-stream 10: libffi-devel 3.4.4-10.el10 Β· MIT
- centos-stream 9: libffi-devel 3.4.2-8.el9 Β· MIT
- centos-stream 9: libffi-devel 3.4.2-8.el9 Β· MIT
- debian bookworm: libffi-dev 3.4.4-1
- debian trixie: libffi-dev 3.4.8-2
- fedora 43: libffi-devel 3.5.1-2.fc43 Β· MIT AND CC-PDDC AND (GPL-3.0-or-later WITH Texinfo-exception)
- fedora 43: libffi-devel 3.5.1-2.fc43 Β· MIT AND CC-PDDC AND (GPL-3.0-or-later WITH Texinfo-exception)
- fedora 43: libffi-devel 3.5.2-1.fc43 Β· MIT AND CC-PDDC AND (GPL-3.0-or-later WITH Texinfo-exception)
- fedora 43: libffi-devel 3.5.2-1.fc43 Β· MIT AND CC-PDDC AND (GPL-3.0-or-later WITH Texinfo-exception)
- fedora 44: libffi-devel 3.5.2-2.fc44 Β· MIT AND CC-PDDC AND (GPL-3.0-or-later WITH Texinfo-exception)
- fedora 44: libffi-devel 3.5.2-2.fc44 Β· MIT AND CC-PDDC AND (GPL-3.0-or-later WITH Texinfo-exception)
- nixos 26.05: ruby3.3-ffi 1.17.2
- nixos 26.05: ruby3.4-ffi 1.17.2
- nixos 26.05: ruby4.0-ffi 1.17.2
- nixpkgs unstable: libffi 40
- nixpkgs unstable: ruby3.4-ffi 1.17.2
- nixpkgs unstable: ruby4.0-ffi 1.17.2
- opensuse-leap 16.0: libffi-devel 3.4.6-160000.2.2 Β· MIT
- opensuse-leap 16.0: libffi-devel 3.4.6-160000.2.2 Β· MIT
- opensuse-leap 16.0: libffi-devel 3.4.6-160000.2.2 Β· MIT
- opensuse-leap 16.0: libffi-devel 3.4.6-160000.2.2 Β· MIT
- ubuntu jammy: libffi-dev 3.4.2-4
- ubuntu noble: libffi-dev 3.4.6-1build1
- ubuntu questing: libffi-dev 3.5.2-1build1
- ubuntu resolute: libffi-dev 3.5.2-4
- ubuntu stonking: libffi-dev 3.8.0-2
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms