RFR: Generate Java enums
We are using jextract in our [cuVS Java](https://github.com/rapidsai/cuvs/tree/branch-25.08/java) project. As of now, jextract generates individual getter functions for the C enums. This results in code where it is difficult to disambiguate which getter functions are logically grouped together for a particular Enum. Additionally, given the lack of this capability, we also have to manually create and maintain Java enums for each enum in the C layer. In this PR, I wish to introduce an option to additionally generate Java enums by passing an optional flag `--generate-java-enums`. In case a Java user wishes to use bitwise operations on the enums, they can use the enum's `.ordinal()` method for this. The original static getter methods will still be generated as before. Example C enum: enum SIZE { S, M, L }; Produces the following with jextract: private static final int S = (int)0L; public static int S() { return S; } private static final int M = (int)1L; public static int M() { return M; } private static final int L = (int)2L; public static int L() { return L; } With this feature enabled using the `--generate-java-enums` flag, the following enum is generated (in addition to the above): public enum Size { S(0), M(1), L(2); private final int value; private Size(int value) {; this.value = value; } public int getValue() { return this.value; } } This PR is created against the `jdk22` branch. I will rebase it to `master` branch if needed. ------------- Commit messages: - Remove extra space in messages.properties - Generate enums in header file instead - Initial work Changes: https://git.openjdk.org/jextract/pull/284/files Webrev: https://webrevs.openjdk.org/?repo=jextract&pr=284&range=00 Stats: 184 lines in 12 files changed: 168 ins; 0 del; 16 mod Patch: https://git.openjdk.org/jextract/pull/284.diff Fetch: git fetch https://git.openjdk.org/jextract.git pull/284/head:pull/284 PR: https://git.openjdk.org/jextract/pull/284
On Fri, 20 Jun 2025 17:09:19 GMT, Vivek Narang <duke@openjdk.org> wrote:
We are using jextract in our [cuVS Java](https://github.com/rapidsai/cuvs/tree/branch-25.08/java) project. As of now, jextract generates individual getter functions for the C enums. This results in code where it is difficult to disambiguate which getter functions are logically grouped together for a particular Enum. Additionally, given the lack of this capability, we also have to manually create and maintain Java enums for each enum in the C layer.
In this PR, I wish to introduce an option to additionally generate Java enums by passing an optional flag `--generate-java-enums`. In case a Java user wishes to use bitwise operations on the enums, they can use the enum's `.ordinal()` method for this. The original static getter methods will still be generated as before.
Example C enum:
enum SIZE { S, M, L };
Produces the following with jextract:
private static final int S = (int)0L; public static int S() { return S; }
private static final int M = (int)1L; public static int M() { return M; }
private static final int L = (int)2L; public static int L() { return L; }
With this feature enabled using the `--generate-java-enums` flag, the following enum is generated (in addition to the above):
public enum Size { S(0), M(1), L(2);
private final int value;
private Size(int value) {; this.value = value; }
public int getValue() { return this.value; } }
This PR is created against the `jdk22` branch. I will rebase it to `master` branch if needed.
In general we have not used the translation to Java enum because it's not 100% semantics preserving. In C enum constants are more of loose constants -- given: enum Color { Red, Green, Blue, } C code can do things like `Red | Blue` -- which are not possible with Java enums. Moreover, C enum constants live in the same namespace as global variables. So it seemed again honest to allow clients to access enum constants directly after having imported (statically) the main generated header class. While in some ways using Java enums to model C enums look more natural, it also represents a significant departure from C. I can imagine that some simple uses of C enums would be quite happy with the approach you propose -- but this approach would not scale for more complex uses. The philosophy of jextract has, so far, been to generate a set of bindings that was as close as possible to the semantics of the extracted header. That has been the main guiding principle. There's a lot of "creativity" jextract could provide on top (and enums is a good example), but adding creativity almost always results in binding that work better in some cases and worse in others. For these reasons, I don't think this PR fits well with the design principles of jextract. One less radical move could be to give up to the "single namespace" property, and at least group related enum constants together, under a separate generated class. That would mean the Java code would require more "qualifiers" than the associated C code, but with some better separation. Would something like that be enough? Or do you really need these constants to be enum constants (e.g. because you use them in exhaustive switches etc.) ? ------------- PR Comment: https://git.openjdk.org/jextract/pull/284#issuecomment-3031589143
On Thu, 3 Jul 2025 09:38:18 GMT, Maurizio Cimadamore <mcimadamore@openjdk.org> wrote:
One less radical move could be to give up to the "single namespace" property, and at least group related enum constants together, under a separate generated class. That would mean the Java code would require more "qualifiers" than the associated C code, but with some better separation. Would something like that be enough? Or do you really need these constants to be enum constants (e.g. because you use them in exhaustive switches etc.) ?
Thanks @mcimadamore for your feedback. Yes, if we can at least group related enum constants together under a separate generated class, that will be awesome. Would it be okay if I update this PR to have this change? Please let me know. Thanks! ------------- PR Comment: https://git.openjdk.org/jextract/pull/284#issuecomment-3046121662
On Thu, 3 Jul 2025 09:38:18 GMT, Maurizio Cimadamore <mcimadamore@openjdk.org> wrote:
``` enum Color { Red, Green, Blue, } ```
C code can do things like `Red | Blue` -- which are not possible with Java enums.
Hey @mcimadamore please excuse my gap in understanding here, but won't this operation be possible in Java by doing: `Color.Red.ordinal() | Color.Blue.ordinal()`? ------------- PR Comment: https://git.openjdk.org/jextract/pull/284#issuecomment-3046175531
No, because ordinal returns 0, 1, 2, 3, 4, 5, etc for each subsequent value. Whereas, it sounds like you need it to return 1, 2, 4, 8, etc. You could do the transformation yourself, of course. But if that doesn't work, maybe come at this from the opposite direction? What, in Java, gives you this OR-like functionality for enums? EnumSet! Maybe the better answer would be to have some way to return a long or long[] from an EnumSet to represent the included values? On Mon, Jul 7, 2025, 2:40 PM Vivek Narang <duke@openjdk.org> wrote:
On Thu, 3 Jul 2025 09:38:18 GMT, Maurizio Cimadamore < mcimadamore@openjdk.org> wrote:
``` enum Color { Red, Green, Blue, } ```
C code can do things like `Red | Blue` -- which are not possible with
Java enums.
Hey @mcimadamore please excuse my gap in understanding here, but won't this operation be possible in Java by doing: `Color.Red.ordinal() | Color.Blue.ordinal()`?
-------------
PR Comment: https://git.openjdk.org/jextract/pull/284#issuecomment-3046175531
On Mon, 7 Jul 2025 18:50:48 GMT, David Alayachew <davidalayachew@gmail.com> wrote:
What, in Java, gives you this OR-like functionality for enums? EnumSet!
Modelling each enum value as an enum set is certainly possible. It also sounds as overkill. One of the principles of jextract is to add as little overhead as possible. I think here we should just be honest and admit that C enums and Java enums don't have much in common, except for the use of the word `enum`. ------------- PR Comment: https://git.openjdk.org/jextract/pull/284#issuecomment-3046580435
On Mon, 7 Jul 2025 21:33:15 GMT, Maurizio Cimadamore <mcimadamore@openjdk.org> wrote:
What, in Java, gives you this OR-like functionality for enums? EnumSet!
Modelling each enum value as an enum set is certainly possible. It also sounds as overkill. One of the principles of jextract is to add as little overhead as possible. I think here we should just be honest and admit that C enums and Java enums don't have much in common, except for the use of the word `enum`.
(note -- this does NOT mean that other, more specialized, code generators could not model C enums as Java enums. Perhaps in some code bases that makes total sense. But jextract is intended to provide reliable bindings for _all_ C headers -- which sort of restricts the kind of tricks we can play) ------------- PR Comment: https://git.openjdk.org/jextract/pull/284#issuecomment-3046583256
On Mon, 7 Jul 2025 21:34:38 GMT, Maurizio Cimadamore <mcimadamore@openjdk.org> wrote:
What, in Java, gives you this OR-like functionality for enums? EnumSet!
Modelling each enum value as an enum set is certainly possible. It also sounds as overkill. One of the principles of jextract is to add as little overhead as possible. I think here we should just be honest and admit that C enums and Java enums don't have much in common, except for the use of the word `enum`.
What, in Java, gives you this OR-like functionality for enums? EnumSet!
Modelling each enum value as an enum set is certainly possible. It also sounds as overkill. One of the principles of jextract is to add as little overhead as possible. I think here we should just be honest and admit that C enums and Java enums don't have much in common, except for the use of the word `enum`.
(note -- this does NOT mean that other, more specialized, code generators could not model C enums as Java enums. Perhaps in some code bases that makes total sense. But jextract is intended to provide reliable bindings for _all_ C headers -- which sort of restricts the kind of tricks we can play)
Hey @mcimadamore please excuse my gap in understanding here, but won't this operation be possible in Java by doing: `Color.Red.ordinal() | Color.Blue.ordinal()`?
As said in another comment, there's no guarantee that the Java enum ordinal and the value of the C enum constant will be the same. So, no, that would not be a good translation. Also, please consider clients passing enums to native functions -- right now they can just say e.g.
```java native_func(RED); ```
Isn't this what the enum `getValue()` method included in this PR is for? E.g. `Color.Red.getValue() | Color.Blue.getValue()` and `native_func(RED.getValue())`. But the other concerns mentioned above still remain, such as making it difficult to discover which enum to use when a jextract-generated method just accepts an `int`, and that users might still accidentally use `ordinal()` or `MyEnum.values()[i]` instead of the `getValue()` method. ------------- PR Comment: https://git.openjdk.org/jextract/pull/284#issuecomment-3080028890
On Fri, 20 Jun 2025 17:09:19 GMT, Vivek Narang <duke@openjdk.org> wrote:
We are using jextract in our [cuVS Java](https://github.com/rapidsai/cuvs/tree/branch-25.08/java) project. As of now, jextract generates individual getter functions for the C enums. This results in code where it is difficult to disambiguate which getter functions are logically grouped together for a particular Enum. Additionally, given the lack of this capability, we also have to manually create and maintain Java enums for each enum in the C layer.
In this PR, I wish to introduce an option to additionally generate Java enums by passing an optional flag `--generate-java-enums`. In case a Java user wishes to use bitwise operations on the enums, they can use the enum's `.ordinal()` method for this. The original static getter methods will still be generated as before.
Example C enum:
enum SIZE { S, M, L };
Produces the following with jextract:
private static final int S = (int)0L; public static int S() { return S; }
private static final int M = (int)1L; public static int M() { return M; }
private static final int L = (int)2L; public static int L() { return L; }
With this feature enabled using the `--generate-java-enums` flag, the following enum is generated (in addition to the above):
public enum Size { S(0), M(1), L(2);
private final int value;
private Size(int value) {; this.value = value; }
public int getValue() { return this.value; } }
This PR is created against the `jdk22` branch. I will rebase it to `master` branch if needed.
Another thing that came to mind: if we translate C enums as Java enums -- how is a native function accepting a C enum type translated? If we translate it as an int-accepting function (what we do now) then the Java enum constants cannot be used to call into native functions. If we translate it as a Java-enum-accepting function, then we would make these function stricter than their C counterparts. ------------- PR Comment: https://git.openjdk.org/jextract/pull/284#issuecomment-3031606147
On Thu, 3 Jul 2025 09:43:59 GMT, Maurizio Cimadamore <mcimadamore@openjdk.org> wrote:
We are using jextract in our [cuVS Java](https://github.com/rapidsai/cuvs/tree/branch-25.08/java) project. As of now, jextract generates individual getter functions for the C enums. This results in code where it is difficult to disambiguate which getter functions are logically grouped together for a particular Enum. Additionally, given the lack of this capability, we also have to manually create and maintain Java enums for each enum in the C layer.
In this PR, I wish to introduce an option to additionally generate Java enums by passing an optional flag `--generate-java-enums`. In case a Java user wishes to use bitwise operations on the enums, they can use the enum's `.ordinal()` method for this. The original static getter methods will still be generated as before.
Example C enum:
enum SIZE { S, M, L };
Produces the following with jextract:
private static final int S = (int)0L; public static int S() { return S; }
private static final int M = (int)1L; public static int M() { return M; }
private static final int L = (int)2L; public static int L() { return L; }
With this feature enabled using the `--generate-java-enums` flag, the following enum is generated (in addition to the above):
public enum Size { S(0), M(1), L(2);
private final int value;
private Size(int value) {; this.value = value; }
public int getValue() { return this.value; } }
This PR is created against the `jdk22` branch. I will rebase it to `master` branch if needed.
Another thing that came to mind: if we translate C enums as Java enums -- how is a native function accepting a C enum type translated? If we translate it as an int-accepting function (what we do now) then the Java enum constants cannot be used to call into native functions. If we translate it as a Java-enum-accepting function, then we would make these function stricter than their C counterparts.
Hey @mcimadamore please excuse my gap in understanding here, but won't this operation be possible in Java by doing: `Color.Red.ordinal() | Color.Blue.ordinal()`?
As said in another comment, there's no guarantee that the Java enum ordinal and the value of the C enum constant will be the same. So, no, that would not be a good translation. Also, please consider clients passing enums to native functions -- right now they can just say e.g. native_func(RED); It would be sad if they had to do: native_func(RED.ordinal()); Of course one could imagine generating one extra overload for each enum-accepting function -- but that has issues: * you can only use the Java enum-accepting overload in some cases, but not other (where you want to use bitwise ops) * if a function uses N enum types, you need 2^N overloads (at least if you want to generate all possible combinations) I don't think much joy is to be had by modelling C enums with Java enums. We've been at it several times, and we keep retracing our steps back to the same answers. ------------- PR Comment: https://git.openjdk.org/jextract/pull/284#issuecomment-3046568103
On Mon, 7 Jul 2025 21:26:46 GMT, Maurizio Cimadamore <mcimadamore@openjdk.org> wrote:
Another thing that came to mind: if we translate C enums as Java enums -- how is a native function accepting a C enum type translated? If we translate it as an int-accepting function (what we do now) then the Java enum constants cannot be used to call into native functions. If we translate it as a Java-enum-accepting function, then we would make these function stricter than their C counterparts.
Hey @mcimadamore please excuse my gap in understanding here, but won't this operation be possible in Java by doing: `Color.Red.ordinal() | Color.Blue.ordinal()`?
As said in another comment, there's no guarantee that the Java enum ordinal and the value of the C enum constant will be the same. So, no, that would not be a good translation. Also, please consider clients passing enums to native functions -- right now they can just say e.g.
native_func(RED);
It would be sad if they had to do:
native_func(RED.ordinal());
Of course one could imagine generating one extra overload for each enum-accepting function -- but that has issues:
* you can only use the Java enum-accepting overload in some cases, but not other (where you want to use bitwise ops) * if a function uses N enum types, you need 2^N overloads (at least if you want to generate all possible combinations)
I don't think much joy is to be had by modelling C enums with Java enums. We've been at it several times, and we keep retracing our steps back to the same answers.
Thanks @mcimadamore for your feedback. Yes, if we can at least group related enum constants together under a separate generated class, that will be awesome. Would it be okay if I update this PR to have this change? Please let me know. Thanks!
I think we should ponder this some more. For simple use cases, it is really great to just have all the enum constants "in scope" in the same way as it happens with C. W/o that, the user would be left to try and figure out "what to include" for each enum. This might not be too obvious because the C source file one is trying to convert might only refer to enum _constants_ but never to the _enum type_ bringing these constants into existence. E.g. a C source might just use `Red` and `Green` and pass them to functions (and maybe these functions just take an `int`) -- and the code might never utter `Color`, ever. As a jextract user, how would I know that I need to `import static foo.Color.*` to get there? I'm not saying this is a blocker -- just listing all the various potential issues with changing the status quo (also as a way to explain why we are where we are). ------------- PR Comment: https://git.openjdk.org/jextract/pull/284#issuecomment-3046576333
On Fri, 20 Jun 2025 17:09:19 GMT, Vivek Narang <duke@openjdk.org> wrote:
We are using jextract in our [cuVS Java](https://github.com/rapidsai/cuvs/tree/branch-25.08/java) project. As of now, jextract generates individual getter functions for the C enums. This results in code where it is difficult to disambiguate which getter functions are logically grouped together for a particular Enum. Additionally, given the lack of this capability, we also have to manually create and maintain Java enums for each enum in the C layer.
In this PR, I wish to introduce an option to additionally generate Java enums by passing an optional flag `--generate-java-enums`. In case a Java user wishes to use bitwise operations on the enums, they can use the enum's `.ordinal()` method for this. The original static getter methods will still be generated as before.
Example C enum:
enum SIZE { S, M, L };
Produces the following with jextract:
private static final int S = (int)0L; public static int S() { return S; }
private static final int M = (int)1L; public static int M() { return M; }
private static final int L = (int)2L; public static int L() { return L; }
With this feature enabled using the `--generate-java-enums` flag, the following enum is generated (in addition to the above):
public enum Size { S(0), M(1), L(2);
private final int value;
private Size(int value) {; this.value = value; }
public int getValue() { return this.value; } }
This PR is created against the `jdk22` branch. I will rebase it to `master` branch if needed.
Apologies for the significant delay of several weeks on this. I will revisit this feature and the suggestions/feedback and will create a fresh PR (if needed) in the near future. Thanks! ------------- PR Comment: https://git.openjdk.org/jextract/pull/284#issuecomment-3508467013
On Fri, 20 Jun 2025 17:09:19 GMT, Vivek Narang <duke@openjdk.org> wrote:
We are using jextract in our [cuVS Java](https://github.com/rapidsai/cuvs/tree/branch-25.08/java) project. As of now, jextract generates individual getter functions for the C enums. This results in code where it is difficult to disambiguate which getter functions are logically grouped together for a particular Enum. Additionally, given the lack of this capability, we also have to manually create and maintain Java enums for each enum in the C layer.
In this PR, I wish to introduce an option to additionally generate Java enums by passing an optional flag `--generate-java-enums`. In case a Java user wishes to use bitwise operations on the enums, they can use the enum's `.ordinal()` method for this. The original static getter methods will still be generated as before.
Example C enum:
enum SIZE { S, M, L };
Produces the following with jextract:
private static final int S = (int)0L; public static int S() { return S; }
private static final int M = (int)1L; public static int M() { return M; }
private static final int L = (int)2L; public static int L() { return L; }
With this feature enabled using the `--generate-java-enums` flag, the following enum is generated (in addition to the above):
public enum Size { S(0), M(1), L(2);
private final int value;
private Size(int value) {; this.value = value; }
public int getValue() { return this.value; } }
This PR is created against the `jdk22` branch. I will rebase it to `master` branch if needed.
This pull request has been closed without being integrated. ------------- PR: https://git.openjdk.org/jextract/pull/284
participants (4)
-
David Alayachew
-
Marcono1234
-
Maurizio Cimadamore
-
Vivek Narang