Introduction
There are many kinds of mappings in JNA, library mapping, function mapping and function parameter and return value mapping. The mapping between libary and function is relatively simple. We have explained it in the previous article. For type mapping, because There are many types of types in JAVA, so here we will extract the type mapping of JNA and explain it separately.
The nature of typemaps
We mentioned earlier that there are two ways to map methods in JAVA and methods in native libary in JNA, one is called interface mapping, and the other is called direct mapping.
But have we considered what is the nature of these two mappings?
For example, native has a method. How do we pass the method parameters in the JAVA code to the native method, and convert the return value of the native method into the return type of the function in JAVA?
The answer is serialization.
Because essentially all interactions are binary interactions. The JAVA type and the native type are converted. The simplest case is that the data length of the bottom layer of the JAVA type and the native type is consistent, so that the data conversion will be simpler.
Let's look at the mapping and length relationship between JAVA types and native types:
C Type | Meaning of Native type | Java Type |
---|---|---|
char | 8-bit integer | byte |
wchar_t | related to the platform | char |
short | 16-bit integer | short |
int | 32-bit integer | int |
int | boolean flag | boolean |
enum | enum type | int (usually) |
long long, __int64 | 64-bit integer | long |
float | 32-bit floating point number | float |
double | 64-bit floating point number | double |
pointer (eg void*) | Platform related | Buffer Pointer |
pointer (eg void*), array | Platform related | <P>[] (array of primitive types) |
The above JAVA types are all types that come with JDK (except Pointer).
In addition to the type mapping that comes with JAVA, JNA also defines some data types, which can be mapped with native types:
C Type | Meaning of Native type | Java Type |
---|---|---|
long | Platform dependent (32- or 64-bit integer) | NativeLong |
const char* | String (native encoding or jna.encoding) | String |
const wchar_t* | string (unicode) | WString |
char** | array of strings | String[] |
wchar_t** | String array (unicode) | WString[] |
void** | array of pointers | Pointer[] |
struct* struct | Structure pointers and structures | Structure |
union | structure | Union |
struct[] | array of structures | Structure[] |
void (*FP)() | Function pointer (Java or native) | Callback |
pointer (<T>*) | pointer | PointerType |
other | Integer type | IntegerType |
other | custom mapping type | NativeMapped |
TypeMapper
In addition to the defined mapping relationship, you can also use TypeMapper to customize the conversion of parameter types. Let's first look at the definition of TypeMapper:
public interface TypeMapper {
FromNativeConverter getFromNativeConverter(Class<?> javaType);
ToNativeConverter getToNativeConverter(Class<?> javaType);
}
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 are enough. Let's take a look at how the official W32APITypeMapper is implemented:
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);
First define a TypeConverter, and implement three methods toNative, fromNative and nativeType in TypeConverter. 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.
With typeMapper, how should it be used? The easiest way to do this is to add it to the third parameter of Native.load like this:
TestLibrary lib = Native.load("testlib", TestLibrary.class, Collections.singletonMap(Library.OPTION_TYPE_MAPPER, mapper));
NativeMapped
TypeMapper needs to be passed in when calling the Native.load method, so as to provide the conversion relationship between JAVA type and native type. TypeMapper can be seen as an external maintainer of type conversion relationships.
Many friends may have thought that since the conversion relationship can be maintained outside the JAVA type, can this conversion relationship be maintained in the JAVA type itself? The answer is yes, we only need to implement the NativeMapped interface in the JAVA type that needs to implement the conversion type relationship.
Let's first look at the definition of the NativeMapped interface:
public interface NativeMapped {
Object fromNative(Object nativeValue, FromNativeContext context);
Object toNative();
Class<?> nativeType();
}
It can be seen that the methods defined in NativeMapped to be implemented are basically the same as those defined in FromNativeConverter and ToNativeConverter.
The following is a specific example to illustrate how NativeMapped should be used. First we define an enum class to implement the NativeMapped interface:
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;
}
}
This class implements the conversion from Integer to TestEnum enumeration.
To use the TestEnum class, you need to define an interface:
public static interface EnumerationTestLibrary extends Library {
TestEnum returnInt32Argument(TestEnum arg);
}
The specific calling 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));
As you can see, because NativeMapped already contains type conversion information, there is no need to specify TypeMapper.
Note that testlib is used here. This testlib is compiled from the native module of JNA. If you are in a MAC environment, you can copy the JNA code and run ant native to get it. After the compilation is complete, copy this libtestlib.dylib to you You can use darwin-aarch64 or darwin-x86 in the resources directory of the project.
If you don't know, you can contact me.
Summarize
This article explains the type mapping rules in JNA and the method of custom type mapping.
Code for this article: https://github.com/ddean2009/learn-java-base-9-to-20.git
This article has been included in www.flydean.com
The most popular interpretation, the most profound dry goods, the most concise tutorials, and many tricks you don't know are waiting for you to discover!
Welcome to pay attention to my official account: "Program those things", understand technology, understand you better!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。