如何使用 FFI 从 Dart 调用 Go lib

Has*_*sef 10 go dart

我需要在 中编写一个函数GO,将其编译为共享库,然后从 中调用它Dart。例如,要返回一个密钥,如下面的代码所示:

package main

func getKey() string {
    theKey := "123-456-789"
    return theKey
}

func main() {}
Run Code Online (Sandbox Code Playgroud)

Has*_*sef 22

答案是:

第一部分GO 方面:

  1. 我们需要使函数可导出,因此名称应大写并变为GetKey()
  2. 我们需要使用cgo以便我们可以创建共享库,cgo这意味着我们需要:

2.1. 使用import "C"

2.2. 使用注释和函数作为//export GetKey

2.3. 使用C.type接口,在本例中代替string使用C.charand C.CString,因为使用指针是更好的方法,所以使用*C.char

因此,Go问题中的函数应重写为:

// filename: lib.go
package main

import "C"

//export GetKey
func GetKey() *C.char {
    theKey := "123-456-789"
    return C.CString(theKey)
}

func main() {}  
Run Code Online (Sandbox Code Playgroud)
  1. 编译上述内容并创建共享库:
go build -buildmode=c-shared -o lib.a lib.go
Run Code Online (Sandbox Code Playgroud)

第二部分Dart 方面:

  1. 创建pubspec.yaml并将其添加到其依赖项中ffi: ^0.1.3,因此它如下:
name: dart_app
description: A new Dart application.

# The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1

environment:
  sdk: ">=2.7.0 <3.0.0"

dependencies:
  ffi: ^0.1.3
Run Code Online (Sandbox Code Playgroud)
  1. 将生成的文件从 GO 复制lib.alib.hdart projetc 文件夹
  2. 创建 dart 文件来处理 FFI,您需要: 3.1. 导入ffiutf8

3.2. 将 FFI 函数签名定义为typedef get_key_func = ffi.Pointer<Utf8> Function();

3.3. 将 Dart 函数签名定义为typedef GetKey = ffi.Pointer<Utf8> Function();

3.4. 将共享库加载为final dylib = ffi.DynamicLibrary.open('lib.a');

3.5. 将 FFI 和 Dart 函数签名映射为final GetKey getKey dylib.lookup<ffi.NativeFunction<get_key_func>>('GetKey').asFunction();

3.6. 定义一个函数来执行,并在该函数内执行以下操作:

3.6.1. 调用 Dart 函数并将输出分配给变量,如下所示var addressOf = getKey();

3.6.2. 解码结果,即指针的地址,并从中获取字符串,如下所示:print(addressOf.ref.toString());

因此,Dart 代码将变为:

//file name fficheck.dart
import 'dart:ffi' as ffi; // For FFI
import 'package:ffi/ffi.dart';
import 'package:ffi/src/utf8.dart';

typedef get_key_func = ffi.Pointer<Utf8> Function(); // FFI fn signature
typedef GetKey = ffi.Pointer<Utf8> Function(); // Dart fn signature
final dylib = ffi.DynamicLibrary.open('lib.a');

final GetKey getKey =
    dylib.lookup<ffi.NativeFunction<get_key_func>>('GetKey').asFunction();

void testffi() {
  print("Hi from dart");
  var addressOf = getKey();
  print(addressOf.ref.toString());
}
Run Code Online (Sandbox Code Playgroud)
  1. 创建主 Dart 文件,导入处理 FFI 的文件,并调用所需的函数,如下所示:
// file name lib.dart
import 'fficheck.dart';

main() {
  print("Hello, World!");
  testffi();
}
Run Code Online (Sandbox Code Playgroud)
  1. 执行 dart 文件为dart main.dart

在此输入图像描述

快速注意,不要偏离问题的范围,如果您返回一个字符串,那么如果您从代码中调用它,但从垃圾中调用它,则必须C在 C 代码中显式释放该函数的返回值收集器环境,Java/Kotlin 你不想担心它。

如果释放分配的缓冲区不方便,通常会填充调用者提供的缓冲区:

func GetKey(buff *C.char, n int) int
Run Code Online (Sandbox Code Playgroud)

如果您可以分配内存但不想处理 C 字符串,则可以将缓冲区插入指针并返回大小。

func GetKey(buff **C.char) int
Run Code Online (Sandbox Code Playgroud)