Русский
Русский
English
Статистика
Реклама

Cgo

Вызов кода Go из Dart с использованием cgo и Dart FFI на простом примере

09.06.2021 16:12:52 | Автор: admin

Ключевой мотивацией для написания данной статьи является факт сильного недостатка информации (особенно в русскоязычном сообществе) по использованию cgo и Dart FFI для вызова Go кода из языка Dart.

Язык Dart, не смотря на свою возрастающую популярность, на данный момент до сих пор не имеет такого же большого сообщества, как у языка Go. Dart заточен под выполнение других задач, по этому он иногда не содержит тех реализаций и функционала, который уже есть на языке Go.

В случае если можно можно избежать экспорта go кода в Dart (например экспортировать готовую c библиотеку), то лучше воспользоваться такой возможностью и не использовать cgo. Однако, могут возникать случаи, когда перегонка go в dart кода является оптимальным решением (например вы уже знакомы с Go и Dart, и не хотите писать код на C, в таком случае есть смысл задуматься об использованием cgo и Dart FFI).

В данной статье на простом примере будет показано как можно вызвать код Go из языка Dart (например в приложениях на Flutter).

Что должно быть установлено:

  • Go

  • Dart

  • Текстовый редактор/IDE (я буду использовать VSCode, так как это самая популярная среда среди Dart и Go сообщества, так же будут установлены специальные плагины для поддержки языков Go и Flutter)

Шаг 1 - Создаем пустое консольное приложение на Dart

Вызываем Command Palette клавишей F1 и создаем новый проект на Dart, выбираем опцию Console Application (данный формат использован для примера, далее код на cgo можно будет использовать в том числе из Flutter проектов или других форматов приложений на Dart).

Назвать приложение можно в целом как угодно, я выбрал название cgo_dartffi_helloworld, исключительно для тестового примера. (Нам потребуется именно директория с проектом на Dart, так как мы будем добавлять ffi в pubspec.yaml файл).

Прожигаем кнопку создать и переходим в директорию со новоиспеченным проектом.

Шаг 2 - Добавляем ffi в yaml файл

Далее нам необходимо добавить ffi в yaml файл для возможности использования go кода из dart.

name: cgo_dartffi_helloworlddescription: A sample command-line application.version: 1.0.0environment:  sdk: '>=2.12.0 <3.0.0'dependencies:  path: ^1.8.0  ffi: ^0.1.3dev_dependencies:  pedantic: ^1.10.0  test: ^1.16.0

Шаг 3 - Создаем .go файл содержащий экспортируемую функцию

Далее необходимо создать файл на go, (в например в руте директории с проектом, например lib.go) который будет содержать функцию для экспорта в Dart. В данном примере эта функция - HelloFromGo().

// filename: lib.gopackage mainimport "C"//export HelloFromGofunc HelloFromGo() *C.char {message := "Hello to dart lang from go"return C.CString(message)}func main() {}

Стоит быть крайне аккуратными при написании кода cgo так как большая часть инструментов, включая сборщик мусора перестают работать. В cgo комментарии имеют значение (да, это странно), именно с помощью комментариев можно обозначить функцию которую необходимо экспортировать (используя слово export). Более подробно данные нюансы описаны на официальной странице cgo https://golang.org/cmd/cgo/, ну а мы вернемся к практической стороне вопроса.

Шаг 4 - Собираем динамическую библиотеку из go файла

Далее необходимо открыть терминал и запустить там следующую команду:

go build -buildmode=c-shared -o lib.a lib.go

Данная команда создаст файл lib.a (который и представляет из себя динамическую c библиотеку). Даже для такого небольшого файлика время сборки заставляет ужаснуться (аж целых несколько секунд, в отличии от моментальных сборок на go, еще один из плюсов go, который теряется при использовании cgo).

Шаг 5 - Проверяем наличие необходимых файлов

На данном этапе наша директория должна выглядеть примерно следующим образом:

Она должна содержать следующие файлы:

  • Измененный pubspec.yaml файл

  • lib.h, lib.a файлы созданные из файла lib.go

  • директорию bin с дефолтным файлом библиотеки dart (туда мы сейчас и отправимся)

Шаг 6 - Прописываем биндинги на cgo функцию в Dart коде

Далее идёт самая сложная (и потенциально вызывающая ошибки) часть процедуры. Наличие ошибок возможно при несоответствии имён (так как нет возможности их проверить), по этому на данном этапе необходимо быть особенно аккуратными.

  • 6.1 - удаляем всё содержимое файла bin/cgo_dartffi_helloworld.dart и начинаем там писать с чистого листа

  • 6.2 - импортируем необходимые библиотеки (для нас это ffi и utf8 для передачи текста)

import 'dart:ffi' as ffi;import 'package:ffi/src/utf8.dart';
  • 6.3 - открываем динамическую библиотеку

final dylib = ffi.DynamicLibrary.open('lib.a');
  • 6.4 - привязываем нашу функции к функции в dart

typedef HelloFromGo = ffi.Pointer<Utf8> Function();typedef HelloFromGoFunc = ffi.Pointer<Utf8> Function();final HelloFromGo _finalFunction = dylib    .lookup<ffi.NativeFunction<HelloFromGoFunc>>('HelloFromGo')    .asFunction();
  • 6.5 - создаем метод который проверит вызов нашей функции (обратите внимание, метод .toDartString переводит стринг из формата C в формат Dart):

void main() {  print(_finalFunction().toDartString());}

Таким образом мы создали функцию на go, которая передает string в язык Dart.

Далее при написании своих функций следует учитывать, что форматы данных в языках Go, C и Dart могут отличаться (и зачастую так происходит), что приводит к необходимости использовать различные конвертации на стороне go/dart кода, более подробно можно ознакомиться по следующим ссылкам:

Полный код на Dart:

import 'dart:ffi' as ffi;import 'package:ffi/src/utf8.dart';final dylib = ffi.DynamicLibrary.open('lib.a');typedef HelloFromGo = ffi.Pointer<Utf8> Function();typedef HelloFromGoFunc = ffi.Pointer<Utf8> Function();final HelloFromGo _finalFunction = dylib    .lookup<ffi.NativeFunction<HelloFromGoFunc>>('HelloFromGo')    .asFunction();void main() {  print(_finalFunction().toDartString());}

При необходимости передавать параметры в вызываемую функцию можно использовать поинтеры и объявить их в вызываемой функции, например:

typedef GetHash = Pointer<Utf8> Function(Pointer<Utf8> str);typedef GetHashFunc = Pointer<Utf8> Function(Pointer<Utf8> str);final GetHash _getHashGoFunction =    _lib.lookup<NativeFunction<GetHashFunc>>('GetHash').asFunction();

Главное помнить, что необходимо проверять форматы передаваемых данных.

Подробнее..
Категории: C , Go , Dart , Flutter , Golang , Cgo , Ffi

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru