DartでNative extensionsを書く


## Dartライブラリを作る

いつもどおりライブラリのディレクトリを作り配置します。

```
pubspec.yaml
analysis_options.yaml
example/
lib/
test/
```


## Dart Native APIのヘッダーファイルを持ってくる
Dartをインストールしたディレクトリにincludeというディレクトリがあるので、それをlib/に持ってきます。
Dartのインストールディレクトリはこんな感じで調べられます。

```
riku@rikuubuntu:~$ which dart
/usr/bin/dart

riku@rikuubuntu:~$ ls /usr/bin/dart -la
lrwxrwxrwx 1 root root 20  6月 21 06:10 /usr/bin/dart -> ../lib/dart/bin/dart

riku@rikuubuntu:~$ ls /usr/lib/dart/
LICENSE  README  bin  dartdoc_options.yaml  include  lib  version

```

## CMakeLists.txtを書く
作ってるライブラリがyour_cool_libだとするとこれが最小限だと思います。

```
cmake_minimum_required(VERSION 3.14)

PROJECT(your_cool_lib)

set(CMAKE_VERBOSE_MAKEFILE on)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_VERBOSE_MAKEFILE on)

add_library(your_cool_lib SHARED your_cool_lib.cpp your_cool_lib.h)


```



## ヘッダーとcppを書く

ヘッダーはこれだけあればいいと思います。

```
#ifndef DART_NATIVE_EXAMPLE_LIBRARY_H
#define DART_NATIVE_EXAMPLE_LIBRARY_H

#include "include/dart_api.h"
#include "include/dart_native_api.h"

DART_EXPORT Dart_Handle rocksdb_interop_Init(Dart_Handle parent_library);

#endif
```

C++はいくつかお約束があります。

まずソースの先頭にこういう関数が必要です。
DartからC++ のライブラリを見つけるためのおまじないのようです。

```
// Forward declaration of ResolveName function.
Dart_NativeFunction ResolveName(Dart_Handle name, int argc, bool *auto_setup_scope);

// The name of the initialization function is the extension name followed
// by _Init.
DART_EXPORT Dart_Handle your_cool_lib_Init(Dart_Handle parent_library) {
    if (Dart_IsError(parent_library)) return parent_library;

    Dart_Handle result_code =
            Dart_SetNativeResolver(parent_library, ResolveName, NULL);
    if (Dart_IsError(result_code)) return result_code;

    return Dart_Null();
}


Dart_Handle HandleError(Dart_Handle handle) {
    if (Dart_IsError(handle)) Dart_PropagateError(handle);
    return handle;
}

```



本体はこんな感じです。
DartとC++では、同じ名前の型でも別物なので全て変換しなければいけません。
また、C++の中では全てのDartオブジェクトはDart_Hanldeという型として扱われます。
Dart->C++はDart_HogeToFugaという関数で変換します。
C++->DartはDart_NewPiyoという関数で変換します。
最後にDart_SetReturnValue(arguments, results);という構文で戻り値をセットします。

```
// int Add(int num1, int num2)
void Add(Dart_NativeArguments arguments) {
    uint64_t num1;
    uint64_t num2;

    Dart_IntegerToUint64(Dart_GetNativeArgument(arguments, 0), &num1);
    Dart_IntegerToUint64(Dart_GetNativeArgument(arguments, 1), &num2);

    uint64_t num3 = num1 + num2;

    Dart_Handle results = Dart_NewInteger(num3);
    Dart_SetReturnValue(arguments, results);

}
```


最後に名前を解決する関数が必要です。
ダブルクォーテーションの中のAddがあとで出てくる
```int add(int num1, int num2) native "Add";```
のダブルクォーテーションの中身です。

```
Dart_NativeFunction ResolveName(Dart_Handle name, int argc, bool *auto_setup_scope) {
// If we fail, we return NULL, and Dart throws an exception.
    if (!Dart_IsString(name)) return NULL;
    Dart_NativeFunction result = NULL;
    const char *cname;
    HandleError(Dart_StringToCString(name, &cname));

    if (strcmp("Add", cname) == 0) result = Add;
    return result;
}
```

## ビルド
これでビルドされると思います。

```
cmake .
make
```

ただ、ビルドする時はbuildというディレクトリを作るのがC++erの流儀のようです。
僕はよく知りません。

```
mkdir build
cd build
cmake ..
make
```



## 呼び出すDart側のライブラリ

your_cool_lib.dart
はこんな感じにします。

```
library your_cool_lib;

import "dart-ext:your_cool_lib";

void add(int num1, int num2) native "Add";
```

インポート文ですが、"dart-ext:ライブラリ名"でライブラリを要求すると
「lib${ライブラリ名}.so」というファイル名を検索しに行くようです。
（これはDartというかlinuxの決まり）
なので、ビルドした共有ライブラリは「lib${ライブラリ名}.so」という名前にしておく必要があります。

## 実行する

```
import "package:your_cool_lib/your_cool_lib.dart";
import "package:test/test.dart";


void main() {
  test("Delete", () {
    var i = add(1, 2);
    expect(i, equals(3));
  })
}
```