在Clojure代码中嵌入任意对象 [英] Embedding arbitrary objects in Clojure code

查看:215
本文介绍了在Clojure代码中嵌入任意对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在Clojure代码中嵌入一个Java对象(在这种情况下为BufferedImage),稍后可以 eval d。



创建代码工作正常:

 (defn f [image] 
`(.getRGB〜 image 0 0))
=> #'user / f

(f some-buffered-image)
=> (.getRGB#< BufferedImage BufferedImage @ 5527f4f9:type = 2 DirectColorModel:rmask = ff0000 gmask = ff00 bmask = ff amask = ff000000 IntegerInterleavedRaster:width = 256 height = 256 #Bands = 4 xOff = 0 yOff = 0 dataOffset [0] 0> 0 0)

但是当尝试 / code> it:

 (eval(f some-buffered-image))
=> CompilerException java.lang.RuntimeException:无法在代码中嵌入对象,也许print-dup未定义:BufferedImage @ 612dcb8c:type = 2 DirectColorModel:rmask = ff0000 gmask = ff00 bmask = ff amask = ff000000 IntegerInterleavedRaster:width = 256 height = 256 #Bands = 4 xOff = 0 yOff = 0 dataOffset [0] 0,编译:(NO_SOURCE_PATH:1)

有任何方法可以让这样的工作吗?



编辑:



我试图这样做的原因是,我想要能够生成从图像采样的代码。该图像被传递给执行代码生成的函数(等价于上面的 f ),但是(由于各种原因)不能作为参数传递到编译代码



我需要生成带引号的代码,因为这是一个更大的代码生成库的一部分,它会对生成的代码进行进一步的转换,因此我不能做类似的操作:

 (defn f [image] 
(fn [](.getRGB image 0 0)) )


解决方案

不知道你需要什么,可以创建使用以下欺骗来欺骗任意对象的代码:

 (def objs(atom []))


(defn make-code-that-evals-to [x]
(let [
nobjs(swap!objs#(conj%x))
i (dec(count nobjs))]
`(nth〜i @objs)))

然后您可以:

 > (eval(make-code-that-evals-to * out *))
#< PrintWriter java.io.PrintWriter@14aed2c>

这只是一个概念的证明,它泄漏正在产生的对象 -

:可以通过以下方式防止泄漏(邪恶!)hack :



上述代码通过在生成代码时从外部存储对象引用来绕过eval的编译器。这可以推迟。对象引用可以存储在生成的代码中,编译器被宏旁路。将引用存储在代码中意味着垃圾收集器正常工作。



关键是包装对象的宏。它做原来的解决方案(即存储对象外部绕过编译器),但只是在编译之前。生成的表达式检索外部引用,然后删除以防止泄漏。



注意:这是邪恶。宏的扩展时间是全局副作用发生的最不可取的地方,这正是这个解决方案的作用。



现在代码:

 (def objs(atom {}))

这里是临时存储对象的位置,由唯一键(字典)键入。

 (defmacro objwrap [x sym] 
(do
(swap!objs#(assoc%sym x));宏扩展中的全局副作用
`(let [o#(@objs〜sym )]
(do
(swap!objs#(dissoc%〜sym))
o#)))))

这是生成代码中的邪恶宏,保留对象引用 x sym 。在编译之前,它将对象存储在外部字典中的 sym 下,并生成检索它的代码,删除外部引用并返回检索的对象。

 (defn make-code-that-evals-to [x] 
(let [s 17];请将17替换为生成的唯一键

没有什么奇怪,只是包装对象在邪恶的宏,以及一个唯一的键。



当然,如果你只展开宏而不评估其结果,你仍然得到泄漏。


I want to embed a Java object (in this case a BufferedImage) in Clojure code that can be evald later.

Creating the code works fine:

(defn f [image]
     `(.getRGB ~image 0 0))
=> #'user/f

(f some-buffered-image)
=> (.getRGB #<BufferedImage BufferedImage@5527f4f9: type = 2 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=ff000000 IntegerInterleavedRaster: width = 256 height = 256 #Bands = 4 xOff = 0 yOff = 0 dataOffset[0] 0> 0 0)

However you get an exception when trying to eval it:

(eval (f some-buffered-image))
=> CompilerException java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: BufferedImage@612dcb8c: type = 2 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=ff000000 IntegerInterleavedRaster: width = 256 height = 256 #Bands = 4 xOff = 0 yOff = 0 dataOffset[0] 0, compiling:(NO_SOURCE_PATH:1) 

Is there any way to make something like this work?

EDIT:

The reason I am trying to do this is that I want to be able to generate code that takes samples from an image. The image is passed to the function that does the code generation (equivalent to f above), but (for various reasons) can't be passed as a parameter to the compiled code later.

I need to generate quoted code because this is part of a much larger code generation library that will apply further transforms to the generated code, hence I can't just do something like:

(defn f [image] 
  (fn [] (.getRGB image 0 0)))

解决方案

Not sure what you need it for, but you can create code that evals to an arbitrary object using the following cheat:

(def objs (atom []))


(defn make-code-that-evals-to [x] 
    (let [
         nobjs (swap! objs #(conj % x)) 
         i (dec (count nobjs))]
     `(nth ~i @objs)))

Then you can:

> (eval (make-code-that-evals-to *out*))
#<PrintWriter java.io.PrintWriter@14aed2c>

This is just a proof of concept and it leaks the objects being produced - you could produce code that removes the reference on eval but then you could eval it only once.

Edit: The leaks can be prevented by the following (evil!) hack:

The above code bypasses eval's compiler by storing the object reference externally at the time the code is generated. This can be deferred. The object reference can be stored in the generated code with the compiler being bypassed by a macro. Storing the reference in the code means the garbage collector works normally.

The key is the macro that wraps the object. It does what the original solution did (i.e. store the object externally to bypass the compiler), but just before compilation. The generated expression retrieves the external reference, then deletes to prevent leaks.

Note: This is evil. Expansion-time of macros is the least desirable place for global side-effects to occur and this is exactly what this solution does.

Now for the code:

(def objs (atom {}))

Here's where will temporarily store the objects, keyed by unique keys (dictionary).

(defmacro objwrap [x sym]
  (do
   (swap! objs #(assoc % sym x) ) ; Global side-effect in macro expansion
   `(let [o# (@objs ~sym)]
      (do
        (swap! objs #(dissoc % ~sym))
        o#))))

This is the evil macro that sits in the generated code, keeping the object reference in x and a unique key in sym. Before compilation it stores the object in the external dictionary under the key sym and generates code that retrieves it, deletes the external reference and returns the retrieved object.

(defn make-code-that-evals-to [x]
  (let [s 17]    ; please replace 17 with a generated unique key. I was lazy here.
  `(objwrap ~x ~s)))

Nothing fancy, just wrap the object in the evil macro, together with a unique key.

Of course if you only expand the macro without evaluating its result, you'll still get a leak.

这篇关于在Clojure代码中嵌入任意对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆