J'écris une fonction qui, en principe, prend un nombre arbitraire d'arguments. Dans la pratique, cependant, il ne devrait jamais être passé qu'un nombre pair d'arguments, et produira des résultats indésirables autrement.
Voici un exemple factice pour le contexte:
(defun my-caller (&rest args)
(while args
(call-other-function (pop args) (pop args))))
Lorsqu'un fichier elisp est compilé en octets, le compilateur d'octets envoie un avertissement lorsqu'il voit une fonction appelée avec le mauvais nombre d'arguments. Évidemment, cela n'arrivera jamais my-caller
, car il est défini pour prendre n'importe quel nombre.
Pourtant, il y a peut-être une propriété de symbole que je peux définir, ou une (declare)
forme que je peux ajouter à sa définition. Quelque chose pour informer l'utilisateur que cette fonction ne doit recevoir qu'un nombre pair d'arguments.
- Existe-t-il un moyen d'informer le compilateur d'octets de cette restriction?
- Sinon, est-ce possible avec une macro, au lieu d'une fonction?
la source
Réponses:
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
ouapply
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:Notez que
funcall
etapply
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:
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: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
ouapply
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'utilisationapply
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.la source
byte-compile-warn
apply
oufuncall
le wrapper de macro. Je vais l'essayer et modifier ma réponse si cela fonctionne.Oui, vous pouvez utiliser
byte-defop-compiler
pour spécifier réellement une fonction qui compile votre fonction,byte-defop-compiler
possède des subtilités intégrées pour vous aider à spécifier que vos fonctions doivent générer des avertissements basés sur un certain nombre d'arguments.Documentation
Ajoutez un formulaire de compilation pour FUNCTION.Si la fonction est un symbole, la variable "byte-SYMBOL" doit nommer l'opcode à utiliser. Si function est une liste, le premier élément est la fonction et le deuxième élément est le symbole du bytecode. Le deuxième élément peut être nul, ce qui signifie qu'il n'y a pas d'opcode. COMPILE-HANDLER est la fonction à utiliser pour compiler cet octet-op, ou peut être les abréviations 0, 1, 2, 3, 0-1 ou 1-2. S'il est nul, le gestionnaire est "byte-compile-SYMBOL.
Usage
Dans votre cas spécifique, vous pouvez utiliser l'une des abréviations pour définir que votre fonction doit recevoir deux arguments.
Maintenant, votre fonction donnera des avertissements lorsqu'elle sera compilée avec autre chose que 2 arguments.
Si vous souhaitez donner des avertissements plus spécifiques et écrire vos propres fonctions de compilation. Regardez
byte-compile-one-arg
et d'autres fonctions similaires dans bytecomp.el pour référence.Notez que vous ne spécifiez pas seulement une fonction pour gérer la validation, mais aussi la compilation. Encore une fois, les fonctions de compilation dans bytecomp.el vous fourniront une bonne référence.
Des itinéraires plus sûrs
Ce n'est pas quelque chose que j'ai vu documenté ou discuté en ligne, mais dans l'ensemble, je dirais que c'est une voie peu judicieuse à prendre. La route correcte (IMO) serait d'écrire vos défuns avec des signatures descriptives ou d'utiliser une macro qui fournira des avertissements au moment de l'expansion, en vérifiant la longueur de vos arguments et en utilisant
byte-compile-warn
ouerror
pour afficher des erreurs. Il peut également être utileeval-when-compile
pour vous de faire une vérification des erreurs.Vous devrez également définir votre fonction avant de l'utiliser, et l'appel à
byte-defop-compiler
devra l'être avant que le compilateur ne passe aux appels réels de votre fonction.Encore une fois, il ne semble pas vraiment être documenté ou conseillé à partir de ce que j'ai vu (pourrait être faux) mais j'imagine que le modèle à suivre ici serait de spécifier une sorte de fichier d'en-tête pour votre paquet qui est plein d'un tas de defuns vides et appelle à
byte-defop-compiler
. Ce serait essentiellement un package qui est requis avant que votre vrai package puisse être compilé.Opinion: Sur la base de ce que je sais, ce qui n'est pas grand-chose, parce que je viens d'apprendre tout cela, je vous conseillerais de ne jamais rien faire de tout cela. déjà
la source