در برخی سناریوها باید توابع خارجی (Foreign Functions) را فراخوانی کنید که آرگومانهایشان شامل اشارهگر به توابع دیگر است. مثال معروف آن، تابع استاندارد C به نام qsort برای مرتبسازی عناصر یک آرایه است:
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
این تابع چهار آرگومان دارد:
base اشارهگری به اولین عنصر آرایهای است که باید مرتب شود.nmemb تعداد عناصر آرایه را مشخص میکند.size اندازهی هر عنصر در آرایه (بر حسب بایت) است.compar اشارهگری به تابعی است که دو عنصر را مقایسه میکند.تابع qsort به اشارهگر یک تابع مقایسهکننده نیاز دارد. از جاوا باید کد را بهعنوان Function Pointer به این تابع خارجی پاس دهید.
Upcall Stubs به شما امکان میدهند کد جاوا را بهعنوان Function Pointer به یک تابع بومی (Native) پاس دهید. در این آموزش نحوهی انجام این کار برای فراخوانی qsort در جاوا را بررسی میکنیم.
ابتدا ببینیم متد مقایسهی دو عنصر — یعنی دو مقدار int — در جاوا چگونه مدلسازی میشود:
class Qsort {
static int qsortCompare(MemorySegment elem1, MemorySegment elem2) {
return Integer.compare(elem1.get(ValueLayout.JAVA_INT, 0), elem2.get(ValueLayout.JAVA_INT, 0));
}
}
در این متد، مقادیر int توسط اشیای MemorySegment نمایش داده میشوند. متد Integer.compare دو مقدار int را بهصورت عددی مقایسه میکند و برای این کار به دسترسی به مقادیر نیاز دارد.
برای این منظور، مثال روی هر MemorySegment متد get(ValueLayout.OfInt, long) را فراخوانی میکند. پارامتر دوم آفست بایتی نسبت به موقعیت آدرس حافظه است.
حال بیایید کد جاوا را با تابع خارجی qsort لینک کنیم. برای این کار یک Downcall Method Handle برای تابع qsort میسازیم:
// Obtain instance of native linker
static final Linker linker = Linker.nativeLinker();
static int[] qsortTest(int[] unsortedArray) throws Throwable {
int[] sorted = null;
// Create downcall handle for qsort
MethodHandle qsort = linker.downcallHandle(linker.defaultLookup().find("qsort").get(),
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.JAVA_LONG,
ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
علاوه بر آن، به یک Method Handle برای نمایش متد مقایسه Qsort::qsortCompare نیز نیاز دارید:
// Create method handle for qsortCompare
MethodHandle comparHandle = MethodHandles.lookup().findStatic(Qsort.class, "qsortCompare",
MethodType.methodType(int.class, MemorySegment.class, MemorySegment.class));
متد MethodHandles.Lookup.findStatic(Class, String, MethodType) یک Method Handle برای یک متد استاتیک میسازد و سه آرگومان نیاز دارد:
Qsort.class کلاس متد است.qsortCompare نام متد است.با داشتن این عناصر، یک توصیف جاوا از تابع C که توسط یک متد جاوا پیادهسازی شده میسازیم.
در این مرحله باید به لینکر اجازه دهید توابع خارجی بتوانند Method Handle های جاوا را فراخوانی کنند.
برای این کار به یک Function Descriptor و یک Function Pointer برای qsortCompare نیاز دارید:
// Create a Java description of a C function implemented by a Java method
FunctionDescriptor qsortCompareDesc = FunctionDescriptor.of(ValueLayout.JAVA_INT,
ValueLayout.ADDRESS.withTargetLayout(ValueLayout.JAVA_INT),
ValueLayout.ADDRESS.withTargetLayout(ValueLayout.JAVA_INT));
// Create function pointer for qsortCompare
MemorySegment compareFunc = linker.upcallStub(comparHandle, qsortCompareDesc, Arena.ofAuto());
متد Linker.upcallStub(MethodHandle,FunctionDescriptor,Arena,Linker.Option...) سه آرگومان نیاز دارد:
comparHandle Method Handleای است که Function Pointer از آن ساخته میشود.qsortCompareDesc Function Descriptor مربوط به Function Pointer است.Arena.ofAuto() Arenaای است که با Function Pointer مرتبط میشود. Arena.ofAuto() یک Arena جدید میسازد که بهصورت خودکار توسط Garbage Collector مدیریت میشود.اکنون میتوانید تابع بومی qsort را برای مرتبسازی یک آرایه فراخوانی کنید.
تابع بومی qsort به یک آرایه بهعنوان ورودی نیاز دارد.
میتوانید از MemorySegment برای تخصیص حافظهی Off-Heap و ذخیرهی آرایهی input در آن استفاده کنید:
try (Arena arena = Arena.ofConfined()) {
MemorySegment array = arena.allocateFrom(ValueLayout.JAVA_INT, input);
برای فراخوانی تابع qsort آرگومانهای جاوا مربوطه را پاس دهید:
qsort.invoke(array, (long)input.length, ValueLayout.JAVA_INT.byteSize(), compareFunc);
اگر میخواهید آرایهی مرتبشده را ببینید، مقادیر آن را از حافظهی Off-Heap به حافظهی On-Heap کپی کنید:
sorted = array.toArray(ValueLayout.JAVA_INT);
در ادامه، کد کامل و اجرایی این مثال آمده است:
import java.lang.foreign.*;
import java.lang.invoke.*;
public class InvokeQsort {
class Qsort {
static int qsortCompare(MemorySegment elem1, MemorySegment elem2) {
return Integer.compare(elem1.get(ValueLayout.JAVA_INT, 0), elem2.get(ValueLayout.JAVA_INT, 0));
}
}
// Obtain instance of native linker
final static Linker linker = Linker.nativeLinker();
static int[] qsortTest(int[] unsortedArray) throws Throwable {
int[] sorted = null;
// Create downcall handle for qsort
MethodHandle qsort = linker.downcallHandle(
linker.defaultLookup().find("qsort").get(),
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS,
ValueLayout.JAVA_LONG,
ValueLayout.JAVA_LONG,
ValueLayout.ADDRESS));
// Create method handle for qsortCompare
MethodHandle comparHandle = MethodHandles.lookup()
.findStatic(Qsort.class,
"qsortCompare",
MethodType.methodType(int.class,
MemorySegment.class,
MemorySegment.class));
// Create a Java description of a C function implemented by a Java method
FunctionDescriptor qsortCompareDesc = FunctionDescriptor.of(
ValueLayout.JAVA_INT,
ValueLayout.ADDRESS.withTargetLayout(ValueLayout.JAVA_INT),
ValueLayout.ADDRESS.withTargetLayout(ValueLayout.JAVA_INT));
// Create function pointer for qsortCompare
MemorySegment compareFunc = linker.upcallStub(comparHandle,
qsortCompareDesc,
Arena.ofAuto());
try (Arena arena = Arena.ofConfined()) {
// Allocate off-heap memory and store unsortedArray in it
MemorySegment array = arena.allocateFrom(ValueLayout.JAVA_INT,
unsortedArray);
// Call qsort
qsort.invoke(array,
(long)unsortedArray.length,
ValueLayout.JAVA_INT.byteSize(),
compareFunc);
// Access off-heap memory
sorted = array.toArray(ValueLayout.JAVA_INT);
}
return sorted;
}
public static void main(String[] args) {
try {
int[] sortedArray = InvokeQsort.qsortTest(new int[] { 0, 9, 3, 4, 6, 5, 1, 8, 2, 7 });
for (int num : sortedArray) {
IO.print(num + " ");
}
IO.println();
} catch (Throwable t) {
t.printStackTrace();
}
}
}
در نهایت میتوانید با اجرای دستور زیر، مثال کامل را بررسی کنید:
$ java InvokeQsort.java
WARNING: A restricted method in java.lang.foreign.Linker has been called
WARNING: java.lang.foreign.Linker::downcallHandle has been called by InvokeQsort in an unnamed module
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
0 1 2 3 4 5 6 7 8 9
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://dev.java/learn/