RFR: 7903947: Access to function pointers in structs could be streamlined

Maurizio Cimadamore mcimadamore at openjdk.org
Thu Jul 24 09:29:11 UTC 2025


On Wed, 23 Jul 2025 12:37:47 GMT, Nizar Benalla <nbenalla at openjdk.org> wrote:

> Please review this patch to add a new option where you can generate an direct invoker methods. This feature is intended to be used on structs that are just a collection of functions we had like to call.
> 
> If this feature is used, we remove the getter to avoid name collisions as we assume this is similar to an interface.
> 
> Changes to `GUIDE.md` have been intentionally left out from this initial patch to make reviews easier.
> 
> 
> struct Foo {
>     struct Bar (*a)(void);
>     struct Bar (*b)(int);
> };
> 
> 
> Before 
> 
> 
> var foo = alloc_callback_h.foo();
> 
> var barA = Foo.a.invoke(Foo.a(foo), arena);
> var barB = Foo.b.invoke(Foo.b(foo), arena, 100);
> 
> 
> After 
> 
> 
> var foo = alloc_callback_h.foo();
> 
> var barA = Foo.a(foo, arena);
> var barB = Foo.b(foo, arena, 100);

Just to recap solution we have explored previously.

It would be nice if, in principle, just doing this:


var bar = Foo.a(foo, <args>);


worked, and it invoked the correct function pointer on the `foo` struct. But this solution can lead to clashes. Specifically, if the function pointer takes no arguments, then `Foo.a(foo)` would look exactly like a struct getter.

Another solution would be to tweak `a::invoke` method to work on a `foo` instance directly, and not on the _address_ that function takes. But this breaks composition -- if you first obtain the function pointer (a MemorySegment) stored at `Foo::a`, and then want to invoke it at a later point, how would you do it? If `invoke` takes a segment for `Foo` you can't really -- you always need to _first_ store the address in the `Foo` struct, and _then_ call the desired function pointer using `a::invoke`. Which seems awkward.

(note also that we cannot "just" add an overload of `a::invoke` that takes a Foo, because that is a memory segment too, so we get another clash).

In other words, there's no "easy" solution here. One possibility might be to add an invoker accessor to `Foo` -- something like `Foo.a$invoke(<args>)`. Since this uses a different name there would be no clash with the getter. This leaves the problem that, in structs that contain only function pointers, the getters are probably not very useful, but maybe we can add logic to filter those out.

The other solution is the one explored here: the developer _knows_ this isn't just _any_ struct, but a "functional" struct. It tells jextract, and jextract reacts by generating slightly different code. Perhaps one issue with this approach (although we arrived at it honestly) is that if one stares at code like:


var barA = Foo.a(<args>);


It is pretty difficult to tell whether that's a getter/setter or an "invoker".

src/main/java/org/openjdk/jextract/impl/StructBuilder.java line 160:

> 158:             boolean isFuncPtr = functionalDispatch && Utils.isFunctionPointer(type);
> 159:             if (isFuncPtr) {
> 160:                 emitFieldSetter(javaName, varTree, layoutField, offsetField);

I'm not sure we even want the setter -- the fields in these structs are typically populated by a library

src/main/java/org/openjdk/jextract/impl/StructBuilder.java line 180:

> 178:      * Generates exactly one invoker, inlining the struct.get(...) to avoid name collision.
> 179:      */
> 180:     private void emitFunctionalConvenience(String invokerName,

I'd call this `emitInvoker`

-------------

PR Comment: https://git.openjdk.org/jextract/pull/287#issuecomment-3112725685
PR Review Comment: https://git.openjdk.org/jextract/pull/287#discussion_r2227973060
PR Review Comment: https://git.openjdk.org/jextract/pull/287#discussion_r2227971894


More information about the jextract-dev mailing list