defmacro生成另一个defmacro

时间:2012-09-13 02:34:25

标签: clojure macros

我遇到了这个问题......我有两个非常相似的宏

(import java.lang.management.ManagementFactory)

(defmacro with-thread-manager [tm & body]
  {:pre [(and (vector? tm) (= 1 (count tm)))]}
  `(let [~(first tm) (ManagementFactory/getThreadMXBean)]
     ~@body))

(defmacro with-os-manager [tm & body]
  {:pre [(and (vector? tm) (= 1 (count tm)))]}
  `(let [~(first tm) (ManagementFactory/getOperatingSystemMXBean)]
     ~@body))

它们的用法如下:

(defn thread-count []
  (with-thread-manager [tm] (.getThreadCount tm)))

(thread-count)
;; => 12

(defn application-cpu-time []
  (with-os-manager [osm] (.getProcessCpuTime osm)))

(application-cpu-time)
;; => 71260000000

我希望将两个with-*-manager概括为另一个宏,以便我可以像这样简化它们:

(defmanagementblock with-thread-manager (ManagementFactory/getThreadMXBean))
(defmanagementblock with-os-manager (ManagementFactory/getOperatingSystemMXBean))

所以我知道最简单的方法是稍微改变一下宏

(defmacro with-thread-manager [tm & body]
  {:pre [(and (vector? tm) (= 1 (count tm)))]}
  (apply list 'let [(first tm) '(ManagementFactory/getThreadMXBean)]
      body))

并写下块:

(defmacro defmanageblock [name MANAGER]
  (list 'defmacro name '[tm & body]
    '{:pre [(and (vector? tm) (= 1 (count tm)))]}
    (list 'apply 'list ''let (vector '(first tm) 'MANAGER)
          'body)))
除了MANAGER没有正确引用外,一切顺利。我尝试了一堆引用和取消引用选项,如',`,~'以及它的许多其他变体。但它没有给出正确的价值。

2 个答案:

答案 0 :(得分:5)

如果你将defmanageblock定义为一个函数,它接受一个描述工厂使用的符号,它将s-expression作为一个列表返回,那么你将有两个调用函数而不是嵌套宏的宏。在大多数情况下,在函数而不是宏中进行真正的代码生成工作会使代码更易于推理和测试

答案 1 :(得分:5)

一个合理的解决方案,如果你不介意跟踪一些范围:

(defmacro defmanagedblock [name mgr]
  `(defmacro ~name [tm# & body#]
     {:pre [(and (vector? tm#)) (= 1 (count tm#))]}
     `(let [~(first tm#) ~'~mgr]
        ~@body#)))