JNA構造でboolean
値を渡すときに使用しているネイティブライブラリから驚くべき警告が表示されます。
value of pCreateInfo->clipped (-1) is neither VK_TRUE nor VK_FALSE
このライブラリではVK_TRUE
およびVK_FALSE
は、それぞれ1および0として定義されます。
構造自体は特に複雑ではなく、その他すべてが機能しているように見えます(ネイティブライブラリは「未定義」のブール値をfalseとして扱うようです)が、ここにとにかくそれは:
public class VkSwapchainCreateInfoKHR extends Structure {
public int sType;
public Pointer pNext;
public int flags;
public Pointer surface;
public int minImageCount;
public int imageFormat;
public int imageColorSpace;
public VkExtent2D imageExtent;
public int imageArrayLayers;
public int imageUsage;
public int imageSharingMode;
public int queueFamilyIndexCount;
public Pointer pQueueFamilyIndices;
public int preTransform;
public int compositeAlpha;
public int presentMode;
public boolean clipped; // <--------- this is the field in question
public Pointer oldSwapchain;
}
clipped
フィールドがfalseの場合、警告は表示されません。trueの場合、警告が表示されます。JNAがtrue
を整数-1にマッピングしているようです。
このライブラリで使用されるネイティブのブール値は多くありませんが、1つがtrueに設定されている場合は常に同じ動作をします(他のすべても正常に動作します)。
特に、clipped
をint
に変更し、値を明示的に1または0に設定すると、すべてが機能します!
JNAブール値true
のデフォルト値は-1ですか?
もしそうなら、どのようにして型マッピングを上書きするのでしょうか?
それともint
を「手動で」使用するだけですか?
実際、そこにあるようにareさまざまなネイティブライブラリ構造に多数のブール値があり、実際には数百ものブール値があります!実装がその制限を強制しているからといって、すべてをint
で置き換えるのではなく、ブール型フィールドの意図を保持するのは良いことです。そこで、JNAの型変換を検討するのに少し時間を費やしました...
JNAは、ネイティブライブラリの作成時にNative::load
への追加の引数として渡されるTypeMapper
を使用したカスタムタイプのマッピングをサポートします。カスタムの型マッピングは、Javaから/へのネイティブコンバーターインターフェイスTypeConverter
を使用して定義されます。
Java boolean
とC int
を相互に1 = trueおよび0 = falseでマッピングするカスタムブールラッパーを定義することは非常に簡単です。
public final class VulkanBoolean {
static final TypeConverter MAPPER = new TypeConverter() {
@Override
public Class<?> nativeType() {
return Integer.class;
}
@Override
public Object toNative(Object value, ToNativeContext context) {
if(value == null) {
return VulkanBoolean.FALSE.toInteger();
}
else {
final VulkanBoolean bool = (VulkanBoolean) value;
return bool.toInteger();
}
}
@Override
public Object fromNative(Object nativeValue, FromNativeContext context) {
if(nativeValue == null) {
return VulkanBoolean.FALSE;
}
else {
final int value = (int) nativeValue;
return value == 1 ? VulkanBoolean.TRUE : VulkanBoolean.FALSE;
}
}
};
public static final VulkanBoolean TRUE = VulkanBoolean(true);
public static final VulkanBoolean FALSE = VulkanBoolean(false);
private final boolean value;
private VulkanBoolean(boolean value) {
this.value = value;
}
public boolean value() {
return value;
}
public int toInteger() {
return value ? 1 : 0;
}
}
タイプマッパーは次のように登録されます。
final DefaultTypeMapper mapper = new DefaultTypeMapper();
mapper.addTypeConverter(VulkanBoolean.class, VulkanBoolean.MAPPER);
...
final Map<String, Object> options = new HashMap<>();
options.put(Library.OPTION_TYPE_MAPPER, mapper);
Native.load("vulkan-1", VulkanLibrary.class, options);
ただし、これは問題の構造がinsideJNAライブラリインターフェースで定義されている場合にのみ機能します-少数の小さなライブラリを作成している場合は簡単です構造体(通常はそうです)ですが、数百のメソッドと約500の構造体(コード生成)があると、少し頭痛の種になります。
代わりに、型マッパーを構造コンストラクターで指定できますが、これには以下が必要です。
カスタムマッピングが必要なevery構造のインスツルメンテーション。
jNAがカスタムタイプのネイティブサイズを決定できるように、すべてのカスタムタイプはNativeMapped
を追加で実装する必要があります(なぜ基本的に同じ情報を2回指定する必要があるかはわかりません)。
各カスタム型はデフォルトのコンストラクタをサポートする必要があります。
これらはどちらも特に快適なオプションではありません。JNAが両方のケースをカバーするグローバルタイプマッピングをサポートしているとしたらすばらしいでしょう。タイプマッパーを使用してすべての構造をコード生成し直す必要があると思います。はぁ。
ただし、これは、問題の構造がJNAライブラリインターフェースの内部内で定義されている場合にのみ機能します。簡単な回避策は、ライブラリ内で基本クラス構造を定義し、それから他のすべてを拡張することです。
public interface Library {
abstract class VulkanStructure extends Structure {
protected VulkanStructure() {
super(VulkanLibrary.TYPE_MAPPER);
}
}
...
}
public class VkSwapchainCreateInfoKHR extends VulkanStructure { ... }
同じメカニズムを使用して、現在次のようなネイティブint
に約300のコード生成列挙を自動的にマップします。
public enum VkSubgroupFeatureFlag implements IntegerEnumeration {
VK_SUBGROUP_FEATURE_BASIC_BIT(1),
VK_SUBGROUP_FEATURE_VOTE_BIT(2),
...
private final int value;
private VkSubgroupFeatureFlag(int value) {
this.value = value;
}
@Override
public int value() {
return value;
}
}
現在、「列挙」を参照するすべての構造体は、実際にはint
として実装されています。 IntegerEnumeration
のカスタムタイプコンバーターを配置すると、フィールドタイプを実際のJava列挙型にすることができ、JNAは整数値との間の変換を処理します(現在、これにより、構造が明らかに型保証され、間違いなく明確になり、int
ではなく実際の列挙を明示的に参照します-ニース。
つまり.
public class VkSwapchainCreateInfoKHR extends VulkanStructure {
...
public int flags;
public Pointer surface;
public int minImageCount;
// The following fields were int but are now the Java enumerations
public VkFormat imageFormat = VkFormat.VK_FORMAT_UNDEFINED;
public VkColorSpaceKHR imageColorSpace;
...
}
(最近 here を正確に実行する例が見つかりました)。
うまくいけば、このすべてのワッフルは、JNAの気まぐれに頭を回そうとしている誰かを助けるでしょう。