У меня есть собственная библиотека .so
, которая имеет следующую функцию:
void foo(void* handle);
Это хорошо работает на C#:
[DllImport("library.so", CallingConvention = (CallingConvention) 3)]
private static extern void foo(ref IntPtr handle);
// ...
IntPtr handle = IntPtr.Zero;
foo(ref handle);
Однако я не могу реализовать это в Java 22 с помощью FFM API:
var LINKER = Linker.nativeLinker();
var SYM_LOOKUP = SymbolLookup.loaderLookup().or(LINKER.defaultLookup());
var FOO = LINKER.downcallHandle(SYM_LOOKUP.find("foo").orElseThrow(), FunctionDescriptor.ofVoid(ADDRESS));
var handle = Arena.global().allocate(ValueLayout.ADDRESS); // is this correct?
FOO.invokeExact(handle);
По какой-то причине я получаю std::bad_array_new_length
в нативном коде (исходников нативной библиотеки, к сожалению, у меня нет).
В JNA есть com.sun.jna.ptr.IntByReference
, есть ли что-то подобное в FFM API?
@Slaw Тем не менее, у меня та же ошибка.
@BasilBourque Java 22
Хм. Я не знаю C#, но эти вопросы и ответы предполагают, что вы были на правильном пути, используя ADDRESS
. Хотя в принятом ответе также говорится, что IntPtr.Zero
«фактически NULL (нулевой указатель)». Этот другой вопрос и ответ , кажется, согласен. Что произойдет, если вы передадите MemorySegment.NULL (вместо выделения собственного сегмента)?
Ваш код выглядит правильно (хотя вы можете использовать Linker.nativeLinker().canonicalLayouts().get("size_t")
вместо ADDRESS
). Но указатель, который вы передаете foo
, указывает на неинициализированную память, тогда как в коде C# память инициализируется нулем. Как foo
справится с этим, зависит от foo
. Мы не можем вам многого сказать, не зная контракта foo
. Возможно, вы захотите попробовать allocateFrom(ADDRESS, MemorySegment.NULL)
вместо allocate
.
В API FFM нет прямого эквивалента IntByReference
. Все указатели представлены как MemorySegment
, к которым не привязан тип.
Вам нужна выделенная память размером с указатель, чтобы где-то указывать на выделенную память размера int. Независимо от того, выделяете ли вы память целочисленного размера или вызываемый API, требуется дополнительная документация.
Я нашел ошибку. На самом деле у меня было 2 собственных метода:
void initHandle(void* handle);
void useHandle(void* handle);
и мне нужно инициализировать дескриптор, а затем использовать его. Я делал это:
var handle = Arena.global().allocate(ValueLayout.ADDRESS);
INIT_HANDLE.invokeExact(handle);
USE_HANDLE.invokeExact(handle);
Оказывается, во второй функции мне пришлось передать дескриптор по-другому:
var handle = Arena.global().allocate(ValueLayout.ADDRESS);
INIT_HANDLE.invokeExact(handle);
USE_HANDLE.invokeExact(handle.get(ValueLayout.ADDRESS, 0));
Что произойдет, если вы выделите
JAVA_INT
? Обратите внимание:ADDEESS
будет иметь размер 8 байт на 64-битной JVM.