各言語の C 呼び出し
代表的なもの
言語 | 実装方法 |
---|---|
Java | |
Go | cgo を使う |
Python | |
Rust | extern キーワードで容易に呼べる |
Ruby | Ruby-FFI を使う |
Javascript | WebAssembly を使う |
Swift |
Dart は?
Dart から C を呼ぶ方法
(これまで)
Native Extension
Dart 側
library sample_hello;
import 'dart-ext:sample_hello';
void hello() native "Hello";
C++ 側 (一部省略)
DART_EXPORT Dart_Handle sample_hello_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();
}
void hello(Dart_NativeArguments arguments) {
Dart_EnterScope();
printf("Hello\n");
Dart_ExitScope();
}
Dart_NativeFunction ResolveName(Dart_Handle name, int argc, bool* auto_setup_scope) {
if (!Dart_IsString(name) || auto_setup_scope == NULL) return NULL;
Dart_EnterScope();
const char *cname;
Dart_StringToCString(name, &cname);
Dart_NativeFunction result = NULL;
if (strcmp(cname, "hello") == 0) result = hello;
Dart_ExitScope();
return result;
}
深いレベルで拡張可能
都度 ResolveName
する
わかりやすく例をもう一個
void isEven(Dart_NativeArguments arguments) {
Dart_EnterScope();
Dart_Handle arg1 = Dart_GetNativeArgument(arguments, 0);
int64_t input;
if (Dart_IsError(Dart_IntegerToInt64(arg1, &input)))
{
Dart_ThrowException(Dart_NewStringFromCString("Error だよ"));
}
Dart_SetReturnValue(arguments, Dart_NewBoolean(input % 2 == 0));
Dart_ExitScope();
}
引数と返り値の型情報が静的に定義されていない
さて、Flutter では?
現状、Swift/Objective-C, Kotlin/Java を経由する必要がある
たくさんの の思いは?
① 既存ソフトをより統合しやすくしてほしい
◯ 大量のグルーコードがつらい
◯ 低オーバーヘッドがいい
SQLite
Realm
OpenCV
crypto, ssh ... libraries
などが具体例として挙げられている
② 大量のデータを効率よく出し入れしたい
なお、Dart 2.4 から TransferableTypedData が使用できるようになったので、ある程度はそれで間に合いそう
こういう要望にどう応えるか?
「Native Exstention でいいんじゃないの...?」
⇒ Dart VM FFI Vision に理由が述べられていた
【 理由 1 】
名前ベースの API
// dart-lang/sdk/runtime/include/dart_api.h より引用
DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
Dart_SetField(Dart_Handle container, Dart_Handle name, Dart_Handle value);
名前解決がキャッシュされない
AOT コンパイラに厳しい
(最悪の場合を想定したり、手動でアノテーションを付けてまわったりしないといけない)
【 理由 2 】
Reflective Marshaling は効率良くない
void isEmailAddress(Dart_NativeArguments arguments)
void
arguments
⇒ 引数/返り値が静的に型付けされた上での Marshaling の方が効率良い
⇒ その点は FFI が優れている
そこで、dart : ffi
https://github.com/dart-lang/sdk/tree/master/sdk/lib/ffi
Google I/O'19 でも言及あり
We are working on a new foreign function interface.
This should help you reuse existing C and C++ code,
which is important for some critical stuff
ちなみに
we expect that moving Flutter Engine from C API to FFI should significantly reduce overheads associated with crossing the boundary between Dart and native code
どう使えるのか?
import "dart:ffi" as ffi;
import 'dart:io' show Platform;
void main() {
final libHelloWorld = ffi.DynamicLibrary.open("./libHelloWorld.dylib");
final helloWorld = libHelloWorld.lookupFunction
<ffi.Void Function(), void Function()>("helloWorld");
helloWorld();
}
ちなみに、先週、
Flutter stable 版に入った
(Android のみで試験的に触れる)
どういう構成になるのか
Bindings: final helloWorld = libHelloWorld.lookupFunction<ffi.Void Function(), void Function()>("helloWorld");
みたいなのを定義するレイヤーのこと
課題をいくつか紹介
1: 例外を拾えない
⇒ C レイヤーを追加実装する
2: CFE への追加実装
補完や静的解析を行うために、
CFE (Common Front-End) への追加実装が必要。
*Dart2 VM からは、生のソースから Dart を直接実行できず、CFE によって生成された Kernel Binary(dill) を与える必要がある
3: サポート対象のプラットフォーム
待ちきれない人がスケジュールを聞く
⇒ 具体的なスケジュールは示せない。待って。
4: HotReload 下での callback の挙動は..?