Я пишу функцию, которая, в принципе, принимает произвольное количество аргументов. На практике, однако, ему всегда следует передавать только четное количество аргументов, и в противном случае это приведет к нежелательным результатам.
Вот фиктивный пример для контекста:
(defun my-caller (&rest args)
(while args
(call-other-function (pop args) (pop args))))
Когда файл elisp компилируется байтом, байтовый компилятор выдает предупреждение, когда видит, что функция вызывается с неправильным числом аргументов. Очевидно, что этого никогда не случится my-caller
, так как он определен, чтобы принимать любое число.
Тем не менее, возможно, есть свойство символа, которое я могу установить, или (declare)
форма, которую я могу добавить к его определению. Что-то, чтобы уведомить пользователя, что этой функции нужно давать только четное количество аргументов.
- Есть ли способ сообщить байт-компилятору об этом ограничении?
- Если нет, возможно ли это с помощью макроса вместо функции?
источник
Ответы:
РЕДАКТИРОВАТЬ : Лучший способ сделать это в недавнем Emacs - определить макрос компилятора для проверки количества аргументов. Мой оригинальный ответ с использованием обычного макроса сохранен ниже, но макрос-компилятор лучше, потому что он не препятствует передаче функции во время выполнения
funcall
илиapply
во время выполнения.В последних версиях Emacs вы можете сделать это, определив для своей функции макрос-компилятор, который проверяет количество аргументов и выдает предупреждение (или даже ошибку), если оно не совпадает. Единственная тонкость в том, что макрос компилятора должен возвращать исходную форму вызова функции без изменений для оценки или компиляции. Это делается с помощью
&whole
аргумента и возврата его значения. Это может быть достигнуто следующим образом:Обратите внимание, что
funcall
иapply
теперь можно использовать, но они обходят проверку аргументов макросом компилятора. Несмотря на свое название, компилятор макросы также , кажется , будет расширен в ходе «истолковано» оценки через C-xC-e, M-xeval-bufferтак вы получите ошибки в оценке, а также по составлению этого примера.Исходный ответ следует:
Вот как вы можете реализовать предложение Джордона «использовать макрос, который будет выдавать предупреждения во время расширения». Оказывается, это очень просто:
Попытка скомпилировать вышеупомянутое в файл не удастся (
.elc
файл не создается), с красивым кликабельным сообщением об ошибке в журнале компиляции, в котором говорится:Кроме того, можно заменить
(error …)
с ,(byte-compile-warn …)
чтобы произвести предупреждение вместо ошибки, позволяя продолжить компиляцию. (Спасибо Джордону за то, что указал на это в комментариях).Поскольку макросы раскрываются во время компиляции, с этой проверкой не взимается штраф за время выполнения. Конечно, вы не можете запретить другим людям звонить
my-caller--function
напрямую, но вы можете по крайней мере объявить это как «частную» функцию, используя соглашение о двойных дефисах.Заметный недостаток использования макроса для этой цели является то , что
my-caller
больше не является функция первого класса , вы не можете передать егоfuncall
илиapply
во время выполнения (или , по крайней мере , он не будет делать то , что вы ожидаете). В этом отношении это решение не так хорошо, как возможность просто объявить предупреждение компилятора для реальной функции. Конечно, использованиеapply
сделает невозможным проверку количества аргументов, передаваемых функции во время компиляции, так что, возможно, это приемлемый компромисс.источник
byte-compile-warn
apply
илиfuncall
макропакета. Я попробую это и отредактирую свой ответ, если это работает.Да, вы можете использовать,
byte-defop-compiler
чтобы фактически указать функцию, которая компилирует вашу функцию,byte-defop-compiler
имеет некоторые встроенные тонкости, которые помогут вам указать, что ваши функции должны выдавать предупреждения, основываясь на количестве аргументов.Документация
Добавьте форму компилятора для FUNCTION.Если функция является символом, тогда переменная "byte-SYMBOL" должна назвать используемый код операции. Если функция представляет собой список, первый элемент является функцией, а второй элемент является символом байт-кода. Второй элемент может быть нулем, что означает отсутствие кода операции. COMPILE-HANDLER - это функция, используемая для компиляции этого байта, или это могут быть сокращения 0, 1, 2, 3, 0-1 или 1-2. Если это ноль, то обработчик "byte-compile-SYMBOL.
использование
В вашем конкретном случае вы могли бы использовать одну из аббревиатур, чтобы определить, что вашей функции нужно дать два аргумента.
Теперь ваша функция выдаст предупреждения при компиляции с чем угодно, кроме 2 аргументов.
Если вы хотите дать более конкретные предупреждения и написать свои собственные функции компилятора. Посмотрите
byte-compile-one-arg
и другие подобные функции в bytecomp.el для справки.Обратите внимание, что вы указываете не только некоторую функцию для проверки, но и на самом деле компиляцию. Снова скомпилируйте функции в bytecomp.el, и вы получите хорошую ссылку.
Безопасные маршруты
Это не то, что я видел документально или обсуждали в Интернете, но в целом я бы сказал, что это неправильный путь. Правильный маршрут (IMO) будет состоять в том, чтобы написать ваши определения функций с описательными сигнатурами или использовать макрос, который будет выдавать предупреждения во время расширения, проверять длину ваших аргументов и использовать
byte-compile-warn
илиerror
для отображения ошибок. Вам также может пригодитьсяeval-when-compile
проверка ошибок.Вам также необходимо определить свою функцию до того, как она когда-либо будет использована, и вызов
byte-defop-compiler
должен быть выполнен до того, как компилятор перейдет к фактическим вызовам вашей функции.Опять же, кажется, на самом деле не документировано или не советовано из того, что я видел (может быть неправильно), но я предполагаю, что шаблон, который будет здесь следовать, будет указывать какой-то заголовочный файл для вашего пакета, который полон кучей пустых defuns и звонки
byte-defop-compiler
. В основном это будет пакет, который требуется до того, как ваш реальный пакет может быть скомпилирован.Мнение: Исходя из того, что я знаю, что немного, потому что я только что узнал обо всем этом, я бы посоветовал вам никогда не делать ничего из этого. Когда-либо
источник