ffi 0.6: C-to-sx callback baseline (1-arg + ctx-ptr forms)
93/93 regression tests pass (+ffi-06-callback).
Mirrors the `app->onInputEvent` install pattern from
library/modules/platform/android.sx:
1. (s32) -> s32 — single primitive arg/return
2. (*void, s32) -> s32 — opaque ctx pointer + value
(the onInputEvent shape)
Side effects via two file-level globals so the test observes both
the return value AND state mutation across multiple calls:
- g_callback_hits = N proves the callback fired N times.
- g_callback_sum = sum of args proves each individual call landed
with the correct value.
The ctx-pointer variant casts `*void` back to `*s32` inside the
callback and reads through it (`p.*`), proving the pointer survives
the round-trip with no aliasing weirdness.
This commit is contained in:
59
examples/ffi-06-callback.sx
Normal file
59
examples/ffi-06-callback.sx
Normal file
@@ -0,0 +1,59 @@
|
||||
// Phase 0 baseline (PLAN-FFI.md step 0.6): sx function passed to C
|
||||
// as a function pointer; C invokes it; sx-side observable effect.
|
||||
// Mirrors the `app->onInputEvent` install pattern in
|
||||
// library/modules/platform/android.sx.
|
||||
//
|
||||
// Two arities covered:
|
||||
// 1. (s32) -> s32 — single-arg callback
|
||||
// 2. (*void, s32) -> s32 — pointer + value (onInputEvent shape)
|
||||
//
|
||||
// Plus a side-effect via a global so we can confirm the callback
|
||||
// actually fired (return value + state mutation both observable).
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
#import c {
|
||||
#source "vendors/ffi_callback/ffi_callback.c";
|
||||
};
|
||||
|
||||
ffi_apply_callback :: (cb: (s32) -> s32, value: s32) -> s32 #foreign;
|
||||
ffi_apply_callback2 :: (cb: (*void, s32) -> s32, ctx: *void, v: s32) -> s32 #foreign;
|
||||
|
||||
g_callback_hits : s32 = 0;
|
||||
g_callback_sum : s32 = 0;
|
||||
|
||||
double_it :: (x: s32) -> s32 {
|
||||
g_callback_hits += 1;
|
||||
g_callback_sum += x;
|
||||
x * 2;
|
||||
}
|
||||
|
||||
add_with_ctx :: (ctx: *void, v: s32) -> s32 {
|
||||
g_callback_hits += 1;
|
||||
// Pass a sentinel via ctx to prove the pointer arg also survives the
|
||||
// round-trip — read it back as an s32 through *s32.
|
||||
p : *s32 = xx ctx;
|
||||
p.* + v;
|
||||
}
|
||||
|
||||
main :: () -> s32 {
|
||||
// ── Single-arg callback ────────────────────────────────────────
|
||||
r1 := ffi_apply_callback(double_it, 21);
|
||||
print("callback returned = {}\n", r1);
|
||||
print("hits after first call = {}\n", g_callback_hits);
|
||||
print("sum after first call = {}\n", g_callback_sum);
|
||||
|
||||
// Two more calls confirm the same fn pointer keeps working.
|
||||
ffi_apply_callback(double_it, 7);
|
||||
ffi_apply_callback(double_it, 11);
|
||||
print("hits after three calls = {}\n", g_callback_hits);
|
||||
print("sum after three calls = {}\n", g_callback_sum);
|
||||
|
||||
// ── Two-arg callback with opaque ctx pointer ───────────────────
|
||||
ctx_val : s32 = 100;
|
||||
r2 := ffi_apply_callback2(add_with_ctx, xx @ctx_val, 42);
|
||||
print("ctx + value = {}\n", r2);
|
||||
print("hits after ctx callback = {}\n", g_callback_hits);
|
||||
|
||||
0;
|
||||
}
|
||||
1
tests/expected/ffi-06-callback.exit
Normal file
1
tests/expected/ffi-06-callback.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
7
tests/expected/ffi-06-callback.txt
Normal file
7
tests/expected/ffi-06-callback.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
callback returned = 42
|
||||
hits after first call = 1
|
||||
sum after first call = 21
|
||||
hits after three calls = 3
|
||||
sum after three calls = 39
|
||||
ctx + value = 142
|
||||
hits after ctx callback = 4
|
||||
9
vendors/ffi_callback/ffi_callback.c
vendored
Normal file
9
vendors/ffi_callback/ffi_callback.c
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "ffi_callback.h"
|
||||
|
||||
int ffi_apply_callback(int (*cb)(int), int value) {
|
||||
return cb(value);
|
||||
}
|
||||
|
||||
int ffi_apply_callback2(int (*cb)(void *ctx, int v), void *ctx, int v) {
|
||||
return cb(ctx, v);
|
||||
}
|
||||
13
vendors/ffi_callback/ffi_callback.h
vendored
Normal file
13
vendors/ffi_callback/ffi_callback.h
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// C-to-sx callback FFI baseline. C takes a function pointer + a value,
|
||||
// invokes the callback with the value, and returns whatever the callback
|
||||
// returned. Mirrors the `app->onInputEvent` pattern in
|
||||
// library/modules/platform/android.sx where sx installs a handler that
|
||||
// native_app_glue invokes from its input-event loop.
|
||||
|
||||
int ffi_apply_callback(int (*cb)(int), int value);
|
||||
|
||||
// Two-arg variant — the actual chess-on-Android shape:
|
||||
// the callback receives a pointer + a value (mirrors
|
||||
// onInputEvent(app, event) where both are opaque pointers from
|
||||
// the C caller's point of view).
|
||||
int ffi_apply_callback2(int (*cb)(void *ctx, int v), void *ctx, int v);
|
||||
Reference in New Issue
Block a user