EDIT : Une meilleure façon de le faire dans les Emacs récents est de définir une macro de compilation pour vérifier le nombre d'arguments. Ma réponse d'origine en utilisant une macro normale est conservée ci-dessous, mais une macro de compilation est supérieure car elle n'empêche pas de passer la fonction à funcall
ou apply
au moment de l'exécution.
Dans les versions récentes d'Emacs, vous pouvez le faire en définissant une macro de compilation pour votre fonction qui vérifie le nombre d'arguments et génère un avertissement (ou même une erreur) si elle ne correspond pas. La seule subtilité est que la macro du compilateur doit retourner le formulaire d'appel de fonction d'origine inchangé pour l'évaluation ou la compilation. Cela se fait en utilisant un &whole
argument et en renvoyant sa valeur. Cela pourrait être accompli comme ceci:
(require 'cl-lib)
(defun my-caller (&rest args)
(while args
(message "%S %S" (pop args) (pop args))))
(define-compiler-macro my-caller (&whole form &rest args)
(when (not (cl-evenp (length args)))
(byte-compile-warn "`my-caller' requires an even number of arguments"))
form)
(my-caller 1 2 3 4)
(my-caller 1 2)
(funcall #'my-caller 1 2 3 4) ; ok
(apply #'my-caller '(1 2)) ; also ok
(my-caller 1) ; produces a warning
(funcall #'my-caller 1 2 3) ; no warning!
(apply #'my-caller '(1 2 3)) ; also no warning
Notez que funcall
et apply
peuvent maintenant être utilisés, mais ils contournent la vérification des arguments par la macro du compilateur. Malgré leur nom, les macros compilateur semblent également être élargi au cours de « interprété » par l' évaluation C-xC-e, M-xeval-bufferet vous obtiendrez des erreurs sur l' évaluation, ainsi que sur la compilation de cet exemple.
La réponse originale suit:
Voici comment vous pouvez implémenter la suggestion de Jordon "d'utiliser une macro qui fournira des avertissements au moment de l'expansion". Cela s'avère très simple:
(require 'cl-lib)
(defmacro my-caller (&rest args)
(if (cl-evenp (length args))
`(my-caller--function ,@args)
(error "Function `my-caller' requires an even number of arguments")))
(defun my-caller--function (&rest args)
;; function body goes here
args)
(my-caller 1 2 3 4)
(my-caller 1 2 3)
Essayer de compiler ce qui précède dans un fichier échouera (aucun .elc
fichier n'est produit), avec un joli message d'erreur cliquable dans le journal de compilation, indiquant:
test.el:14:1:Error: `my-caller' requires an even number of arguments
Vous pouvez également remplacer (error …)
par (byte-compile-warn …)
pour produire un avertissement au lieu d'une erreur, permettant à la compilation de continuer. (Merci à Jordon de l'avoir signalé dans les commentaires).
Étant donné que les macros sont développées au moment de la compilation, aucune pénalité d'exécution n'est associée à cette vérification. Bien sûr, vous ne pouvez pas empêcher d'autres personnes d'appeler my-caller--function
directement, mais vous pouvez au moins l'annoncer comme une fonction "privée" en utilisant la convention à double trait d'union.
Un inconvénient notable de l'utilisation d'une macro à cette fin est qu'elle my-caller
n'est plus une fonction de première classe: vous ne pouvez pas la transmettre à funcall
ou apply
au moment de l'exécution (ou du moins, elle ne fera pas ce que vous attendez). À cet égard, cette solution n'est pas aussi bonne que de pouvoir simplement déclarer un avertissement du compilateur pour une fonction réelle. Bien sûr, l'utilisation apply
rendrait impossible de vérifier le nombre d'arguments transmis à la fonction au moment de la compilation, donc c'est peut-être un compromis acceptable.