- Clinker
- 实现scanf和printf
- 总结
Clinker是个类,来自Java新提案 JEP 412,连同MemoryHandles, MemoryAccess等用来简化JNI调用C函数的过程。它能做到无需JNI,直接通过纯Java代码调用C函数。
实现scanf和printf
测试类
import com.jmc.lang.time.Time;
import jdk.incubator.foreign.*;
import java.lang.invoke.MethodType;
import java.util.HashMap;
import java.util.function.Consumer;
public class ClinkerTest {
public static void main(String[] args) throws Throwable {
// scanf & printf
var aPtr = Pointer.of(0);
var bPtr = Pointer.of(0L);
int res = scanf("%d %lld", aPtr, bPtr);
printf("scanf: a = %d, b = %lld, return value = %d.n", aPtr.get(), bPtr.get(), res);
printf("---------------------------------------------n");
printf("%s %s!n", "Hello", "World");
printf("%d / %.1f = %.6lfn", 10, 3.0f, 10 / 3.0);
printf("The time is: %sn", Time.now());
}
public static int printf(String format, Object... args) {
var clinker = Clinker.getInstance();
var lookup = Clinker.systemLookup();
var printf = clinker.downcallHandle(
// vprintf才能传入va_list
lookup.lookup("vprintf").orElse(null),
MethodType.methodType(int.class, MemoryAddress.class, Clinker.VaList.class),
FunctionDescriptor.of(Clinker.C_INT, Clinker.C_POINTER, Clinker.C_VA_LIST)
);
// 只属于本线程,可释放的“内存区域”(try结束后自动释放)
try (var scope = ResourceScope.newConfinedScope()) {
if (args.length == 0) {
// printf(char *format);
return (int) printf.invokeExact(Clinker.toCString(format, scope).address(),
Clinker.VaList.empty());
}
Consumer builderConsumer = builder -> {
for (var arg : args) {
switch (arg) {
case Integer i -> builder.vargFromInt(Clinker.C_INT, i);
case Long l -> builder.vargFromLong(Clinker.C_LONG_LONG, l);
case Float f -> builder.vargFromDouble(Clinker.C_DOUBLE, f);
case Double d -> builder.vargFromDouble(Clinker.C_DOUBLE, d);
case String s -> builder.vargFromAddress(Clinker.C_POINTER, Clinker.toCString(s, scope));
default -> throw new IllegalStateException("Unsupported type: " + arg.getClass().getSimpleName());
}
}
};
// printf(char *format, ...);
return (int) printf.invokeExact(Clinker.toCString(format, scope).address(),
Clinker.VaList.make(builderConsumer, scope));
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
public static int scanf(String format, Pointer>... ptrs) {
var clinker = Clinker.getInstance();
var lookup = Clinker.systemLookup();
var scanf = clinker.downcallHandle(
// vscanf才能传入va_list
lookup.lookup("vscanf").orElse(null),
MethodType.methodType(int.class, MemoryAddress.class, Clinker.VaList.class),
FunctionDescriptor.of(Clinker.C_INT, Clinker.C_POINTER, Clinker.C_VA_LIST)
);
try (var scope = ResourceScope.newConfinedScope()) {
if (ptrs.length == 0) {
throw new RuntimeException("args can not be null!");
}
var bindMap = new HashMap, MemorySegment>();
Consumer builderConsumer = builder -> {
for (var ptr : ptrs) {
MemorySegment cPtr;
if (ptr.type() == Integer.class || ptr.type() == Float.class) {
cPtr = MemorySegment.allocateNative(4, scope);
} else if (ptr.type() == Long.class || ptr.type() == Double.class || ptr.type() == String.class) {
cPtr = MemorySegment.allocateNative(8, scope);
} else {
throw new RuntimeException("Unsupported type: " + ptr.type().getSimpleName());
}
builder.vargFromAddress(Clinker.C_POINTER, cPtr);
bindMap.put(ptr, cPtr);
}
};
// scanf(char *format, ...);
int res = (int) scanf.invokeExact(Clinker.toCString(format, scope).address(),
Clinker.VaList.make(builderConsumer, scope));
bindMap.forEach((ptr, cPtr) -> {
switch (ptr.get()) {
case Integer ignored -> ptr.setUnchecked(MemoryAccess.getInt(cPtr));
case Long ignored -> ptr.setUnchecked(MemoryAccess.getLong(cPtr));
case Float ignored -> ptr.setUnchecked(MemoryAccess.getFloat(cPtr));
case Double ignored -> ptr.setUnchecked(MemoryAccess.getDouble(cPtr));
case String ignored -> ptr.setUnchecked(Clinker.toJavaString(cPtr));
default -> throw new IllegalStateException("Unsupported type: " + ptr.type().getSimpleName());
}
});
return res;
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
}
指针类
import java.util.function.Function; public class Pointer{ private T value; private Pointer() {} public static Pointer of(T initValue) { var ptr = new Pointer (); ptr.value = initValue; return ptr; } public T set(T newValue) { T oldValue = value; this.value = newValue; return oldValue; } @SuppressWarnings("unchecked") public void setUnchecked(Object newValue) { this.value = (T) newValue; } public T get() { return value; } public T update(Function updateFunc) { T oldValue = value; this.value = updateFunc.apply(oldValue); return oldValue; } @SuppressWarnings("unchecked") public Class type() { return (Class ) value.getClass(); } }
总结
Clinker能做到用纯Java代码实现调用C语言函数,但是过程比较繁琐,目前还处于孵化器阶段。相信发布正式版后它会变得更加精简易用!



