Spec-Zone.ru › Elisp

Написание функций модулей

Основная причина написания модуля 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 и модулем (см. Преобразование между значениями Lisp и значениями модулей). API, описанный ниже и в следующих подразделах, предоставляет средства для преобразования между базовыми типами данных C и соответствующими объектами emacs_value.

В теле функции модуля не пытайтесь обращаться к элементам массива args с индексом больше nargs-1: память для массива args выделяется ровно под nargs значений, и обращение за его пределы, скорее всего, приведёт к аварийному завершению модуля. В частности, если значение nargs, переданное функции во время выполнения, равно нулю, она не должна обращаться к args вообще, поскольку в этом случае память для него не выделяется.

Функция модуля всегда возвращает значение. Если функция завершается нормально, код 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, строка с символами вне ASCII, закодированная в UTF-8, либо указатель NULL; в последнем случае у функции не будет документации. Строка документации может заканчиваться строкой, задающей объявленную сигнатуру вызова; см. Строки документации функций.

Поскольку каждая функция модуля должна принимать указатель на среду в качестве первого аргумента, вызов make_function можно было бы выполнить из любой функции модуля, но обычно это делают из функции инициализации модуля, чтобы после загрузки модуля Emacs знал обо всех его функциях.

Наконец, привяжите функцию Lisp к символу, чтобы код Lisp мог вызывать вашу функцию по имени. Для этого используйте функцию API модуля intern (см. intern), указатель на которую также предоставляется в среде, доступной функциям модуля.

С учётом описанных выше шагов код, позволяющий вызывать из Lisp функцию C module_func под именем module-func, будет выглядеть следующим образом в составе функции инициализации модуля:

 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 и обратно (см. Преобразование между значениями Lisp и значениями модулей) или иным образом взаимодействовать с Emacs, в стеке вызовов должен присутствовать вызов из Emacs к emacs_module_init или к функции модуля. Функции модулей не могут взаимодействовать с Emacs во время сборки мусора; см. Сборка мусора. Они могут взаимодействовать с Emacs только из потоков интерпретатора Lisp, созданных Emacs (включая главный поток); см. Потоки. Параметр командной строки --module-assertions позволяет обнаружить некоторые нарушения перечисленных выше требований. См. Начальные параметры в Руководстве GNU Emacs.

С помощью 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) при загрузке пакета в Emacs.

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

Функция: void make_interactive (emacs_env *env, emacs_value function, emacs_value spec)

Эта функция, доступная начиная с Emacs 28, делает функцию function интерактивной, используя спецификацию интерактивности spec. Emacs интерпретирует spec так же, как аргумент формы interactive. См. Использование interactive и Символы кодов для interactive. function должна быть функцией модуля Emacs, возвращённой make_function.

Обратите внимание, что в нативном интерфейсе модулей нет поддержки получения спецификации интерактивности функции модуля. Для этого используйте функцию interactive-form. См. Использование interactive. Сделать функцию модуля неинтерактивной после того, как она стала интерактивной с помощью 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, устанавливает финализатор функции fin для функции модуля, на которую ссылается arg. arg должен ссылаться на функцию модуля, то есть на объект, возвращённый make_function. Значением fin может быть NULL, чтобы удалить финализатор функции для arg, либо указатель на функцию, которая будет вызвана при сборке мусора для объекта, на который ссылается arg. Для каждой функции можно задать не более одного финализатора; если у arg уже есть финализатор, он заменяется значением fin.

Copyright © 1990-1996, 1998-2026 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Module-Functions.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API