Common Lisp:如何在宏中使用宏?

问题描述 投票:0回答:1

我正在使用 cl-sdl2 编写一个应用程序(一个简单的游戏)。 cl-sdl2 包含一个名为

WITH-EVENT-LOOP
的宏,可用于通过一些事件处理程序启动 SDL 应用程序。

目前我使用宏如下:

(sdl2:with-event-loop (:method :poll)
  (:idle () (on-idle :renderer renderer
                     :tick-func tick-func
                     :render-func render-func))
  (:quit () t)

  ;; Input handlers below
  (:keydown (:keysym keysym)
            (funcall input-func :keydown :keysym keysym))
  (:keyup (:keysym keysym)
          (funcall input-func :keyup :keysym keysym)))

此代码清单很小,因为我省略了几个输入处理程序。至少还定义了 6 个遵循相同模式的处理程序,用于处理鼠标和游戏控制器输入事件,因此我的实际代码更长且高度重复。

我可以编写一个宏来生成这些样板形式:

(defmacro forward-evt-to (target evt &rest evt-args)
  `(,evt ,evt-args
         (funcall ,target ,evt ,@evt-args)))

这会生成一个可以被

WITH-EVENT-LOOP
理解的列表:

> (macroexpand-1 '(forward-evt-to input-func :keydown :keysym keysym))
(:KEYDOWN (:KEYSYM KEYSYM) (FUNCALL INPUT-FUNC :KEYDOWN :KEYSYM KEYSYM))
T

但是当我尝试使用该宏时,出现编译器错误。例如,这会失败:

(sdl2:with-event-loop (:method :poll)
  (:idle () (on-idle :renderer renderer
                     :tick-func tick-func
                     :render-func render-func))
  (:quit () t)

  (forward-evt-to input-func :keydown :keysym keysym)
  (forward-evt-to input-func :keyup :keysym keysym))

WITH-EVENT-LOOP
尝试编译代码而不扩展它并给出错误

; The value
;      INPUT-FUNC
;    is not of type
;      LIST

有什么方法可以在

FORWARD-EVT-TO
宏内部使用我的
WITH-EVENT-LOOP
宏吗?我不能/不想直接修改
WITH-EVENT-LOOP

macros common-lisp lisp-macros
1个回答
0
投票

一种方法是编写一个新的宏,它将扩展为您的目标形式。

示例草图:

CL-USER 8 > (defmacro with-event-loop-1 (foo &body clauses &environment env)
              `(with-event-loop ,foo
                                ,(loop for clause in clauses
                                       when (keywordp (first clause))
                                         collect clause
                                       else
                                         collect (macroexpand-1 clause env))))
WITH-EVENT-LOOP-1

CL-USER 9 > (pprint
             (macroexpand
              '(with-event-loop-1 (:method :poll)
                 (:idle () (on-idle :renderer renderer
                                    :tick-func tick-func
                                    :render-func render-func))
                 (:quit () t)
                 
                 (forward-evt-to input-func :keydown :keysym keysym)
                 (forward-evt-to input-func :keyup :keysym keysym))))

(WITH-EVENT-LOOP
 (:METHOD :POLL)
 ((:IDLE NIL (ON-IDLE :RENDERER RENDERER
                      :TICK-FUNC TICK-FUNC
                      :RENDER-FUNC RENDER-FUNC))
  (:QUIT NIL T)
  (:KEYDOWN (:KEYSYM KEYSYM) (FUNCALL INPUT-FUNC :KEYDOWN :KEYSYM KEYSYM))
  (:KEYUP (:KEYSYM KEYSYM) (FUNCALL INPUT-FUNC :KEYUP :KEYSYM KEYSYM))))
© www.soinside.com 2019 - 2024. All rights reserved.