栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

纯Java代码调用C函数 —— CLinker

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

纯Java代码调用C函数 —— CLinker

纯Java代码调用C函数
    • Clinker
    • 实现scanf和printf
    • 总结

Clinker

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语言函数,但是过程比较繁琐,目前还处于孵化器阶段。相信发布正式版后它会变得更加精简易用!


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/511728.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号