如何在不同的编程语言间调用函数,对于像C/C++等语言来说,互操作性是与生俱来的,但对于像Java、C#、Dart之类的具有运行时的托管型语言来说调用非托管型语言比较容易(JNI、DllLink、FFI),互相间的调用则比较苦难,以下是几种方式:
- 双方运行服务器然后互相调用。无论是gRPC、事件队列驱动等。优点各自运行一个微服务、或者加一台MQ,互不打扰。缺点资源代价比较高,如果是简单的需求就很麻烦。
- 运行时模拟。像IKVM在本运行时对另一个语言的实现。优点无依赖,缺点不可维护
- 将对方变成Native Code后,再调用。dotnet平台的Native AOT与JVM平台的GraalVM Native Image都能编译成可执行或共享库文件,而这时候再由对方调用就很顺畅。
我将用最少见的C#调用Native Image编译后的Java举例
-
添加类库、在Java主类中编写入口或EntryPoint
@CEntryPoint(name = "toSM2")
static CCharPointer toSM2(IsolateThread thread, CCharPointer cStr, int i) {
String jStr = CTypeConversion.toJavaString(cStr);
return SM2jar.toSM2(jStr);
}
- 编译
echo 'Main-Class: Native' > manifest.txt
javac -cp 'SM2.jar' Native.class
jar cfm native.jar .\manifest.txt -C . .
native-image -cp 'SM2.jar;native.jar' --shared -o libtoSM2
- 讲dll或so文件放在程序生成目录供C#中调用
static void Main(string[] args)
{
IntPtr prms = new IntPtr(0);
IntPtr isolate = new IntPtr(0);
IntPtr thread;
if(graal_create_isolate(IntPtr.Zero,out isolate,out thread) != 0)
{
Console.WriteLine("error");
return;
}
string c = Marshal.PtrToStringAnsi(toSM2(thread, "292813122"));
Console.WriteLine(c);
graal_tear_down_isolate(thread);
}
[DllImport("libtoSM2")]
static extern IntPtr toSM2(IntPtr isolate,string str);
[DllImport("libtoSM2")]
static extern int graal_create_isolate(IntPtr prms,out IntPtr isolate,out IntPtr thread);
[DllImport("libtoSM2")]
static extern int graal_tear_down_isolate(IntPtr thread);