2011-11-16 75 views
1

我在執行一個宏來執行一些Java互操作時收到一個空指針異常,但我找不出原因。使用Clojure宏中的綁定的空指針異常

我使用嵌套數據的地圖設置一個Java對象的字段,並且因爲地圖鍵的名稱是一樣的對象的字段名,我創建了一個宏:

(defmacro set-keys! [pose m k klst] 
    `(set! (. ~pose ~(symbol (name k))) 
     (double-array (map #(% (~(keyword (name k)) ~m)) ~klst)))) 

爲了測試,我定義pse要與設置的關注爲零領域中的一個初始化的Java對象,我定義爲mp的簡化圖,將仍起作用:

(def mp {:pos {:x 1 :y 2}) 

現在到了奇怪的行爲。用顯式類型的參數執行set-keys!工作:

user> (set-keys! pse mp :pos [:x :y]) 
#<double[] [[email protected]> 

但是,如果使用運行的代碼在一個let調用內部:

user> (let [x :pos y [:x :y]] 
     (set-keys! pse mp x y)) 

我得到一個空指針異常與「沒有消息。」作爲消息。這裏有什麼問題?

編輯:我與(def pse (pose_t.))定義psepose_t是生成的類,並且pose_t()構造函數初始化所有陣列在其字段中,但不進行初始化的值。的類代碼爲簡化示例的相關行是:

public double pos[]; 

public pose_t() { 
    pos = new double[3]; 
} 

回答

3

宏參數不被評估,所以~(keyword (name k))擴展爲 - 大約 - (keyword (name (quote x))) - 這是:X,不:POS

在至少我想這就是問題所在。如果不是,如果您可以包含pse的定義將會很好。

附錄:你可以檢查一下宏實際上擴大到通過評估:

user> (macroexpand-1 '(set-keys! pse mp x y)) 
(set! (. pse x) (clojure.core/double-array (clojure.core/map 
    (fn* [p1__2066__2067__auto__] (p1__2066__2067__auto__ (:x mp))) y))) 

注:X

附錄2:(. obj field)居然還沒有評估field,這意味着你會必須使用java反射來使字段參數動態化。

+0

你說得對,就是這個問題。對於那部分我可以把'〜(關鍵字(名字k))'改爲'〜k',但是我會用'〜(symbol(name k))'擴展爲'x' 'pos'。爲什麼不能用unquote force'x'來評估?是因爲宏擴展發生在運行時?如果是這樣,我怎樣才能以編程方式將參數發送給宏?我最終將不得不在傳遞給map的函數內使用'set-keys!'在集合上調用'map'。 – troyastorino

+0

是的,這是因爲宏操作* code * - 不是值。 unquote不計算,它插入作爲參數給出的文字表達式(一段代碼)。據我所知,使用普通函數和java反射或關鍵字到字段賦值函數的固定映射,可以更清晰地編寫所需的功能。也就是說,如果您確實需要動態傳遞值 - 如果您可以「靜態」傳遞字段名稱,那麼您已經有了一個可行的解決方案。 –