Native support for offsetof
I'm currently trying to bind a C library (by writing a .vapi) that has a callback/signal system that works as such:
- A
Listener
struct that takes anotify
function as a field. This is easy enough to bind with delegates. - A
Signal
struct that has anadd
method that adds aListener
to thatSignal
. There is no mechanism to provide user data. - When the listener method is called, it has two parameters; a pointer to the instance of the
Listener
struct, and a pointer to some data that is relevant to the signal invocation (not user data).
So, we can write some bindings and achieve this in Vala with something like this:
public class MyClass {
public Listener my_listener;
public MyClass () {
my_listener.notify = _my_handler;
some_object.some_signal.add (ref my_listener);
}
private static void _my_handler(ref Listener listener, void *data) {
}
}
Now, what do we do if we want access to the MyClass
instance relevant to this signal invocation?
The way this is intended to work in C is to put the Listener
struct in our own struct with the other instance data we require, and then use the offset of the Listener
struct within it to perform some pointer arithmetic to get back to the "parent" structure.
We can achieve this in Vala, but it's a bit of a hack. See the following minimal example.
public class MyClass {
public Listener my_listener;
[CCode (cname = "offsetof (MyClass, my_listener)")]
extern const int my_listener_offset;
public MyClass () {
my_listener.notify = _my_handler;
some_object.some_signal.add (ref my_listener);
}
private static void _my_handler(ref Listener listener, void *data) {
unowned MyClass self = (MyClass*)((size_t)&listener - my_listener_offset);
// Now we can do stuff with the `self` instance of `MyClass`
}
}
This only works if the Listener
field in MyClass
is public
, otherwise it ends up in the private structure of the class. And obviously, if we rename the field or the class, we have to remember to update our CCode
annotation to make sure the offsetof
call is still correct.
I would propose that a native offsetof
(similar to how sizeof
exists) method was added to Vala to achieve this without adding hacky C bindings, and it could/should warn if you were trying to get the offset of a private class member, as this would be an offset inside a private struct.
Or, as an alternative, slightly more complicated proposal, it would be good if Vala could provide a native method for getting the "parent" class from a pointer to a member of that class, omitting the need to do the pointer arithmetic in application code.
Zig language has a @fieldParentPtr
builtin that does this: https://ziglang.org/documentation/master/#fieldParentPtr
Libraries that use this callback/signal method, often provide helper macros to do this pointer arithmetic for you. For example, Wayland provides wl_container_of
, but these macros are difficult if not impossible to bind as they often take the name of a field rather than a pointer to it.
The Linux kernel also uses a similar mechanism in places, and provides similar helper macros.