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

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


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


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

# Mac で DartからC言語で書かれた関数を読んでみる。
https://github.com/kyorohiro/dart.check.nativeext_2018y/tree/master/n01_hello_nativeext


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

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

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

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


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

```dart:libhello.dart
library libhello;

import 'dart-ext:hello';

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

```

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


### C言語側でDLLを作成する

```cpp: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 が 検討されています。


https://github.com/flutter/flutter/issues/2396

https://github.com/dart-lang/sdk/issues/34452

https://github.com/flutter/flutter/issues/7053

https://gist.github.com/mraleph/2582b57737711da40262fad71215d62e#structure-layouts-and-portability

# 次回

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