如何仅在IPv6网络上使用Clozure CL



我试图在仅在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以覆盖此设置?

建议函数

普通LISP的几种实现允许咨询( -> patching )正常功能。建议是一个非标准功能,不同的实现以略有不同的方式提供。相关机制是针对关闭通用函数的标准化的:以下方法:

目的是在定义函数之后添加一个或多个补丁程序,而无需更改原始源代码。

通常,这需要函数调用此函数 iNalined

clozure Common Lisp

中的宏建议

可以使用宏ADVISE中的clozure cl中的修补功能。请参阅文档有关建议。

假设我们有一个函数FOOBAR

? (defun foobar (a b &key c (d :foobar)) (list a b c d))
FOOBAR

FOOBARTEST中被调用:

? (defun test (a) (foobar a 20 :c 30))
TEST
? (test 10)
(10 20 30 :FOOBAR)

我们现在想修补FOOBAR,以便以不同的值调用name 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)

这可以完成!

首先制作原始Make-Socket

的副本
(IN-PACKAGE :ccl)
(DEFPARAMETER original-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))

这将信号a 连续错误

在替补处键入:go以继续。这将成功修补套筒。

现在,任何拨号的调用都将是新的定义。尝试:

(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)

这将避免可连续的错误信号,并直接贴上内核函数。

最新更新