upm
1.7.1
Sensor/Actuator repository for libmraa (v2.0.0)
|
The "Creating Java Bindings Guide" serves as a basic tutorial for using the SWIG software development tool to create 'glue code' required for Java to call into C/C++ code. It contains: guides for dealing with type conversions, exception handling, callbacks; recommendations on how to write/modify the native API to avoid issues on the Java side, and also workarounds for those issues that can't be avoided.
This guide was created with the upm and mraa libraries in mind, and uses examples taken from these sources, but its usage can be extended to any project of creating Java bindings for C/C++ libraries.
SWIG Java-specific Documentation
As much as possible, avoid passing values/returning values through pointers given as as arguments to methods. As the Java language does not have pointers, SWIG provides a workaround in the typemaps.i library.
Functions that return multiple values through pointers, that make sense to be grouped together into an array1 (e.g. speed values, acceleration values), should be replaced by functions that return a pointer to an array in which the elements are the returned values. Afterwards, wrap the C array with a Java array. E.g.:
Replaced by:
Functions that return N values through pointers, that do not make sense to grouped together (e.g. a general purpose function that returns both the light intensity and air pollution), should be replaced by N functions (one for each value) that read only one specific value. E.g.:
Replaced by:
Functions that return N values through pointers; values that do not make sense to be grouped together, but are time dependent, and make sense to be read at the same time. For example, a sensor that reads air humidity and temperature. A user may want to know the temperature value temp read at the exact moment the humidity value humid was read. These should be replaced by N+1 functions: a getData() function that reads all values at the same time and stores them in global variables; and N getter functions, one for each value. E.g.
Replaced by:
1this depends on the interpretation of the returned data. For example, arguments that return the temperature and light intensity, don't make sense to be grouped into an array of size 2. But acceleration on the three axis can be grouped together in an array of size 3. where accelX is accel[0], accelY is accel[1], accelZ is accel[2].
Notice: Sometimes, you may be required to write JNI code. Be aware of the difference between the C JNI calling syntax and the C++ JNI calling syntax.The C++ calling syntax will not compile as C and also vice versa. It is however possible to write JNI calls which will compile under both C and C++ and is covered in the Typemaps for both C and C++ compilation section of the SWIG Documentation.
The exception directive allows you to define a general purpose exception handler. For example, you can specify the following:
If [method_name] is not specified then the directive is applied to all methods in its scope.
The usual thing you'd want to do is catch the C++ exception and throw an equivalent exception in your language.
The exception.i library file provides support for creating language independent exceptions in your interfaces. To use it, simply put an "%include exception.i" in your interface file. This provides a function SWIG_exception() that can be used to raise common language exceptions in a portable manner. For example :
In the upm library, the upm_exception.i interface file provides the functionality to catch common exceptions and propagate them through SWIG. It uses the exception.i library file and is language independent.
The upm_exception.i interface file is included in the upm.i file, therefor SWIG wraps all generated methods' body in a try-catch statement for the following exceptions:
To throw a specific Java exception:
Where FindClass and ThrowNew are JNI functions.
Java defines two tipes of exceptions: checked exception and unchecked exceptions (errors and runtime exceptions). Checked exceptions are subject to the Catch or Specify Requirement.
The C++ compiler does not force the code to catch any exception.
The exception directive does not specify if a method throws a checked exception (does not add classes to the throws clause). For this, the javaexception(classes) directive is used; where classes is a string containing one or more comma separated Java classes.
In the upm library, the java_exceptions.i library file provides the functionality to catch exceptions and propagate them through SWIG as Java checked exceptions. The file provides SWIG wrappers, in the form of macros, that can be applied to methods.E.g. use the READDATA_EXCEPTION(function) macro for functions that read data from a sensor and throw a std::runtime_error in case of a read failure. This will result in:
SWIG can wrap arrays in a more natural Java manner than the default by using the arrays_java.i library file. Just include this file into your SWIG interface file.
Functions that return arrays, return a pointer to that array. E.g.:
SWIG:
In C, arrays are tipically passed as pointers, with an integer value representig the length of the array. In Java, the length of an array is always known, so the length argument is redundant. This example shows how to wrap the C array and also get rid the length argument. E.g.:
SWIG:
!!!! There is a difference between TYPE *name and TYPE * name in typemaps!!!!!
Callbacks in the UPM Java library (as well as the MRAA Java library) make use of the void mraa_java_isr_callback(void* data) method from MRAA.
Callbacks in the UPM Java library are implemented as follows (we use the a110x Hall Effect sensors as example):
We extend the sensor class with another method, installISR(jobject runnable), which is a wrapper over the original installISR(void (*isr)(void *), void *arg) method. This will install the mraa_java_isr_callback() method as the interrupt service routine (ISR) to be called, with jobject runnable as argument.
Java callbacks are added at the SWIG interface level. For ease-of-use, a collection of macros are available in src/_upm.i.
src/a110x/a110x.i:
Will expand to the following SWIG wrapper code:
To use callback in java, we create a ISR class, which implements the Java Runnable interface, and we override the run() method with the code to be executed when the interrupt is received. An example for the a110x Hall sensor that increments a counter each time an interrupt is received: