# Callback ABI Top-level V3 procedures are now **C-callable**: - no hidden static-link parameter; - arguments and result follow the C/QBE ABI; - `ADR(Proc)` yields a function pointer usable through a C `typedef` / `void *`; - `short real`, `CBool`, C strings and C-layout record pointers work in callback signatures. Example: ```modula2 PROCEDURE add (a, b : INTEGER) : INTEGER; BEGIN RETURN a + b END add; PROCEDURE call_add (fn : ADDRESS; a, b : INTEGER) : INTEGER; EXTERNAL "m2_c_call_add"; ... call_add(ADR(add), 20, 22) ``` The C side is: ```c int m2_c_call_add(void *fn, int a, int b) { int (*f)(int, int) = (int (*)(int, int))fn; return f(a, b); } ``` Covered by `compiler/tests/t_callback.mod`. ## Nested procedures Nested procedures still take a hidden parent-frame link and are **not** C-callable. Keep callbacks at module level, or use a small C/Modula-2 trampoline with a context record. ## GTK/GObject pattern A `GtkClosures`-style design now works without a C wrapper for the V3 procedure itself: ```modula2 PROCEDURE Tramp2 (instance, ctx : ADDRESS); ``` The C side registers `Tramp2` as the signal callback and passes the `ctx` pointer as user data. Keep allocation, destroy handling and `user_data` ownership in a context object, as in the existing `GtkClosures` design. ## Still not supported - V3 instance methods as direct C callbacks; they still carry a hidden receiver, so use a top-level trampoline. - Nested procedure values. - C varargs callbacks. - C struct-by-value callback arguments/results; use pointers or a C shim.