Introduction to the
There are many types of mapping in JNA, library mapping, function mapping, function parameter and return value mapping, libary mapping to functions is relatively easy, as we have covered in previous articles. For type mapping, there are so many types in JAVA, So here we will isolate the type mapping of JNA.
The nature of type mapping
We mentioned earlier that there are two methods in JNA to map JAVA methods to native Libary methods. One method is called Interface Mapping and the other is called Direct Mapping.
But have we thought about the nature of these two mappings?
For example, native has a method. How do we pass method parameters in JAVA code to Native method and convert the return value of native method into the return type of JAVA function?
The answer is serialization.
Because essentially all interactions are binary interactions. The simplest case for converting a JAVA type to a Native type is that the underlying data length of the JAVA type is the same as that of the Native type. In this way, data conversion is easier.
Let’s look at the mapping and length relationship between JAVA types and Native types:
C Type | The meaning of Native type | Java Type |
---|---|---|
char | 8-bit integer | byte |
wchar_t | Platform specific | char |
short | 16 – bit integer | short |
int | 32 – bit integer | int |
int | boolean flag | boolean |
enum | Enumerated type | int (usually) |
long long, __int64 | The 64 – bit integer | long |
float | 32 – bit floating point number | float |
double | The 64 – bit floating point number | double |
pointer (e.g. void*) | Platform specific | Buffer Pointer |
pointer (e.g. void*), array | Platform specific |
[] (Primitive array) |
All of the above JAVA types are JDK native (except Pointer).
In addition to JAVA’s built-in type mapping, JNA also defines some data types that can be mapped to native types:
C Type | The meaning of Native type | Java Type |
---|---|---|
long | Platform dependent (32-bit or 64-bit integer) | NativeLong |
const char* | String (native encoding or jna.encoding) | String |
const wchar_t* | String (unicode) | WString |
char** | String array | String[] |
wchar_t** | String Arrays (Unicode) | WString[] |
void** | An array of Pointers | Pointer[] |
struct* struct | Structure pointer and structure | Structure |
union | The structure of the body | Union |
struct[] | Struct array | Structure[] |
void (*FP)() | Function Pointers (Java or Native) | Callback |
pointer ( *) | Pointer to the | PointerType |
other | Integer types | IntegerType |
other | Custom mapping type | NativeMapped |
TypeMapper
In addition to the defined mapping, you can also use TypeMapper to convert parameter types.
public interface TypeMapper { FromNativeConverter getFromNativeConverter(Class<? > javaType); ToNativeConverter getToNativeConverter(Class<? > javaType); }Copy the code
TypeMapper is an interface that defines two Converter methods, getFromNativeConverter and getToNativeConverter.
If you want to use TypeMapper you need to implement it and these two methods will do. Let’s take a look at the official W32APITypeMapper:
TypeConverter stringConverter = new TypeConverter() {
@Override
public Object toNative(Object value, ToNativeContext context) {
if (value == null)
return null;
if (value instanceof String[]) {
return new StringArray((String[])value, true);
}
return new WString(value.toString());
}
@Override
public Object fromNative(Object value, FromNativeContext context) {
if (value == null)
return null;
return value.toString();
}
@Override
public Class<?> nativeType() {
return WString.class;
}
};
addTypeConverter(String.class, stringConverter);
addToNativeConverter(String[].class, stringConverter);
Copy the code
First, define a TypeConverter, in which three methods toNative, fromNative and nativeType are implemented. In this example, the native type is WString and the JAVA type is String. And this TypeConverter is the FromNativeConverter and ToNativeConverter that will eventually be used.
Now that you have a typeMapper, how do you use it? The easiest way to do this is to add it to the third parameter of native.load, as follows:
TestLibrary lib = Native.load("testlib", TestLibrary.class, Collections.singletonMap(Library.OPTION_TYPE_MAPPER, mapper));
Copy the code
NativeMapped
The TypeMapper needs to be passed in when the Native. Load method is called to provide the conversion relationship between JAVA types and Native types. The TypeMapper can be thought of as an external maintainer of the type conversion relationship.
If you can maintain a conversion relationship outside of a JAVA type, can you maintain the conversion relationship inside the JAVA type itself? The answer is yes, we simply implemented NativeMapped interface in the JAVA type where the conversion type relationship was implemented.
(NativeMapped interface)
public interface NativeMapped { Object fromNative(Object nativeValue, FromNativeContext context); Object toNative(); Class<? > nativeType(); }Copy the code
You can see that the methods to be implemented in NativeMapped were basically the same as in FromNativeConverter and ToNativeConverter.
It’s no use saying NativeMapped. (Apped!) (” NativeMapped “) (” NativeMapped “)
public enum TestEnum implements NativeMapped {
VALUE1, VALUE2;
@Override
public Object fromNative(Object nativeValue, FromNativeContext context) {
return values()[(Integer) nativeValue];
}
@Override
public Object toNative() {
return ordinal();
}
@Override
public Class<?> nativeType() {
return Integer.class;
}
}
Copy the code
This class implements conversions from Integer to TestEnum enumeration.
To use the TestEnum class, define an interface:
public static interface EnumerationTestLibrary extends Library {
TestEnum returnInt32Argument(TestEnum arg);
}
Copy the code
The specific call logic is as follows:
EnumerationTestLibrary lib = Native.load("testlib", EnumerationTestLibrary.class);
assertEquals("Enumeration improperly converted", TestEnum.VALUE1, lib.returnInt32Argument(TestEnum.VALUE1));
assertEquals("Enumeration improperly converted", TestEnum.VALUE2, lib.returnInt32Argument(TestEnum.VALUE2));
Copy the code
It was no longer necessary to specify TypeMapper because NativeMapped contained the conversion information.
Note that testlib is used here. This testlib is compiled from the JNA Native module. If you’re on a MAC, you can copy the JNA code and run Ant Native to get it. Copy libtestlib.dylib to the Resources directory in your project and darwin-aarch64 or Darwin-x86 will do.
Have not the classmate, can contact me.
conclusion
This article explained the type mapping rules in JNA and the methods for customizing type mappings.
The code for this article: github.com/ddean2009/l…
This article is available at www.flydean.com
The most popular interpretation, the most profound dry goods, the most concise tutorial, many tips you didn’t know waiting for you to discover!
Welcome to pay attention to my public number: “procedures those things”, understand technology, more understand you!