Here's the answer to your question, then I'll continue with the GNOME widgets.
C unions in GNU Modula-2 (as done in this project)
Modula-2 has no C union type, so the bindings use one of three idioms depending on how C uses the type:
- Opaque handle (ADDRESS) — when C only ever passes it by address. This is what almost all GTK/GObject types use:
TYPE AdwViewSwitcher = ADDRESS;
- Fixed-size opaque blob (the union/struct carrier) — when Modula-2 must allocate the value on its own stack and pass it by address. GtkTextIter is the worked example (src/GtkTextIter.def):
TYPE GtkTextIter = RECORD
opaque: ARRAY [0..9] OF ADDRESS; (* 10 * 8 = 80 bytes, 8-byte aligned *)
END;
The ARRAY OF ADDRESS gives both the exact byte size and the pointer alignment C expects. You call it via VAR:
VAR start, stop: GtkTextIter;
gtk_text_buffer_get_bounds(buffer, start, stop);
A union nested in a struct is simply part of that opaque block — we never name its members.
- Variant record (the real M2 equivalent of a C union) — when you actually need to read/write overlapping members:
TYPE Payload = RECORD
CASE tag: CARDINAL OF
0: i: INTEGER |
1: d: REAL |
2: p: ADDRESS
END
END;
The tag + CASE maps to a tagged union. In the GTK/GIR world we haven't needed this, because GObject-Introspection exposes unions through accessor functions, so the union stays opaque (idiom 1 or 2).
The generator documents the same policy (tools/gir2def.py): "All instance/opaque/unknown/pointer types map to ADDRESS; scalars map to the closest Modula-2 type. Value structs are therefore treated as opaque."
If you want, I can add a real GValue binding using idiom 2/3 as a demonstration — it's the classic C union case.
Now, continuing with the GNOME widgets. Let me pull the remaining signatures and enums.