JPSG: a Preprocessor for Java with an Emphasis on Specialization to Primitive Types
In this post, I present Java Primitive Specialization Generator (JPSG), a preprocessor created to support the development of code that should be specialized to all Java primitive types (boolean
, byte
, short
, char
, int
, float
, long
, double
) along with Object types, before we can take advantage of the Project Valhalla.
JPSG was originally developed to support Koloboke collections library which has primitive specializations of maps and sets. All other collections libraries with primitive specializations (fastutil, Eclipse Collections, HPPC, Trove) have their own template processing engines to support the generation of primitive specializations as well. There are two things that make JPSG different from the other code generators for primitive types:
Templates are valid Java sources
JPSG’s template files can be valid Java source files which correspond to one of the specializations produced from the respective template. This allows making the development of templates more convenient and less error-prone by configuring directories with template files as source directories in your IDE so that IDE features such as autocomplete and inspections will work in the template files.
This is possible because JPSG captures and replaces actual type occurrences in code instead of relying on patterns that can’t be part of Java code. For example, the following file CharIntPair.java
is a valid JPSG template:
/* with char|byte|short|int|long|float|double|object key
int|byte|char|short|long|float|double|object value */
/* if !(object key object value) */
interface CharIntPair/*<>*/ extends Pair<Character, Integer> {
/* if !(object key) */ char getCharKey(); /* endif */
/* if !(object value) */ int getIntValue(); /* endif */
}
JPSG produces the following specialization FloatObjectPair.java
from the above template, along with 62 others:
interface FloatObjectPair<V> extends Pair<Float, V> {
float getFloatKey();
}
Object and primitive specializations can be generated from a single template
The above example shows that JPSG allows specialization for primitive types as well as object (generic) types. This makes it possible to produce all possible specializations of types such as Pair<A, B>
, Map<K, V>
, Triple<A, B, C>
, etc. from a single source template, while with some other template processing engines usually 4 or 8 different templates are required to cover all {object, primitive} combinations across all dimensions of specialization.
“Java macros”
JPSG supports /* with */
, /* if */
, and /* define */
blocks that allow for non-trivial specialization of some code for specific primitive types, or, generally, “C macros style” preprocessing of Java code. The preprocessing capabilities of JPSG are, perhaps, less powerful than of java-comment-preprocessor, so you may want to use that project unless you need specialization to primitive types.
Applications
- Libraries of Java collections, queues, and concurrency utilities that want to specialize to primitive types before we can leverage Project Valhalla.
- Integration of the above with serialization, RPC, and ORM frameworks.
- ByteBuffer-like libraries. Note, for example, that some of the implementations of
CharBuffer
,ShortBuffer
,IntBuffer
,FloatBuffer
,LongBuffer
, andDoubleBuffer
in OpenJDK are generated from templates themselves. JPSG could be used for some similar purposes. - Performance-sensitive code where some behavior needs to be parameterized but introducing an abstraction carries too much of a performance penalty. Again, you probably can use
java-comment-preprocessor to parameterize such code just as well as JPSG.
So, JPSG is not a tool for every day Java development, but there are cases when it comes really useful.