如何使用 TypeConverter 通过 JNA 将 C 整数映射到 Java 枚举?
Posted
技术标签:
【中文标题】如何使用 TypeConverter 通过 JNA 将 C 整数映射到 Java 枚举?【英文标题】:How to map C integer to Java enum via JNA using TypeConverter? 【发布时间】:2015-10-04 08:20:10 【问题描述】:我希望 JNA 自动进行转换。现在我正在关注第二个答案in a very similar question 和JNA 自己的EnumConverter 实用程序类的解决方案。有一个关键的区别,我的枚举有一个构造函数参数。
我的代码定义了TypeConverter
:
public class SentinelStatusConverter implements TypeConverter
@Override
public SentinelStatus fromNative(Object nativeValue, FromNativeContext context)
Integer code = (Integer) nativeValue;
return SentinelStatus.fromCode(code);
@Override
public Integer toNative(Object value, ToNativeContext context)
SentinelStatus status = (SentinelStatus) value;
return Integer.valueOf(status.getCode());
@Override
public Class<Integer> nativeType()
return Integer.class;
public class SentinelTypeMapper extends DefaultTypeMapper
public SentinelTypeMapper()
addTypeConverter(SentinelStatus.class, new SentinelStatusConverter());
这是直接注册本机 C 库的代码以及我的自定义 TypeMapper
。 C 函数返回一个 int
,我想将其自动映射到 SentinelStatus
枚举中:
public class SentinelLibrary
static
Map<String, Object> options = new HashMap<String, Object>();
options.put(Library.OPTION_TYPE_MAPPER, new SentinelTypeMapper());
Native.register(NativeLibrary.getInstance("libnamelib", options));
public static native SentinelStatus hasp_get_sessioninfo(
NativeLong sessionHandle,
String query,
PointerByReference info);
SentinelStatus
是一个enum
,就像这样:
public enum SentinelStatus
HASP_STATUS_OK(0),
HASP_SOME_ERROR(13),
...
HASP_NOT_IMPL(1831);
private final int code;
SentinelStatus(final int code) this.code = code;
public int getCode() return this.code;
public static SentinelStatus fromCode(final int code)
for (SentinelStatus status : EnumSet.allOf(SentinelStatus.class))
if (code == status.getCode())
return status;
return SentinelStatus.HASP_NOT_IMPL;
使用此 JNA 映射和转换器,每当我尝试加载 SentinelLibrary
类时都会出错:
java.lang.ExceptionInInitializerError
...
Caused by: java.lang.IllegalArgumentException: Unsupported Structure field type class package.name.SentinelStatus
at com.sun.jna.Structure$FFIType.get(Structure.java:1851)
at com.sun.jna.Structure$FFIType.get(Structure.java:1806)
at com.sun.jna.Native.register(Native.java:1438)
at com.sun.jna.Native.register(Native.java:1165)
at package.name.SentinelLibrary.<clinit>(line with Native.register() call)
我已阅读文档并且对映射的类或类型没有任何限制。只有NativeMapped
接口要求实现者提供一个公共的无参数构造函数。
是否可以通过这种方式将 C 整数映射到枚举?
更新:
在进一步翻阅 JNA 代码后,我将此字段添加到 SentinelStatus
枚举中:
public final static TypeMapper TYPE_MAPPER = new SentinelTypeMapper();
现在SentinelLibrary
已正确加载。但是所有返回枚举的方法都返回null
,错误打印到stderr
:
JNA: unrecognized return type, size 4
【问题讨论】:
知道抛出异常的位置可能很有用。 【参考方案1】:您很可能在库定义的上下文之外定义您的Structure
(这是存储TypeMapper
信息的位置)。
您可以在您的 Structure
类上显式设置类型映射器,或者在您的本机 Library
类中定义您的 Structure
类。如果没有为Structure
明确定义TypeMapper
,它将回退到任何周围的Library
类提供的选项。
public class MyStructure extends Structure
public MyStructure()
setTypeMapper(new MyTypeMapper());
或者,
public interface MyLibrary extends Library
public class MyStructure extends Structure
或者直接映射:
public class MyLibrary implements Library
Native.register(...);
public class MyStructure extends Structure
当Structure
类找不到显式设置的类型映射器时,它会查找Library
类型的封闭类,并尝试查找实例化该类时使用的选项。这些选项在加载本机库时被缓存,因此可以通过使用 Library
子类作为键的键查找来获得这些选项。您需要实现Library
接口,以便您的直接映射库被识别为本机库类。
编辑
这是 JNA 的类型映射器处理中的一个错误(请参阅project issue)。这似乎仅限于直接映射库和enum
类型映射。
编辑
这里有几个 JNA 错误在起作用,如果您想尝试,请联系 fix is available。此问题与直接映射库上的类型映射器隔离,其中将原语转换为 Java 对象。
【讨论】:
感谢您的快速答复。它解释了为什么我的SentinelStatus
在register
图书馆时可能无法被识别。但问题仍然存在。我想映射到的SentinelStatus
是enum
。我真的不能让它成为Structure
的子类。
根据您的错误,SentinelStatus
被用作Structure
中的字段。需要能够找到类型映射器的是 Structure
容器。
库缓存也可能发生在Structure
查找发生之后,在这种情况下,您需要遵循第一个示例并在结构实例上显式设置类型映射器。如果您包含更多的库定义,尤其是那些包含 Structure
用法的位,将会有所帮助。
SentinelStatus
enum 实际上仅用作来自SentinelLibrary
的native
方法的返回类型(可以在第一个代码块的末尾看到)。我没有在任何地方使用任何Structure
子类。
显然,JNA 错误报告中有一个错误。以上是关于如何使用 TypeConverter 通过 JNA 将 C 整数映射到 Java 枚举?的主要内容,如果未能解决你的问题,请参考以下文章
Android 房间持久库 - TypeConverter 错误错误:无法弄清楚如何将字段保存到数据库”
TypeConverter() 在 Android 中的 Room 出现 TypeConverter 错误时具有私有访问权限