我曾尝试在仅使用IPv6的网络中使用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
时未指定地址系列或指定:internet
。
是否有办法在运行时修补ccl:make-socket
以覆盖此设置?
答案 0 :(得分:5)
建议功能
Common Lisp的几个实现允许建议正常功能的( - > 修补)。建议是一种非标准功能,不同的实现方式略有不同。 CLOS泛型函数的相关机制是标准化的:before,:after和:around around。
目的是在定义函数之后向函数添加一个或多个补丁,而不更改原始源代码。
通常,这要求对此函数的函数调用不是内联。
Clozure Common Lisp中的宏ADVISE
Clozure CL中的修补功能可以通过宏ADVISE
完成。请参阅advising的文档。
假设我们有一个函数FOOBAR
:
? (defun foobar (a b &key c (d :foobar)) (list a b c d))
FOOBAR
FOOBAR
在TEST
内被调用:
? (defun test (a) (foobar a 20 :c 30))
TEST
? (test 10)
(10 20 30 :FOOBAR)
我们现在要修补FOOBAR
,以便使用不同的值调用命名的arg :D
。
我们更改arglist以在两个必需的args之后插入新的命名参数:
? (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)
答案 1 :(得分:2)
这可以做到!
首先制作原始make-socket的副本
tabindex
然后重新定义make-socket。注意:您必须提供所有关键字参数的完整规范。事实上,我只使用你问题中的那些来进行演示。
<div class = "some-random-class">
<script> *script stuff* </script>
<div class = "some-other-class">
<p> </p>
</div>
</div>
这将发出 可持续错误信号。
在repl处键入(IN-PACKAGE :ccl)
(DEFPARAMETER original-make-socket #'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))
另一种方法是在重新定义make-socket之前覆盖全局变量:go
。
(IN-PACKAGE :cl-user)
(ccl:make-socket :remote-host "ya.ru" :remote-port 443 :address-family :IRRELEVANT)
这将避免可持续的错误信号,并直接修补内核函数。