За очень немногими исключениями, большинству модулей необходимо обмениваться данными с программами на Lisp, которые их вызывают: принимать аргументы для функций модуля и возвращать значения из функций модуля. Для этой цели API модуля предоставляет тип emacs_value, который представляет объекты Emacs Lisp, передаваемые через API; это функциональный аналог типа Lisp_Object, используемого в примитивах Emacs на C (см. Написание примитивов Emacs). В этом разделе описываются части API модуля, которые позволяют создавать объекты emacs_value, соответствующие основным типам данных Lisp, и как получать доступ из C к данным в объектах emacs_value, соответствующих объектам Lisp.
Все функции, описанные ниже, на самом деле являются указателями на функции, предоставляемыми через указатель на окружение, который принимает каждая функция модуля. Поэтому код модуля должен вызывать эти функции через указатель на окружение, например так:
emacs_env *env; /* указатель на окружение */ env->some_function (аргументы…);
Указатель emacs_env обычно будет получен из первого аргумента функции модуля или из вызова get_environment, если вам нужно окружение в функции инициализации модуля.
Большинство описанных ниже функций стали доступны в Emacs 25 — первом выпуске Emacs, поддерживающем динамические модули. Для немногих функций, ставших доступными в более поздних выпусках Emacs, мы упоминаем первую версию Emacs, в которой они появились.
Следующие функции API извлекают значения различных типов данных C из объектов emacs_value. Все они вызывают условие ошибки wrong-type-argument (см. Предикаты типов), если объект emacs_value аргумента не принадлежит типу, ожидаемому функцией. См. Нелокальные выходы в модулях для подробностей о том, как работает сигнализация ошибок в модулях Emacs и как перехватывать условия ошибок внутри модуля до того, как они будут сообщены в Emacs. Функция API type_of (см. type_of) может использоваться для получения типа объекта emacs_value.
intmax_t extract_integer (emacs_env *env, emacs_value arg) ¶Эта функция возвращает значение целого числа Lisp, заданного arg. Тип данных C возвращаемого значения — intmax_t — это самый широкий целочисленный тип данных, поддерживаемый компилятором C, обычно long long. Если значение arg не помещается в intmax_t, функция сигнализирует об ошибке, используя символ ошибки overflow-error.
bool extract_big_integer (emacs_env *env, emacs_value arg, int *sign, ptrdiff_t *count, emacs_limb_t *magnitude) ¶Эта функция, доступная начиная с Emacs 27, извлекает целочисленное значение arg. Значение arg должно быть целым числом (fixnum или bignum). Если sign не равен NULL, он сохраняет знак arg (-1, 0 или +1) в *sign. Модуль числа сохраняется в magnitude следующим образом. Если и count, и magnitude не равны NULL, то magnitude должен указывать на массив, содержащий как минимум *count элементов типа unsigned long. Если magnitude достаточно велик, чтобы вместить модуль arg, то эта функция записывает модуль в массив magnitude в формате с прямым порядком байтов (little-endian), сохраняет количество записанных элементов массива в *count и возвращает true. Если magnitude недостаточно велик, она сохраняет требуемый размер массива в *count, сигнализирует об ошибке и возвращает false. Если count не равен NULL, а magnitude равен NULL, то функция сохраняет требуемый размер массива в *count и возвращает true.
Emacs гарантирует, что максимальное требуемое значение *count никогда не превышает min (PTRDIFF_MAX, SIZE_MAX) / sizeof (emacs_limb_t), поэтому вы можете использовать malloc (*count * sizeof *magnitude) для выделения массива magnitude, не беспокоясь о целочисленном переполнении при вычислении размера.
Это беззнаковый целочисленный тип, используемый как тип элемента для массивов модулей в функциях преобразования больших целых чисел. Гарантируется, что этот тип имеет уникальные объектные представления, т. е. не содержит битов заполнения.
Этот макрос раскрывается в константное выражение, задающее максимально возможное значение для объекта emacs_limb_t. Выражение пригодно для использования в #if.
double extract_float (emacs_env *env, emacs_value arg) ¶Эта функция возвращает значение числа с плавающей точкой Lisp, заданного arg, как значение C типа double.
struct timespec extract_time (emacs_env *env, emacs_value arg) ¶Эта функция, доступная начиная с Emacs 27, интерпретирует arg как значение времени Emacs Lisp и возвращает соответствующую структуру struct timespec. См. Время суток. struct timespec представляет временную метку с наносекундной точностью. Она имеет следующие члены:
time_t tv_secЦелое число секунд.
long tv_nsecДоля секунды в наносекундах.
Для временных меток, возвращаемых extract_time,
это значение всегда неотрицательно и меньше одного миллиарда.
(Хотя POSIX требует, чтобы тип tv_nsec был long,
на некоторых нестандартных платформах типом является long long.)
Если time имеет более высокую точность, чем наносекунды, то эта функция усекает его до наносекундной точности в сторону отрицательной бесконечности. Эта функция сигнализирует об ошибке, если time (усечённое до наносекунд) не может быть представлено struct timespec. Например, если time_t является 32-битным целым типом, то значение time, равное десяти миллиардам секунд, вызовет ошибку, а значение time, равное 600 пикосекундам, будет усечено до нуля.
Если вам нужно работать со значениями времени, которые не представимы через struct timespec, или если вам нужна более высокая точность, вызовите функцию Lisp encode-time и работайте с её возвращаемым значением. См. Преобразование времени.
bool copy_string_contents (emacs_env *env, emacs_value arg, char *buf, ptrdiff_t *len) ¶Эта функция сохраняет текст строки Lisp в кодировке UTF-8, заданной arg, в массив char, на который указывает buf, который должен иметь достаточно места для хранения как минимум *len байт, включая завершающий нулевой байт. Аргумент len не должен быть нулевым указателем, и при вызове функции он должен указывать на значение, задающее размер buf в байтах.
Если размер буфера, заданный *len, достаточно велик для хранения текста строки, функция сохраняет в *len фактическое количество скопированных в buf байт, включая завершающий нулевой байт, и возвращает true. Если буфер слишком мал, функция вызывает условие ошибки args-out-of-range, сохраняет требуемое количество байт в *len и возвращает false. См. Нелокальные выходы в модулях о том, как обрабатывать ожидающие условия ошибок.
Аргумент buf может быть нулевым указателем, и в этом случае функция сохраняет в *len количество байт, необходимое для хранения содержимого arg, и возвращает true. Так можно определить размер buf, необходимый для хранения конкретной строки: сначала вызовите copy_string_contents с NULL в качестве buf, затем выделите достаточно памяти для хранения количества байт, сохранённого функцией в *len, и вызовите функцию снова с ненулевым buf, чтобы фактически выполнить копирование текста.
emacs_value vec_get (emacs_env *env, emacs_value vector, ptrdiff_t index) ¶Эта функция возвращает элемент vector по индексу index. Индекс первого элемента вектора равен нулю. Функция вызывает условие ошибки args-out-of-range, если значение index недопустимо. Чтобы извлечь данные C из значения, возвращаемого функцией, используйте другие функции извлечения, описанные здесь, в зависимости от типа данных Lisp, хранящегося в этом элементе вектора.
ptrdiff_t vec_size (emacs_env *env, emacs_value vector) ¶Эта функция возвращает количество элементов в vector.
void vec_set (emacs_env *env, emacs_value vector, ptrdiff_t index, emacs_value value) ¶Эта функция сохраняет value в элементе vector с индексом index. Она вызывает условие ошибки args-out-of-range, если значение index недопустимо.
Следующие функции API создают объекты emacs_value из основных типов данных C. Все они возвращают созданный объект emacs_value.
emacs_value make_integer (emacs_env *env, intmax_t n) ¶Эта функция принимает целочисленный аргумент n и возвращает соответствующий объект emacs_value. Она возвращает либо fixnum, либо bignum в зависимости от того, находится ли значение n в пределах, установленных most-negative-fixnum и most-positive-fixnum (см. Основы целых чисел).
emacs_value make_big_integer (emacs_env *env, int sign, ptrdiff_t count, const emacs_limb_t *magnitude) ¶Эта функция, доступная начиная с Emacs 27, принимает целочисленный аргумент произвольного размера и возвращает соответствующий объект emacs_value. Аргумент sign задаёт знак возвращаемого значения. Если sign не равен нулю, то magnitude должен указывать на массив из как минимум count элементов, задающих модуль возвращаемого значения в формате с прямым порядком байтов (little-endian).
Следующий пример использует библиотеку GNU Multiprecision Library (GMP) для вычисления следующего вероятного простого числа после заданного целого числа. См. (gmp)Введение для общего обзора GMP и см. (gmp)Импорт и экспорт целых чисел о том, как преобразовывать массив magnitude в значения GMP mpz_t и обратно.
#include <emacs-module.h>
int plugin_is_GPL_compatible;
#include <assert.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <gmp.h>
static void
memory_full (emacs_env *env)
{
static const char message[] = "Память исчерпана";
emacs_value data = env->make_string (env, message,
strlen (message));
env->non_local_exit_signal
(env, env->intern (env, "error"),
env->funcall (env, env->intern (env, "list"), 1, &data));
}
enum
{
order = -1, endian = 0, nails = 0,
limb_size = sizeof (emacs_limb_t),
max_nlimbs = ((SIZE_MAX < PTRDIFF_MAX ? SIZE_MAX : PTRDIFF_MAX)
/ limb_size)
};
static bool
extract_big_integer (emacs_env *env, emacs_value arg, mpz_t result)
{
ptrdiff_t nlimbs;
bool ok = env->extract_big_integer (env, arg, NULL, &nlimbs, NULL);
if (!ok)
return false;
assert (0 < nlimbs && nlimbs <= max_nlimbs);
emacs_limb_t *magnitude = malloc (nlimbs * limb_size);
if (magnitude == NULL)
{
memory_full (env);
return false;
}
int sign;
ok = env->extract_big_integer (env, arg, &sign, &nlimbs, magnitude);
assert (ok);
mpz_import (result, nlimbs, order, limb_size, endian, nails, magnitude);
free (magnitude);
if (sign < 0)
mpz_neg (result, result);
return true;
}
static emacs_value
make_big_integer (emacs_env *env, const mpz_t value)
{
size_t nbits = mpz_sizeinbase (value, 2);
int bitsperlimb = CHAR_BIT * limb_size - nails;
size_t nlimbs = nbits / bitsperlimb + (nbits % bitsperlimb != 0);
emacs_limb_t *magnitude
= nlimbs <= max_nlimbs ? malloc (nlimbs * limb_size) : NULL;
if (magnitude == NULL)
{
memory_full (env);
return NULL;
}
size_t written;
mpz_export (magnitude, &written, order, limb_size, endian, nails, value);
assert (written == nlimbs);
assert (nlimbs <= PTRDIFF_MAX);
emacs_value result = env->make_big_integer (env, mpz_sgn (value),
nlimbs, magnitude);
free (magnitude);
return result;
}
static emacs_value
next_prime (emacs_env *env, ptrdiff_t nargs, emacs_value *args,
void *data)
{
assert (nargs == 1);
mpz_t p;
mpz_init (p);
extract_big_integer (env, args[0], p);
mpz_nextprime (p, p);
emacs_value result = make_big_integer (env, p);
mpz_clear (p);
return result;
}
int
emacs_module_init (struct emacs_runtime *runtime)
{
emacs_env *env = runtime->get_environment (runtime);
emacs_value symbol = env->intern (env, "next-prime");
emacs_value func
= env->make_function (env, 1, 1, next_prime, NULL, NULL);
emacs_value args[] = {symbol, func};
env->funcall (env, env->intern (env, "defalias"), 2, args);
return 0;
}
emacs_value make_float (emacs_env *env, double d) ¶Эта функция принимает аргумент double d и возвращает соответствующее значение с плавающей точкой Emacs.
emacs_value make_time (emacs_env *env, struct timespec time) ¶Эта функция, доступная начиная с Emacs 27, принимает аргумент struct timespec time и возвращает соответствующую временную метку Emacs в виде пары (ticks . hz). См. Время суток. Возвращаемое значение представляет ту же временную метку, что и time: все входные значения представимы, и потеря точности никогда не происходит. time.tv_sec и time.tv_nsec могут быть произвольными значениями. В частности, не требуется, чтобы time была нормализована. Это означает, что time.tv_nsec может быть отрицательным или больше 999 999 999.
emacs_value make_string (emacs_env *env, const char *str, ptrdiff_t len) ¶Эта функция создаёт строку Emacs из строки текста C, на которую указывает str, длина которой в байтах (не включая завершающий нулевой байт) равна len. Исходная строка в str может быть либо строкой ASCII, либо строкой в кодировке UTF-8, не являющейся ASCII; она может содержать встроенные нулевые байты и не обязана заканчиваться завершающим нулевым байтом в str[len]. Функция вызывает условие ошибки overflow-error, если len отрицателен или превышает максимальную длину строки Emacs. Если len равен нулю, то str может быть NULL; в противном случае он должен указывать на допустимую память. Для ненулевого len make_string возвращает уникальные изменяемые строковые объекты.
emacs_value make_unibyte_string (emacs_env *env, const char *str, ptrdiff_t len) ¶Эта функция подобна make_string, но не налагает ограничений на значения байтов в строке C и может использоваться для передачи двоичных данных в Emacs в виде однобайтовой (unibyte) строки.
API не предоставляет функций для управления структурами данных Lisp, например, для создания списков с помощью cons и list (см. Построение ячеек cons и списков), извлечения элементов списка с помощью car и cdr (см. Доступ к элементам списков), создания векторов с помощью vector (см. Функции для векторов) и т. д. Для этого используйте intern и funcall, описанные в следующем подразделе, для вызова соответствующих функций Lisp.
Обычно объекты emacs_value имеют довольно короткое время жизни: оно заканчивается, когда указатель emacs_env, использованный для их создания, выходит из области видимости. Иногда вам может понадобиться создать глобальные ссылки: объекты emacs_value, которые живут столько, сколько вы пожелаете. Используйте следующие две функции для управления такими объектами.
emacs_value make_global_ref (emacs_env *env, emacs_value value) ¶Эта функция возвращает глобальную ссылку на value.
void free_global_ref (emacs_env *env, emacs_value global_value) ¶Эта функция освобождает global_value, ранее созданную с помощью make_global_ref. global_value больше не действительна после вызова. Код вашего модуля должен сопоставлять каждый вызов make_global_ref с соответствующим free_global_ref.
Альтернативой хранению структур данных C, которые необходимо передавать функциям модуля позже, является создание объектов указателя пользователя. Объект указателя пользователя, или user-ptr, — это объект Lisp, который инкапсулирует указатель C и может иметь связанную функцию финализатора, которая вызывается при сборке мусора этого объекта (см. Сборка мусора). API модуля предоставляет функции для создания объектов user-ptr и доступа к ним. Эти функции вызывают условие ошибки wrong-type-argument, если они вызываются для emacs_value, который не представляет объект user-ptr.
emacs_value make_user_ptr (emacs_env *env, emacs_finalizer fin, void *ptr) ¶Эта функция создаёт и возвращает объект user-ptr, который оборачивает указатель C ptr. Функция финализатора fin может быть нулевым указателем (означает отсутствие финализатора) или функцией со следующей сигнатурой:
typedef void (*emacs_finalizer) (void *ptr);
Если fin не является нулевым указателем, он будет вызван с ptr в качестве аргумента, когда объект user-ptr будет собран сборщиком мусора. Не выполняйте дорогостоящий код в финализаторе, так как сборка мусора должна завершаться быстро, чтобы Emacs оставался отзывчивым.
void * get_user_ptr (emacs_env *env, emacs_value arg) ¶Эта функция извлекает указатель C из объекта Lisp, представленного arg.
void set_user_ptr (emacs_env *env, emacs_value arg, void *ptr) ¶Эта функция устанавливает указатель C, встроенный в объект user-ptr, представленный arg, в ptr.
emacs_finalizer get_user_finalizer (emacs_env *env, emacs_value arg) ¶Эта функция возвращает финализатор объекта user-ptr, представленного arg, или NULL, если он не имеет финализатора.
void set_user_finalizer (emacs_env *env, emacs_value arg, emacs_finalizer fin) ¶Эта функция изменяет финализатор объекта user-ptr, представленного arg, на fin. Если fin является нулевым указателем, объект user-ptr не будет иметь финализатора.
Обратите внимание, что тип emacs_finalizer работает как для финализаторов указателей пользователя, так и для финализаторов функций модуля. См. Финализаторы функций модуля.