Что означает #+ в коде cl-mysql?

Недавно я пытался прочитать код о 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. Но до сих пор не понимаю, зачем писать что-то подобное. Кто-нибудь может помочь мне прояснить ситуацию, спасибо.


person c0rehe110    schedule 03.05.2016    source источник
comment
Я закрыл это как дубликат, так как на него ответили в других вопросах, но я проголосовал за него, потому что это хороший пример того, как используется эта конструкция, и, как вы заметили, это довольно сложно искать для этого, пока вы не узнаете, как он называется (после этого вам обычно больше не нужно его искать).   -  person Joshua Taylor    schedule 03.05.2016


Ответы (2)


#+ — это макрос чтения, который проверяет, находится ли ключевое слово в специальной переменной *FEATURES*. Если его нет, следующая форма будет пропущена (читателем; компилятор ее никогда не увидит). Существует также #-, который делает обратное.

Есть некоторые вещи, которые не являются частью стандарта 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!")

напечатает Foo! либо на SBCL, либо на ECL.

#+(and sbcl quicklisp) (format t "Bar!")

будет печатать Bar! только на SBCL, для которого доступен Quicklisp.

person jkiiski    schedule 03.05.2016
comment
Спасибо за ваши комментарии, действительно спасибо - person c0rehe110; 03.05.2016

Можно представить, что мы можем написать:

(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 необходимо защитить от чтения. В этом случае символ sb-thread:make-mutex будет прочитан только в том случае, если функция sb-thread присутствует в списке cl:*features*. Что, вероятно, только для SBCL или Lisp, который утверждает, что sb-threads доступен.

Выражения функций здесь предотвращают попытки Лиспа читать символы с неизвестными пакетами - пакеты неизвестны, потому что соответствующее программное обеспечение не загружено или недоступно.

person Rainer Joswig    schedule 03.05.2016