2012-05-24 7 views
5

に任意のオブジェクトを埋め込む私はeval後日間することができるClojureのコードで(この場合のBufferedImageに)Javaオブジェクトを埋め込みたいです。コードの作成Clojureのコード

が正常に動作します:

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

(f some-buffered-image) 
=> (.getRGB #<BufferedImage [email protected]: 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) 

evalにそれをしようとしたときに例外を取得しかし:

(eval (f some-buffered-image)) 
=> CompilerException java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: [email protected]: 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) 

この作品のようなものを作るための方法はありますか?

EDIT:

私はこれを行うにしようとしています理由は、私がイメージからサンプルを取るコードを生成できるようにしたいということです。画像は、(上記fに相当)符号の生成を行いますが、(様々な理由のために)後でコンパイルされたコードにパラメータとして渡すことができない関数に渡されます。

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

このエラーを返すのはちょうどevalですか?マクロ(例えば 'if')はそれをうまく使えますか?もしそうなら、それはおそらくevalが物事をテキストを通過させるようにしているからです(これはevalには意味があります)。これが問題なのであれば、マクロが必要なときに必要なときにevalを使用している可能性があります。 –

+0

@andrewcooke私が理解しているように、 'eval'は物事が* text *を通過することを強制しません。 Eval(コンパイル経由)は、オブジェクトを作成するJVMコードを生成します。未知のオブジェクトに使用される最後の手段は、シリアライゼーションを介してテキストを読むことです。 –

+2

@mikera引用符で囲んだものとevalを使って 'defn f [a](fn [](.getRGB a 0 0))')のようなやり方をした理由についてコメントすることができますか? – user100464

答えて

2

ないあなたがそれを必要なものを確認していますが、次のチートを使用して、任意のオブジェクトに試用版のコードを作成することができます

(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 [email protected]> 

これはただでありますコンセプトの証明は、それは、オブジェクトが生成されて漏れた - あなたはevalの上の参照を削除するコードを生成することができますが、あなたは一度だけ、それをevalしことができます。

編集:リークは以下によって防ぐことができるハック(悪!):

上記のコードは、コードが生成された時点で、外部のオブジェクト参照を格納することにより、evalののコンパイラをバイパスします。これは延期することができます。オブジェクト参照は生成されたコードに格納され、コンパイラはマクロによってバイパスされます。コード内に参照を格納すると、ガベージコレクタは正常に動作します。

キーは、オブジェクトをラップするマクロです。それは、元の解決策が行ったこと(すなわち、オブジェクトをコンパイラをバイパスするために外部に格納する)を行うが、コンパイルの直前である。生成された式は外部参照を取得し、リークを防ぐために削除します。

注:これは悪です。マクロの展開時間は、グローバルな副作用が発生するための最も望ましくない場所であり、これはまさにこの解決策が行うことです。コードのための今

:一時的に一意キー(辞書)をキーとするオブジェクトを格納する場所

(def objs (atom {})) 

がここにあります。

(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#)))) 

これはxにおけるオブジェクト参照とsymに一意のキーを保持し、生成されたコードに座っ悪マクロあります。コンパイルする前に、オブジェクトを外部辞書のキーの下に格納し、それを取得するコードを生成し、外部参照を削除し、取得したオブジェクトを返します。

(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))) 

悪意のあるマクロ内のオブジェクトを一意のキーと共にラップするだけで、気にすることはありません。

もちろん、結果を評価せずにマクロを展開しても、まだ漏れがあります。

+0

素敵なアイデア!しかし、私はメモリリークがこれを少し厄介にすることに同意します – mikera

+0

@mikera私はそれを修正する方法を持っています、時間が許せば編集します:) –

+0

@mikera解決策が追加されました。これは穀物に逆らっているように見えるので、これを回避する方法を探しています。 –

3

私が推測:

は、私はそれゆえ、私はちょうどような何かを行うことができない、これは生成されたコードにさらに変換を適用する非常に大きなコード生成ライブラリの一部であるため、引用されたコードを生成する必要がありますあなたはコンパイル時にオブジェクト(または必要なオブジェクトを作成する方法を)取るマクロを記述する必要があり、あるべきバイナリ形式でそのオブジェクト(バイト配列)とマクロの出力をシリアル化 - を参照してくださいシンボルをシリアル化されていないデータをデシリアライズすることによってオブジェクトを取得するために使用できる関数とを使用します。

+0

なぜシリアル化が必要ですか?コンパイルされたコードは外部バイト配列に依存するので、なぜオブジェクト配列にして、元のオブジェクトへの参照を保持していないのでしょうか? –

+0

基本的には、コンパイル時に評価されるマクロの場合、ex: '(embed-resource" my-file.jpeg ")'の場合、マクロ埋め込みリソースはファイルを読み込み、コード内にjava配列として格納しますこれはコード自体に画像を埋め込むことを可能にします – Ankur

+0

ありがとう、私は今それを得ると思います。私が正しく理解していれば、コード生成時にオブジェクトを直ちにシリアル化することもできます(OPの例では関数 'f')。 –

0

なぜ: (のdefmacro M [IMG] `(.getRGB〜のIMG 0)) 次いでuは書くことができる:(M一部緩衝画像)

差をf関数であることですその引数は、その本体が評価される前に評価されます。したがって、イメージオブジェクト自体は生成されたコード内に配置されます。しかし、マクロの場合、その引数は評価されません。そのため、コード内にはsome-buffered-imageというシンボルだけが配置されます。生成されるコードは、(.getRGB some-buffered-image 0 0)です。ちょうどあなたが直接ソースコードを書くように。私はそれが欲しいものだと思う。

しかし、なぜ生成されたコードにオブジェクトを配置できないのですか?答えは:はい、できます。例外メッセージは真実ではありません。生成されたコードにいくつかの種類のオブジェクトを埋め込むことができますが、すべての種類のオブジェクトを埋め込むことはできません。これらは、シンボル、数字、文字、文字列、正規表現パタン、キーワード、ブール値、リスト、マップ、セットなどを含みます。これらのオブジェクトはすべてClojureコンパイラによって理解されます。他の言語のキーワード、演算子、リテラルに似ています。 Clojureコンパイラには、すべての種類のオブジェクトを知っている必要はありません.CやJavaコンパイラがその構文に含まれていないすべての単語を知る必要はありません。