infix is a modern, lightweight C library that lets you call any C function or create C callbacks at runtime, using simple, human-readable strings to describe the function's signature.
It's designed to be the simplest way to add a dynamic Foreign Function Interface (FFI) to your project, whether you're building a language runtime, a plugin system, or just need to call functions from a dynamically loaded library.
- Human-Readable Signatures: Describe complex C functions with an intuitive string format (e.g.,
"({double, double}, int) -> *char"). - Forward & Reverse Calls: Call C functions ("forward") and create C function pointers that call back into your code ("reverse").
- Simple Integration: Add a single C file and a header directory to your project to get started. No complex dependencies.
- Type Registry: Define, reuse, and link complex, recursive, and mutually-dependent structs by name.
- Security-First Design: Hardened against vulnerabilities with Write XOR Execute (W^X) memory, guard pages, and fuzz testing.
- High Performance: A Just-in-Time (JIT) compiler generates optimized machine code trampolines, making calls nearly as fast as a direct C call after the initial setup.
- Installation Guide: How to build and integrate
infix. - API Quick Reference: A complete reference for the public API.
- The FFI Cookbook: Practical, real-world examples and recipes.
- Signature Language Reference: A deep dive into the signature string format.
- Porting Guide: Instructions for adding support for a new CPU architecture or ABI.
- Contributing Guide: How to contribute to the project.
The heart of infix is its signature string. Here’s how you would call a simple C function:
#include <infix/infix.h>
#include <stdio.h>
// The C function we want to call.
int add(int a, int b) { return a + b; }
int main() {
// 1. Describe the function's signature as a string.
const char* signature = "(int, int) -> int";
// 2. Create a "trampoline"—a JIT-compiled function wrapper.
infix_forward_t* trampoline = NULL;
infix_forward_create(&trampoline, signature, (void*)add, NULL);
infix_cif_func cif = infix_forward_get_code(trampoline);
// 3. Prepare an array of *pointers* to the arguments.
int a = 10, b = 32;
void* args[] = { &a, &b };
int result;
// 4. Call the function through the trampoline.
cif(&result, args);
printf("Result: %d\n", result); // Output: Result: 42
// 5. Clean up.
infix_forward_destroy(trampoline);
return 0;
}infix can also generate native C function pointers that call back into your code. This is perfect for interfacing with C libraries that expect callbacks, like qsort.
#include <infix/infix.h>
#include <stdlib.h>
// The C handler function for our callback.
int compare_integers(const void* a, const void* b) {
return (*(const int*)a - *(const int*)b);
}
void run_qsort_example() {
// 1. Describe the callback's signature.
const char* cmp_sig = "(*void, *void) -> int";
// 2. Create a reverse trampoline.
infix_reverse_t* context = NULL;
infix_reverse_create_callback(&context, cmp_sig, (void*)compare_integers, NULL);
// 3. Get the JIT-compiled C function pointer.
int (*my_comparator)(const void*, const void*) = infix_reverse_get_code(context);
// 4. Use the generated callback with the C library function.
int numbers[] = { 5, 1, 4, 2, 3 };
qsort(numbers, 5, sizeof(int), my_comparator);
// `numbers` is now sorted: [1, 2, 3, 4, 5]
infix_reverse_destroy(context);
}The easiest way to use infix is to add its source directly to your project.
- Copy the
src/andinclude/directories into your project. - Add
src/infix.cto your build system's list of source files. - Add the
include/directory to your include paths. #include <infix/infix.h>in your code.
For more advanced build options, including building as a standalone library with CMake or xmake, see the Building and Integration Guide.
infix is built on three core principles:
- Security First: An FFI library with a JIT is a prime target for vulnerabilities. We defend against these with a multi-layered approach, including strict W^X memory, hardened integer arithmetic, and continuous fuzz testing.
- Performance by Design: FFI overhead should be minimal.
infixseparates the one-time generation cost from the near-zero call-time cost, making it exceptionally fast in high-performance applications when trampolines are cached. - Simplicity and Portability: Platform- and ABI-specific logic is strictly isolated, making the library easy to maintain, simple to integrate, and straightforward to port to new architectures.
infix is rigorously tested on a wide array of operating systems, compilers, and architectures with every commit.
In addition to the CI platforms tested here on Github, I can verify infix builds and passes unit tests on Android/Termux.
To maximize usability for all, infix is dual-licensed under the Artistic License 2.0 and the MIT License. You may choose to use the code under the terms of either license.
At your discretion, all standalone documentation (.md), explanatory text, Doxygen-style documentation blocks, comments, and code examples contained within this repository may be used, modified, and distributed under the terms of the Creative Commons Attribution 4.0 International License (CC BY 4.0). I encourage you to share and adapt the documentation for any purpose (generating an API reference website, creating tutorials, etc.), as long as you give appropriate credit.