7
2

More than 5 years have passed since last update.

Dart を拡張しよう (1) Dart Native Extension

Last updated at Posted at 2018-12-20

Dart で プログラムを書く場合、 Package 不足を感じることがあると思います。
そのような場合、 他言語のPackage を 移植することになると思います。

Dart と 拡張機能については、
Flutter Cordova Dart:io Html5 WASM などなど
意外とパターンが多いので、10回くらいに分けて、紹介していきます。

今回は、Dart:ioでの、拡張方法を見ていきます。

Mac で DartからC言語で書かれた関数を読んでみる。

C言語側の関数を用意する

足し算
int calcSum(int x, int y) 
{
  return x+y;
}

足し算をする関数です。これを、Dartから読んでみます。

単純な関数ですが、この関数を実現できれば、他の関数も同様の方法で、
実現できると思います。

Dart側のインターフェイスを用意する

libhello.dart
library libhello;

import 'dart-ext:hello';

int sum(int x, int y) native "sum";

C言語 の関数に対応する Dartのメソッドを用意します。

C言語側でDLLを作成する

libhello.cpp
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "include/dart_api.h"
#include "include/dart_native_api.h"

int calcSum(int x, int y) 
{
  return x+y;
}

//
// https://www.dartlang.org/articles/dart-vm/native-extensions
//
Dart_NativeFunction ResolveName(Dart_Handle name, int argc, bool *auto_setup_scope);

DART_EXPORT Dart_Handle hello_Init(Dart_Handle parent_library)
{
  printf(">> called hello_init\n");
  if (Dart_IsError(parent_library))
  {
    printf(">>>> error in parent_library\n");
    return parent_library;
  }

  Dart_Handle result_code = Dart_SetNativeResolver(parent_library, ResolveName, NULL);
  if (Dart_IsError(result_code))
  {
    printf(">>>> error in result code\n");
    return result_code;
  }

  return Dart_Null();
}

void sum(Dart_NativeArguments arguments)
{
  printf(">> called sum\n");
  Dart_EnterScope();
  Dart_Handle arg1 = Dart_GetNativeArgument(arguments, 0);
  Dart_Handle arg2 = Dart_GetNativeArgument(arguments, 1);

  int64_t x;
  int64_t y;
  if (Dart_IsError(Dart_IntegerToInt64(arg1, &x)))
  {
    Dart_ThrowException(Dart_NewStringFromCString("error at x\n"));
  }
  if (Dart_IsError(Dart_IntegerToInt64(arg2, &y)))
  {
    Dart_ThrowException(Dart_NewStringFromCString("error at y\n"));
  }

  printf(">>>> x=%lld y=%lld\n", x, y);
  Dart_SetReturnValue(arguments, Dart_NewInteger(calcSum(x,y)));
  Dart_ExitScope();
}

Dart_NativeFunction ResolveName(Dart_Handle name, int argc, bool *auto_setup_scope)
{
  printf(">> called resolve name\n");
  if (!Dart_IsString(name))
  {
    return NULL;
  }
  if (auto_setup_scope == NULL)
  {
    return NULL;
  }

  Dart_NativeFunction result = NULL;
  Dart_EnterScope();
  const char *cname;
  Dart_StringToCString(name, &cname);

  if (0 == strcmp(cname, "sum"))
  {
    result = sum;
  }

  Dart_ExitScope();
  return result;
}

と、こんな感じです。

初期化時に、DART_EXPORT Dart_Handle {ライブラリー名}_Init(Dart_Handle parent_library)
が呼ばれる事と、

Dart_NativeFunction ResolveName(Dart_Handle name, int argc, bool *auto_setup_scope)
にて、関数名と実際に動作する関数を結びつける事がわかれば、そんなに難しいコードでもないと思います。

サンプルコードのビルド For Mac

mkdir build
cd build
cmake ..
make .
cd ..
cp ./build/libhello.dylib ./
dart main.dart

として、 CMake を 実行してみてください。
無事、Dart VM から呼び出せる MAC 用の DLL が作成されます。

PS

Flutter に 関しては、 Dart Native Extension は、 サポートされていません。
FFI が 検討されています。

次回

「Windows でビルド」 or 「非同期メソッドについて」 or 「Dart と WASM との 連携」 or 「FFI」 について解説します。

7
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2