Написание функций модуля
Основной причиной написания модуля Emacs является предоставление дополнительных функций программам Lisp, которые загружают модуль. В этом подразделе описывается, как писать такие функции модуля.
Функция модуля имеет следующий общий вид и сигнатуру:
- Функция: emacs_value emacs_function (emacs_env *env, ptrdiff_t nargs, emacs_value *args, void *data)
-
Аргумент env предоставляет указатель на среду API, необходимую для доступа к объектам и функциям Emacs. Аргумент nargs — это требуемое количество аргументов, которое может быть нулевым (см.
make_functionниже для более гибкого указания количества аргументов), а args — указатель на массив аргументов функции. Аргумент data указывает на дополнительные данные, необходимые функции, которые были подготовлены при вызовеmake_function(см. ниже) для создания функции Emacs изemacs_function.Функции модуля используют тип
emacs_valueдля обмена объектами Lisp между Emacs и модулем (см. Значения модуля). API, описанная ниже и в последующих подразделах, предоставляет средства для преобразования между базовыми типами данных C и соответствующими объектамиemacs_value.Функция модуля всегда возвращает значение. Если функция возвращает значение нормально, код Lisp, который её вызвал, увидит объект Lisp, соответствующий значению
emacs_value, возвращенному функцией. Однако, если пользователь ввёл C-g, или если функция модуля или её вызываемые функции сигнализировали об ошибке или вышли нелокально (см. Нелокальные вызовы модуля), Emacs проигнорирует возвращаемое значение и прекратит работу или бросит исключение, как и при возникновении аналогичных ситуаций в коде Lisp.Заголовочный файл emacs-module.h предоставляет тип
emacs_functionкак псевдоним типа указателя на функцию модуля.
После написания кода C для функции модуля необходимо создать из неё объект Lisp-функции, используя функцию make_function, указатель на которую предоставляется в среде (напомню, что указатель на среду возвращается get_environment). Это обычно делается в функции инициализации модуля (см. Функция инициализации модуля) после проверки совместимости API.
- Функция: emacs_value make_function (emacs_env *env, ptrdiff_t min_arity, ptrdiff_t max_arity, emacs_function func, const char *docstring, void *data)
-
Это возвращает функцию Emacs, созданную из функции C func, сигнатура которой описана для
emacs_functionвыше. Аргументы min_arity и max_arity указывают минимальное и максимальное количество аргументов, которые может принять func. Аргумент max_arity может иметь специальное значениеemacs_variadic_function, что делает функцию принимающей неограниченное количество аргументов, подобно ключевому слову&restв Lisp (см. Список аргументов).Аргумент data — способ передачи произвольных дополнительных данных функции func при её вызове. Любой указатель, переданный
make_function, будет передан без изменений функции func.Аргумент docstring указывает строку документации для функции. Она должна быть либо строкой ASCII, либо строкой UTF-8 с кодировкой отличной от ASCII, либо указателем
NULL; в последнем случае у функции не будет документации. Строка документации может заканчиваться строкой, указывающей объявленную конвенцию вызова, см. Документация функции.Поскольку каждая функция модуля должна принимать указатель на среду в качестве первого аргумента, вызов
make_functionможет быть сделан из любой функции модуля, но обычно вы хотите сделать это из функции инициализации модуля, чтобы все функции модуля были известны Emacs после загрузки модуля.
Наконец, вы должны связать Lisp-функцию с символом, чтобы код Lisp мог вызывать вашу функцию по имени. Для этого используйте функцию API модуля intern (см. intern), указатель на которую также предоставляется в среде, к которой могут получить доступ функции модуля.
Комбинируя вышеупомянутые шаги, код, который обеспечивает возможность вызова функции C module_func в качестве функции module-func из Lisp, будет выглядеть так, как часть функции инициализации модуля:
emacs_env *env = runtime->get_environment (runtime);
emacs_value func = env->make_function (env, min_arity, max_arity,
module_func, docstring, data);
emacs_value symbol = env->intern (env, "module-func");
emacs_value args[] = {symbol, func};
env->funcall (env, env->intern (env, "defalias"), 2, args);
Это делает символ module-func известным Emacs вызовом env->intern, а затем вызывает defalias из Emacs, чтобы привязать функцию к этому символу. Обратите внимание, что вместо defalias можно использовать fset; различия описаны в defalias.
Функции модуля, включая функцию emacs_module_init (см. функция инициализации модуля), могут взаимодействовать с Emacs только путём вызова функций среды из некоторого активного указателя emacs_env, в то время, когда они вызываются напрямую или косвенно из Emacs. Другими словами, если функция модуля хочет вызывать Lisp-функции или примитивы Emacs, преобразовывать объекты emacs_value в типы данных C и обратно (см. Значения модуля) или взаимодействовать с Emacs любым другим способом, некоторый вызов из Emacs к emacs_module_init или функции модуля должен быть в стеке вызовов. Функции модуля не могут взаимодействовать с Emacs во время выполнения сборки мусора; см. Сборка мусора. Они могут взаимодействовать с Emacs только из потоков интерпретатора Lisp (включая основной поток), созданных Emacs; см. Потоки. Команда командной строки --module-assertions может обнаруживать некоторые нарушения вышеуказанных требований. См. Initial Options в The GNU Emacs Manual.
Используя API модуля, можно определить более сложные типы функций и данных: встроенные функции, макросы и т.д. Однако, полученный код C будет громоздким и трудно читаемым. Поэтому мы рекомендуем ограничивать код модуля, который создаёт функции и структуры данных, абсолютным минимумом и оставлять остальное для пакета Lisp, который будет сопровождать ваш модуль, потому что выполнение этих дополнительных задач в Lisp намного проще и даст гораздо более читаемый код. Например, с учётом функции модуля module-func, определённой выше, один из способов создания макроса module-macro на её основе — с помощью простого Lisp-обёртки:
(defmacro module-macro (&rest args) "Documentation string for the macro." (module-func args))
Пакет Lisp, идущий с вашим модулем, может затем загрузить модуль с помощью примитива load (см. Динамические модули) при загрузке пакета в Emacs.
По умолчанию, функции модуля, созданные с помощью make_function, не являются интерактивными. Для их интерактивности можно использовать следующую функцию.
- Функция: void make_interactive (emacs_env *env, emacs_value function, emacs_value spec)
Эта функция, доступная начиная с Emacs 28, делает функцию function интерактивной с использованием спецификации интерактивного режима spec. Emacs интерпретирует spec как аргумент формы
interactive. Использование интерактивного режима, и см. Интерактивные коды. function должна быть функцией модуля Emacs, возвращаемойmake_function.
Обратите внимание, что нет встроенной поддержки модулей для извлечения спецификации интерактивного режима функции модуля. Используйте функцию interactive-form для этого. Использование интерактивного режима. После того, как вы сделали функцию модуля интерактивной с помощью make_interactive, её нельзя сделать неинтерактивной.
Если вы хотите выполнить некоторый код при сборке мусора объекта функции модуля (т.е. объекта, возвращённого make_function ), вы можете установить финализатор функции. Финализаторы функций доступны с Emacs 28. Например, если вы передали некоторую структуру, выделенную в куче, аргументу data функции make_function , вы можете использовать финализатор для освобождения структуры. См. (libc)Базовое выделение, и см. (libc)Освобождение после malloc. Функция финализатора имеет следующую сигнатуру:
void finalizer (void *data)
Здесь data получает значение, переданное в data при вызове make_function. Обратите внимание, что финализатор не может взаимодействовать с Emacs каким-либо способом.
Сразу после вызова make_function у вновь созданной функции нет финализатора. Используйте set_function_finalizer для добавления финализатора, если это необходимо.
- Функция: void emacs_finalizer (void *ptr)
Заголовочный файл emacs-module.h предоставляет тип
emacs_finalizerкак псевдоним типа функции финализатора Emacs.
- Функция: emacs_finalizer get_function_finalizer (emacs_env *env, emacs_value arg)
Эта функция, доступная с Emacs 28, возвращает финализатор функции, связанный с функцией модуля, представленной arg. arg должен ссылаться на функцию модуля, то есть объект, возвращаемый
make_function. Если финализатор не связан с функцией, возвращаетсяNULL.
- Функция: void set_function_finalizer (emacs_env *env, emacs_value arg, emacs_finalizer fin)
Эта функция, доступная с Emacs 28, устанавливает финализатор функции, связанный с функцией модуля, представленной arg, в fin. arg должен ссылаться на функцию модуля, то есть объект, возвращаемый
make_function. fin может бытьNULLдля очистки финализатора функции arg или указателем на функцию, которая будет вызвана при сборке мусора объекта, представленного arg. Не более одного финализатора функции может быть задано для каждой функции; если arg уже имеет финализатор, он заменяется на fin.
Copyright © 1990-1996, 1998-2022 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Module-Functions.html