Экспорт функций
Руководство по экспорту функций из вашего плагина для использования другими языковыми модулями в Plugify.
В экосистеме Plugify плагины на Go могут экспортировать функции, чтобы их могли вызывать другие плагины. Это руководство объясняет, как определять и экспортировать функции в Go, и содержит примеры.
Базовое сопоставление типов
В таблице показано, какими типами Go представлены типы Plugify:
| Тип C++ | Тип Go | Псевдоним Plugify | Поддержка ссылок |
|---|---|---|---|
| void | void (в Go не используется) | void | ❌ |
| bool | bool | bool | ✅ |
| char | int8 | char8 | ✅ |
| char16_t | uint16 | char16 | ✅ |
| int8_t | int8 | int8 | ✅ |
| int16_t | int16 | int16 | ✅ |
| int32_t | int32 | int32 | ✅ |
| int64_t | int64 | int64 | ✅ |
| uint8_t | uint8 | uint8 | ✅ |
| uint16_t | uint16 | uint16 | ✅ |
| uint32_t | uint32 | uint32 | ✅ |
| uint64_t | uint64 | uint64 | ✅ |
| uintptr_t | uintptr | ptr64 | ✅ |
| uintptr_t | uintptr | ptr32 | ✅ |
| float | float32 | float | ✅ |
| double | float64 | double | ✅ |
| void* | именованный тип функции | function | ❌ |
| plg::string | string | string | ✅ |
| plg::any | any | any | ✅ |
| plg::vector<bool> | []bool | bool[] | ✅ |
| plg::vector<char> | []int8 | char8[] | ✅ |
| plg::vector<char16_t> | []uint16 | char16[] | ✅ |
| plg::vector<int8_t> | []int8 | int8[] | ✅ |
| plg::vector<int16_t> | []int16 | int16[] | ✅ |
| plg::vector<int32_t> | []int32 | int32[] | ✅ |
| plg::vector<int64_t> | []int64 | int64[] | ✅ |
| plg::vector<uint8_t> | []uint8 | uint8[] | ✅ |
| plg::vector<uint16_t> | []uint16 | uint16[] | ✅ |
| plg::vector<uint32_t> | []uint32 | uint32[] | ✅ |
| plg::vector<uint64_t> | []uint64 | uint64[] | ✅ |
| plg::vector<uintptr_t> | []uintptr | ptr64[] | ✅ |
| plg::vector<uintptr_t> | []uintptr | ptr32[] | ✅ |
| plg::vector<float> | []float32 | float[] | ✅ |
| plg::vector<double> | []float64 | double[] | ✅ |
| plg::vector<plg::string> | []string | string[] | ✅ |
| plg::vector<plg::any> | []any | any[] | ✅ |
| plg::vector<plg::vec2> | []plugify.Vector2 | vec2[] | ✅ |
| plg::vector<plg::vec3> | []plugify.Vector3 | vec3[] | ✅ |
| plg::vector<plg::vec4> | []plugify.Vector4 | vec4[] | ✅ |
| plg::vector<plg::mat4x4> | []plugify.Matrix4x4 | mat4x4[] | ✅ |
| plg::vec2 | plugify.Vector2 | vec2 | ✅ |
| plg::vec3 | plugify.Vector3 | vec3 | ✅ |
| plg::vec4 | plugify.Vector4 | vec4 | ✅ |
| plg::mat4x4 | plugify.Matrix4x4 | mat4x4 | ✅ |
Экспорт функций в Go
Чтобы экспортировать функцию, пометьте её комментарием //plugify:export Name. Функция должна быть экспортируемой в смысле Go (начинаться с заглавной буквы). Генератор go-plugify создаёт для неё cgo-обёртку __Name и описывает функцию в манифесте плагина, где funcName равен имени этой обёртки.
Использование генератора для упрощения экспорта функций
Генератор из go-plugify (его вызывает ваш generator.go, см. ниже) упрощает экспорт функций Go:
- Сканирует пакет плагина: находит функции с комментарием
//plugify:export. - Создаёт манифест: записывает манифест
.ppluginс сигнатурами этих функций. - Генерирует файлы: записывает
autoexports.goиautoexports.hс cgo-обёртками, которые преобразуют типы Plugify в типы Go.
Маршалинг вручную писать не нужно.
Простой пример
Простой пример экспорта функции в плагине на Go:
Определение функции
Манифест плагина
Генератор описывает функцию в разделе methods манифеста:
Сгенерированный код
Сгенерированный autoexports.go содержит обёртку, которую вызывают другие плагины:
Этот код преобразует типы Plugify в типы Go и позволяет вызывать функцию из других плагинов.
Сложный пример: Экспорт сложных функций
Пример экспорта функции со сложными типами параметров и возвращаемого значения:
Определение функции
Манифест плагина
Сгенерированный код
Строки и срезы приходят как контейнеры Plugify; обёртка преобразует их в значения Go, а результат обратно:
Этот код преобразует типы Plugify в типы Go и позволяет вызывать функцию из других плагинов.
Экспорт функций с параметрами по ссылке
Plugify поддерживает параметры по ссылке (выходные или входно-выходные), через которые функция может изменить значения и вернуть их вызывающему. В Go такие параметры передаются указателями.
Определение функции с параметрами по ссылке
Манифест плагина с параметрами по ссылке
В манифесте параметры по ссылке помечены "ref": true:
Сгенерированный код для параметров по ссылке
Генератор создаёт обёртки, которые передают указатели:
Поддержка параметров по ссылке
Параметры по ссылке работают с большинством типов Plugify, как показано в столбце «Поддержка ссылок» таблицы сопоставления типов. Следующие типы не поддерживают ссылки:
void(не может передаваться по ссылке)function(типы колбэков)
Все остальные типы, включая примитивы, строки, срезы и структуры, передаются по ссылке через указатели.
Обработка обратных вызовов (Callbacks)
Plugify позволяет экспортировать функции, принимающие колбэки. Тип колбэка должен быть именованным типом функции Go:
Определение функции
Манифест плагина
Генератор выносит сигнатуру колбэка в prototypes и ссылается на неё по имени:
Сгенерированный код
Обёртка превращает нативный указатель на функцию в значение вашего типа функции Go:
Автоматизация генерации манифеста и экспорта
Манифест и код экспорта не пишутся вручную: вы помечаете функции комментариями //plugify:export Name и запускаете go generate. Генератор разбирает пакет и записывает:
- Манифест плагина (файл
.pplugin) со всеми экспортируемыми методами - Файлы экспорта (
autoexports.goиautoexports.h) с кодом маршалинга
Преимущества автоматической генерации
- Без ручного JSON: сигнатуры функций берутся из кода
- Полная информация о типах: типы параметров и возвращаемых значений сопоставляются автоматически
- Автоматический маршалинг: генерируются обёртки с преобразованием типов
- Меньше ошибок: код и манифест не расходятся
- Простая поддержка: изменения сигнатур попадают в манифест при следующей генерации
Настройка: Создание вашего генератора
Генератор входит в пакет go-plugify, но запускается из вашего собственного generator.go, который вызывает plugify.Generate(). В шаблоне Go-плагина этот файл уже есть:
Строка //go:build ignore исключает generator.go из сборки плагина. Директива //go:generate, которая его запускает, пишется в одном из исходных файлов плагина, например в main.go:
description, author, website и license являются обязательными полями манифеста: без этих флагов Plugify отклонит сгенерированный манифест.
Использование комментария //plugify:export
Пометьте экспортируемые функции комментарием //plugify:export FuncName:
Запуск генератора
Запустите генератор командой go generate:
Или вручную с теми же флагами:
Затем соберите плагин как обычно (go build -buildmode=c-shared).
Как это работает
- Генерация: парсер Go ищет в пакете комментарии
//plugify:export - Анализ типов: из сигнатур функций типы Go сопоставляются с типами Plugify (например,
int32→int32,[]string→string[]) - Манифест: создаётся файл
.ppluginсо всеми экспортируемыми методами - Код экспорта: создаются
autoexports.goиautoexports.hс кодом маршалинга
Лучшие практики
- Используйте
//plugify:export: помечайте функции для генерации комментарием//plugify:export FuncName. - Заведите файл генератора: свой
generator.go, вызывающийplugify.Generate(). - Следуйте соглашениям о типах: используйте типы из таблицы сопоставления.
- Документируйте функции: комментариями Go к экспортируемым функциям.
- Используйте
go generate: перегенерируйте манифест и файлы экспорта после изменений. - Тестируйте: проверяйте, что экспортируемые функции работают при вызове из других плагинов.
- Держите сгенерированные файлы актуальными: перегенерируйте после любого изменения сигнатур.
Заключение
Генератор из go-plugify упрощает экспорт функций в плагинах на Go. Пометив функции комментариями //plugify:export и запустив go generate, вы получаете и манифест плагина, и код экспорта с маршалингом типов. JSON не нужно писать вручную, ошибок меньше. Сложный маршалинг колбэков генератор тоже берёт на себя.