2020年2月19日 星期三

dart 使用 ffi 和 c 語言溝通

參考官網文章: https://dart.dev/guides/libraries/c-interop
1. 使用 linux 作業系統, 安裝好dart sdk ,  c 語言編譯程式加上  cmake
2. 新增一個 dart 專案,  專案內新增一個 hello 目錄 , 進入 hello 目錄, 準備要編譯成  hello 模組(libhello.so):
2.1 先在 hello 目錄裡面編輯一個檔案 CMakeLists.txt:
cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
project(hello_library VERSION 1.0.0 LANGUAGES C)
add_library(hello_library SHARED hello.c hello.def)
add_executable(hello_test hello.c)
set_target_properties(hello_library PROPERTIES
    PUBLIC_HEADER hello.h
    VERSION ${PROJECT_VERSION}
    SOVERSION 1
    OUTPUT_NAME "hello"
    XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Hex_Identity_ID_Goes_Here"
)

2.2. 為了指示要將函數(testWork, add, reverse 等等) 導出來(export)使用, 要編輯一個文字檔案 hello.def:
LIBRARY   hello
EXPORTS
   testWork
   add
   reverse

2.3. 編輯一個 c 程式,用來宣告函式原型的標頭檔 hello.h:
void testWork( );
int  add(int a, double b);
char *reverse(char *str);

2.4. 編輯 c 模組程式的原始碼  hello.c:
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include "hello.h"
int main( ) {
    testWork( );
    printf("3 + 5 = %d\n", add(3, 5));
    printf("%s\n",reverse("Hello-String") );
    return 0;
}
void testWork( ) {
    printf("It works!\n");
}
int add(int a, double b) {
    return a + b;
}
char *_bufferPointer = NULL;
int _buffrLength = 0;
char *reverse(char *src) {
    int size = strlen(src);
    if(size == 0) return NULL;
    else if(size > _buffrLength) { // allocate if space not enough
        if(_bufferPointer != NULL) free(_bufferPointer);
        _bufferPointer = (char *)malloc(size + 1);//from heap
        _buffrLength = size;// maximum characters to hold
    }
    _bufferPointer[size] = '\0';// eos
    for (int i = 0; i < size; i++) _bufferPointer[size - 1 - i] = src[i];// reverse
    return _bufferPointer;
}

2.5 上述檔案都編輯完成後就能執行 make 作業, 製作成動態程式庫 (hello/libhello.so)
   cmake  .
   make

3. 在專案內新增 pubspec.yaml 檔案, 完成後, 開啟終端機執行  pub get 準備使用 ffi:
name: cinterop
version: 0.0.1
description: >-
  An example of calling C code from Dart through FFI
author: MyName<email@google.com>
environment:
  sdk: '>=2.6.0 <3.0.0'
dependencies:
  ffi: ^0.1.3
dev_dependencies:
  test: ^1.5.3

4.編輯一個透過 ffi  界面呼叫 c 語言的 dart 範例程式 main.dart:
import 'dart:ffi';
import 'package:ffi/ffi.dart';
main( ) {
  final libso  = DynamicLibrary.open('hello/libhello.so');
  final testFun= libso.lookup<NativeFunction<Void Function( )>>('testWork').
                 asFunction<void Function()>();
  final addFun = libso.lookup<NativeFunction<Int32 Function(Int32, Double)>>('add').
                 asFunction<int Function(int,double)>( );
  final revFun = libso.lookup<NativeFunction<Pointer<Utf8> Function(Pointer<Utf8>)>>('reverse').
                 asFunction<Pointer<Utf8> Function(Pointer<Utf8>)>( );
  final c2dartString   = (Pointer<Utf8> _pointer) => Utf8.fromUtf8(_pointer);
  final dartstring2c   = (String _string) => Utf8.toUtf8(_string);
  final reverse = (String _string) => c2dartString(revFun(dartstring2c(_string)));
  testFun( );
  print('adder: 334 + 5 = ${addFun(334, 5.0)}');
  for(int i=0; i<20 ; i++)   print('${reverse("$i.Hello-string")}');
}

5. 用  dart VM 執行上述 main.dart 看看:  /pathToDartSDK/bin/dart   main.dart
It works!
adder: 334 + 5 = 339
gnirts-olleH.0
gnirts-olleH.1
gnirts-olleH.2
gnirts-olleH.3
gnirts-olleH.4
gnirts-olleH.5
gnirts-olleH.6
gnirts-olleH.7
gnirts-olleH.8
gnirts-olleH.9
gnirts-olleH.01
gnirts-olleH.11
gnirts-olleH.21
gnirts-olleH.31
gnirts-olleH.41
gnirts-olleH.51
gnirts-olleH.61
gnirts-olleH.71
gnirts-olleH.81
gnirts-olleH.91

後記: dart SDK 的 dart2native 可以將原始碼轉成可執行檔, 不用 dart VM, 直接就能執行:
/pathToDartSDK/bin/dart2native  main.dart  &&   ./main.exe

沒有留言:

張貼留言

使用 pcie 轉接器連接 nvme SSD

之前 AM4 主機板使用 pcie ssd, 但主機板故障了沒辦法上網, 只好翻出以前買的 FM2 舊主機板, 想辦法讓老主機復活, 但舊主機板沒有 nvme 的界面, 因此上網買了 pcie 轉接器用來連接 nvme ssd, 遺憾的是 grub2 bootloader 無法識...