I’m getting a Null Pointer Exception while in executing a macro to do some Java interop, and I can’t figure out why.
I’m setting the fields of a Java object using a map with nested data, and since the names of the map keys are the same as the field names of the object, I created a macro:
(defmacro set-keys! [pose m k klst]
`(set! (. ~pose ~(symbol (name k)))
(double-array (map #(% (~(keyword (name k)) ~m)) ~klst))))
For testing, I defined pse to be an initialized Java object with the fields of interest set to zero, and I defined mp as a simplified map that would still work:
(def mp {:pos {:x 1 :y 2})
Now comes the odd behavior. Executing set-keys! with explicitly typed arguments works:
user> (set-keys! pse mp :pos [:x :y])
#<double[] [D@691ba57a>
But if I use run the code in a inside a let call:
user> (let [x :pos y [:x :y]]
(set-keys! pse mp x y))
I get a Null Pointer Exception with “No message.” as the message. What is the issue here?
EDIT: I defined pse with (def pse (pose_t.)), pose_t is a generated class, and the pose_t() constructor initializes all the arrays in its fields, but doesn’t initialize the values. The relevant lines of the class code for the simplified example are:
public double pos[];
public pose_t() {
pos = new double[3];
}
Macro arguments are not evaluated, so
~(keyword (name k))expands to – roughly –(keyword (name (quote x)))– which is :x, not :posAt least I guess that’s the problem. If not, it would be nice if you could include a definition for
pse.Addendum: you can inspect what the macro is actually expanding to by evaluating:
Note the 😡
Addendum 2:
(. obj field)actually also doesn’t evaluatefield, meaning you’d have to use java reflection to make the field argument dynamic.