当我在IPv6 only网络中工作时,我曾尝试用Clozure CL替换SBCL,但遇到了这样的错误:
MIGRATIONS> (ignore-errors (ccl:make-socket :remote-host "ya.ru" :remote-port 443))
NIL
#<CCL:NO-APPLICABLE-METHOD-EXISTS #x302005215E5D>
MIGRATIONS> (ignore-errors (ccl:make-socket :remote-host "ya.ru" :remote-port 443 :address-family :internet))
NIL
#<CCL:NO-APPLICABLE-METHOD-EXISTS #x3020052549AD>
MIGRATIONS> (ignore-errors (ccl:make-socket :remote-host "ya.ru" :remote-port 443 :address-family :internet6))
#<BASIC-TCP-STREAM ISO-8859-1 (SOCKET/16) #x3020051D4A9D>问题是,许多库在使用CCL:MAKE-TCP-SOCKET时没有指定address-family或指定:internet。
有没有办法在运行时修补ccl:make-socket以覆盖此设置?
发布于 2017-09-09 01:59:22
通知函数
Common Lisp的几个实现允许正常函数的通知(->修补)。建议是一个非标准功能,不同的实现提供它的方式略有不同。CLOS泛型函数的相关机制是标准化的,使用了:before、:after和:after方法。
其目的是在定义函数后向其添加一个或多个补丁,而不更改原始源代码。
通常,这要求对此函数的函数调用不是内联的。
Clozure Common Lisp中的宏建议
可以使用宏ADVISE来完成Clozure CL中的修补函数。请参阅advising的文档。
假设我们有一个函数FOOBAR
? (defun foobar (a b &key c (d :foobar)) (list a b c d))
FOOBAR在TEST内部调用FOOBAR
? (defun test (a) (foobar a 20 :c 30))
TEST
? (test 10)
(10 20 30 :FOOBAR)我们现在希望修补FOOBAR,以便使用不同的值调用命名为arg的:D。
我们更改arglist以在两个必需的参数之后插入新的命名参数:
? (advise foobar (let ((arglist (list* (first arglist)
(second arglist)
:d :ipv6
(cddr arglist))))
(:do-it)) ; calling the original function
:when :around ; advise around it
:name :ipv6) ; the name of this advise
#<Compiled-function (CCL::ADVISED 'FOOBAR) (Non-Global) #x3020010D1CCF>现在我们可以调用我们的TEST函数,它将调用建议的函数FOOBAR。
? (test 10)
(10 20 30 :IPV6)CCL的建议:MAKE-SOCKET
您可以为CCL:MAKE-SOCKET编写类似的建议。
未经测试:
(advise ccl:make-socket (let ((arglist (list* :address-family
:internet6
arglist)))
(:do-it))
:when :around
:name :internet6)发布于 2017-09-08 03:59:11
这是可以做到的!
首先复制原始的make-socket
(IN-PACKAGE :ccl)
(DEFPARAMETER original-make-socket #'make-socket)然后重新定义make-socket。注意:您必须提供所有关键字参数的完整规范。实际上,我只使用了您问题中的那些用于演示。
(defun make-socket (&key (remote-host "defau.lt")
(remote-port 443)
(address-family :internet6))
(declare (ignore address-family))
(format t "Calling new make-socket with address-family as internet6!")
(funcall original-make-socket
:remote-host remote-host
:remote-port remote-port
:address-family :internet6))这将发出continuable error的信号。
在repl处键入:go以继续。这将成功修补make-socket。
现在对make-socket的任何调用都将是对新定义的调用。尝试:
(IN-PACKAGE :cl-user)
(ccl:make-socket :remote-host "ya.ru" :remote-port 443 :address-family :IRRELEVANT)另一种方法是在重新定义make-socket之前覆盖全局变量*warn-if-redefine-kernel*。
(setf *warn-if-redefine-kernel* nil)这将避免连续误差信号,并直接修补内核函数。
https://stackoverflow.com/questions/46042158
复制相似问题