最近我试图阅读关于cl-mysql的代码,但被#+
卡住了。
试着用谷歌搜索它,但不起作用,所以转到这里
(defun make-lock (name)
#+sb-thread (sb-thread:make-mutex :name name)
#+ecl (mp:make-lock :name name)
#+armedbear (ext:make-thread-lock)
#+ (and clisp mt) (mt:make-mutex :name name)
#+allegro (mp:make-process-lock :name name))
看起来它适用于不同的后端lisp编译器。但仍然不知道为什么要写这样的东西。任何人都可以帮我说清楚。
#+
是一个读取器宏,用于检查关键字是否在特殊变量*FEATURES*
中。如果它不在那里,下面的表单将被跳过(读者;编译器永远看不到它)。还有CCD_ 4起相反的作用。
有些东西不是Common Lisp标准的一部分,但它们足够重要,以至于所有(或大多数)实现都为它们提供了非标准的扩展。当您想在需要处理多个实现的代码中使用它们时,必须使用读取时间条件为当前实现提供正确的代码。互斥(以及一般的线程)就是其中之一。
当然,第三方图书馆也可能提供一些功能。*FEATURES*
的内容看起来像这样:
(:SWANK :QUICKLISP :SB-BSD-SOCKETS-ADDRINFO :ASDF-PACKAGE-SYSTEM :ASDF3.1
:ASDF3 :ASDF2 :ASDF :OS-UNIX :NON-BASE-CHARS-EXIST-P :ASDF-UNICODE :64-BIT
:64-BIT-REGISTERS :ALIEN-CALLBACKS :ANSI-CL :ASH-RIGHT-VOPS
:C-STACK-IS-CONTROL-STACK :COMMON-LISP :COMPARE-AND-SWAP-VOPS
:COMPLEX-FLOAT-VOPS :CYCLE-COUNTER :ELF :FLOAT-EQL-VOPS
:FP-AND-PC-STANDARD-SAVE :GENCGC :IEEE-FLOATING-POINT :INLINE-CONSTANTS
:INTEGER-EQL-VOP :INTERLEAVED-RAW-SLOTS :LARGEFILE :LINKAGE-TABLE :LINUX
:LITTLE-ENDIAN :MEMORY-BARRIER-VOPS :MULTIPLY-HIGH-VOPS :OS-PROVIDES-DLADDR
:OS-PROVIDES-DLOPEN :OS-PROVIDES-GETPROTOBY-R :OS-PROVIDES-POLL
:OS-PROVIDES-PUTWC :OS-PROVIDES-SUSECONDS-T :PACKAGE-LOCAL-NICKNAMES
:PRECISE-ARG-COUNT-ERROR :RAW-INSTANCE-INIT-VOPS :SB-DOC :SB-EVAL :SB-FUTEX
:SB-LDB :SB-PACKAGE-LOCKS :SB-SIMD-PACK :SB-SOURCE-LOCATIONS :SB-TEST
:SB-THREAD :SB-UNICODE :SBCL :STACK-ALLOCATABLE-CLOSURES
:STACK-ALLOCATABLE-FIXED-OBJECTS :STACK-ALLOCATABLE-LISTS
:STACK-ALLOCATABLE-VECTORS :STACK-GROWS-DOWNWARD-NOT-UPWARD :SYMBOL-INFO-VOPS
:UNIX :UNWIND-TO-FRAME-AND-CALL-VOP :X86-64)
因此,如果您想编写依赖于Quicklisp的代码,例如,您可以使用#+quicklisp
。如果您想要仅在Quicklisp不可用时运行的代码,则可以使用#-quicklisp
。
也可以使用特征的布尔表达式。例如,
#+(or sbcl ecl) (format t "Foo!")
将在SBCL或ECL上打印CCD_ 8。
#+(and sbcl quicklisp) (format t "Bar!")
将仅在具有Quicklisp可用的SBCL上打印CCD_ 9。
可以想象我们可以写:
(defun make-lock (name)
(cond ((member :sb-thread *features)
(sb-thread:make-mutex :name name))
((member :ecl *features*)
(mp:make-lock :name name))
...))
但这通常不起作用,因为当符号包不存在时,我们无法读取符号,并且有些包是特定于实现/库/应用程序的。包不是在读取时以惰性/自动方式创建的。
在Common Lisp中,读取不存在的包的符号会导致错误:
CL-USER 1 > (read-from-string "foo:bar")
Error: Reader cannot find package FOO.
1 (continue) Create the FOO package.
2 Use another package instead of FOO.
3 Try finding package FOO again.
4 (abort) Return to level 0.
5 Return to top loop level 0.
在您的示例中,sb-thread:make-mutex
是一个在SBCL中有意义的符号,但在Allegro CL中没有。此外,包SB-THREAD
不存在于Allegro CL中。因此,需要保护Allegro CL不读取它。在这种情况下,只有在cl:*features*
列表中存在功能sb-thread
的情况下,才会读取符号sb-thread:make-mutex
。这可能只适用于SBCL,或者声称有sb-threads
可用的Lisp。
这里的功能表达式防止Lisp尝试读取带有未知包的符号——这些包是未知的,因为相应的软件没有加载或不可用。