From c26cc1a5083fbea62aba8fdd952ca387aad2a928 Mon Sep 17 00:00:00 2001 From: David Briscoe Date: Sat, 26 Mar 2022 17:16:09 -0700 Subject: [PATCH] Include offending type in precondition failure msg Output the type that was incorrect to make it more obvious what the user is doing wrong without them adding logging. To avoid string building during lots of vector math, introduced a private precond module that only builds the strings when an error occurs. It uses error() to put the resulting error message at the caller to cpml. Before: lua: ~/cpml/modules/vec2.lua:52: new: Wrong argument type for x ( expected) lua: ~/cpml/modules/vec2.lua:424: __add: Wrong argument type for right hand operand. ( expected) example stack traceback: [C]: in function 'assert' ~/cpml/modules/vec2.lua:424: in metamethod '__add' test_cpml.lua:32: in main chunk [C]: in ? After: lua: test_cpml.lua:31: new: Wrong argument type for x: string ( expected) lua: test_cpml.lua:32: __add: Wrong argument type 'string' for right hand operand. ( expected) example stack traceback: [C]: in function 'error' ~/cpml/modules/_private_precond.lua:13: in function 'modules._private_precond.assert' ~/cpml/modules/vec2.lua:425: in metamethod '__add' test_cpml.lua:32: in main chunk [C]: in ? The tracebacks are longer, but the initial error is at the location of the mistake and the output includes the input type. --- modules/_private_precond.lua | 17 +++++++++++++++++ modules/color.lua | 17 +++++++++-------- modules/mat4.lua | 3 ++- modules/quat.lua | 31 ++++++++++++++++--------------- modules/vec2.lua | 21 +++++++++++---------- modules/vec3.lua | 29 +++++++++++++++-------------- 6 files changed, 70 insertions(+), 48 deletions(-) create mode 100644 modules/_private_precond.lua diff --git a/modules/_private_precond.lua b/modules/_private_precond.lua new file mode 100644 index 0000000..af4106f --- /dev/null +++ b/modules/_private_precond.lua @@ -0,0 +1,17 @@ +-- Preconditions for cpml functions. +local precond = {} + + +function precond.typeof(t, expected, msg) + if type(t) ~= expected then + error(("%s: %s (<%s> expected)"):format(msg, type(t), expected), 3) + end +end + +function precond.assert(cond, msg, ...) + if not cond then + error(msg:format(...), 3) + end +end + +return precond diff --git a/modules/color.lua b/modules/color.lua index 094459c..414a451 100644 --- a/modules/color.lua +++ b/modules/color.lua @@ -3,6 +3,7 @@ local modules = (...):gsub('%.[^%.]+$', '') .. "." local utils = require(modules .. "utils") +local precond = require(modules .. "_private_precond") local color = {} local color_mt = {} @@ -103,20 +104,20 @@ end function color.new(r, g, b, a) -- number, number, number, number if r and g and b and a then - assert(type(r) == "number", "new: Wrong argument type for r ( expected)") - assert(type(g) == "number", "new: Wrong argument type for g ( expected)") - assert(type(b) == "number", "new: Wrong argument type for b ( expected)") - assert(type(a) == "number", "new: Wrong argument type for a ( expected)") + precond.typeof(r, "number", "new: Wrong argument type for r") + precond.typeof(g, "number", "new: Wrong argument type for g") + precond.typeof(b, "number", "new: Wrong argument type for b") + precond.typeof(a, "number", "new: Wrong argument type for a") return new(r, g, b, a) -- {r, g, b, a} elseif type(r) == "table" then local rr, gg, bb, aa = r[1], r[2], r[3], r[4] - assert(type(rr) == "number", "new: Wrong argument type for r ( expected)") - assert(type(gg) == "number", "new: Wrong argument type for g ( expected)") - assert(type(bb) == "number", "new: Wrong argument type for b ( expected)") - assert(type(aa) == "number", "new: Wrong argument type for a ( expected)") + precond.typeof(rr, "number", "new: Wrong argument type for r") + precond.typeof(gg, "number", "new: Wrong argument type for g") + precond.typeof(bb, "number", "new: Wrong argument type for b") + precond.typeof(aa, "number", "new: Wrong argument type for a") return new(rr, gg, bb, aa) end diff --git a/modules/mat4.lua b/modules/mat4.lua index 6cbfbfc..48530ba 100644 --- a/modules/mat4.lua +++ b/modules/mat4.lua @@ -6,6 +6,7 @@ local vec2 = require(modules .. "vec2") local vec3 = require(modules .. "vec3") local quat = require(modules .. "quat") local utils = require(modules .. "utils") +local precond = require(modules .. "_private_precond") local private = require(modules .. "_private_utils") local sqrt = math.sqrt local cos = math.cos @@ -861,7 +862,7 @@ function mat4_mt.__eq(a, b) end function mat4_mt.__mul(a, b) - assert(mat4.is_mat4(a), "__mul: Wrong argument type for left hand operand. ( expected)") + precond.assert(mat4.is_mat4(a), "__mul: Wrong argument type '%s' for left hand operand. ( expected)", type(a)) if vec3.is_vec3(b) then return vec3(mat4.mul_vec4({}, a, { b.x, b.y, b.z, 1 })) diff --git a/modules/quat.lua b/modules/quat.lua index 4173317..9755602 100644 --- a/modules/quat.lua +++ b/modules/quat.lua @@ -4,6 +4,7 @@ local modules = (...):gsub('%.[^%.]+$', '') .. "." local constants = require(modules .. "constants") local vec3 = require(modules .. "vec3") +local precond = require(modules .. "_private_precond") local private = require(modules .. "_private_utils") local DOT_THRESHOLD = constants.DOT_THRESHOLD local DBL_EPSILON = constants.DBL_EPSILON @@ -58,20 +59,20 @@ quat.zero = new(0, 0, 0, 0) function quat.new(x, y, z, w) -- number, number, number, number if x and y and z and w then - assert(type(x) == "number", "new: Wrong argument type for x ( expected)") - assert(type(y) == "number", "new: Wrong argument type for y ( expected)") - assert(type(z) == "number", "new: Wrong argument type for z ( expected)") - assert(type(w) == "number", "new: Wrong argument type for w ( expected)") + precond.typeof(x, "number", "new: Wrong argument type for x") + precond.typeof(y, "number", "new: Wrong argument type for y") + precond.typeof(z, "number", "new: Wrong argument type for z") + precond.typeof(w, "number", "new: Wrong argument type for w") return new(x, y, z, w) -- {x, y, z, w} or {x=x, y=y, z=z, w=w} elseif type(x) == "table" then local xx, yy, zz, ww = x.x or x[1], x.y or x[2], x.z or x[3], x.w or x[4] - assert(type(xx) == "number", "new: Wrong argument type for x ( expected)") - assert(type(yy) == "number", "new: Wrong argument type for y ( expected)") - assert(type(zz) == "number", "new: Wrong argument type for z ( expected)") - assert(type(ww) == "number", "new: Wrong argument type for w ( expected)") + precond.typeof(xx, "number", "new: Wrong argument type for x") + precond.typeof(yy, "number", "new: Wrong argument type for y") + precond.typeof(zz, "number", "new: Wrong argument type for z") + precond.typeof(ww, "number", "new: Wrong argument type for w") return new(xx, yy, zz, ww) end @@ -456,19 +457,19 @@ function quat_mt.__eq(a,b) end function quat_mt.__add(a, b) - assert(quat.is_quat(a), "__add: Wrong argument type for left hand operand. ( expected)") - assert(quat.is_quat(b), "__add: Wrong argument type for right hand operand. ( expected)") + precond.assert(quat.is_quat(a), "__add: Wrong argument type '%s' for left hand operand. ( expected)", type(a)) + precond.assert(quat.is_quat(b), "__add: Wrong argument type '%s' for right hand operand. ( expected)", type(b)) return a:add(b) end function quat_mt.__sub(a, b) - assert(quat.is_quat(a), "__sub: Wrong argument type for left hand operand. ( expected)") - assert(quat.is_quat(b), "__sub: Wrong argument type for right hand operand. ( expected)") + precond.assert(quat.is_quat(a), "__sub: Wrong argument type '%s' for left hand operand. ( expected)", type(a)) + precond.assert(quat.is_quat(b), "__sub: Wrong argument type '%s' for right hand operand. ( expected)", type(b)) return a:sub(b) end function quat_mt.__mul(a, b) - assert(quat.is_quat(a), "__mul: Wrong argument type for left hand operand. ( expected)") + precond.assert(quat.is_quat(a), "__mul: Wrong argument type '%s' for left hand operand. ( expected)", type(a)) assert(quat.is_quat(b) or vec3.is_vec3(b) or type(b) == "number", "__mul: Wrong argument type for right hand operand. ( or or expected)") if quat.is_quat(b) then @@ -483,8 +484,8 @@ function quat_mt.__mul(a, b) end function quat_mt.__pow(a, n) - assert(quat.is_quat(a), "__pow: Wrong argument type for left hand operand. ( expected)") - assert(type(n) == "number", "__pow: Wrong argument type for right hand operand. ( expected)") + precond.assert(quat.is_quat(a), "__pow: Wrong argument type '%s' for left hand operand. ( expected)", type(a)) + precond.typeof(n, "number", "__pow: Wrong argument type for right hand operand.") return a:pow(n) end diff --git a/modules/vec2.lua b/modules/vec2.lua index be9f287..1d8a55f 100644 --- a/modules/vec2.lua +++ b/modules/vec2.lua @@ -3,6 +3,7 @@ local modules = (...):gsub('%.[^%.]+$', '') .. "." local vec3 = require(modules .. "vec3") +local precond = require(modules .. "_private_precond") local private = require(modules .. "_private_utils") local acos = math.acos local atan2 = math.atan2 @@ -49,16 +50,16 @@ vec2.zero = new(0, 0) function vec2.new(x, y) -- number, number if x and y then - assert(type(x) == "number", "new: Wrong argument type for x ( expected)") - assert(type(y) == "number", "new: Wrong argument type for y ( expected)") + precond.typeof(x, "number", "new: Wrong argument type for x") + precond.typeof(y, "number", "new: Wrong argument type for y") return new(x, y) -- {x, y} or {x=x, y=y} elseif type(x) == "table" or type(x) == "cdata" then -- table in vanilla lua, cdata in luajit local xx, yy = x.x or x[1], x.y or x[2] - assert(type(xx) == "number", "new: Wrong argument type for x ( expected)") - assert(type(yy) == "number", "new: Wrong argument type for y ( expected)") + precond.typeof(xx, "number", "new: Wrong argument type for x") + precond.typeof(yy, "number", "new: Wrong argument type for y") return new(xx, yy) @@ -401,19 +402,19 @@ function vec2_mt.__eq(a, b) end function vec2_mt.__add(a, b) - assert(vec2.is_vec2(a), "__add: Wrong argument type for left hand operand. ( expected)") - assert(vec2.is_vec2(b), "__add: Wrong argument type for right hand operand. ( expected)") + precond.assert(vec2.is_vec2(a), "__add: Wrong argument type '%s' for left hand operand. ( expected)", type(a)) + precond.assert(vec2.is_vec2(b), "__add: Wrong argument type '%s' for right hand operand. ( expected)", type(b)) return a:add(b) end function vec2_mt.__sub(a, b) - assert(vec2.is_vec2(a), "__add: Wrong argument type for left hand operand. ( expected)") - assert(vec2.is_vec2(b), "__add: Wrong argument type for right hand operand. ( expected)") + precond.assert(vec2.is_vec2(a), "__add: Wrong argument type '%s' for left hand operand. ( expected)", type(a)) + precond.assert(vec2.is_vec2(b), "__add: Wrong argument type '%s' for right hand operand. ( expected)", type(b)) return a:sub(b) end function vec2_mt.__mul(a, b) - assert(vec2.is_vec2(a), "__mul: Wrong argument type for left hand operand. ( expected)") + precond.assert(vec2.is_vec2(a), "__mul: Wrong argument type '%s' for left hand operand. ( expected)", type(a)) assert(vec2.is_vec2(b) or type(b) == "number", "__mul: Wrong argument type for right hand operand. ( or expected)") if vec2.is_vec2(b) then @@ -424,7 +425,7 @@ function vec2_mt.__mul(a, b) end function vec2_mt.__div(a, b) - assert(vec2.is_vec2(a), "__div: Wrong argument type for left hand operand. ( expected)") + precond.assert(vec2.is_vec2(a), "__div: Wrong argument type '%s' for left hand operand. ( expected)", type(a)) assert(vec2.is_vec2(b) or type(b) == "number", "__div: Wrong argument type for right hand operand. ( or expected)") if vec2.is_vec2(b) then diff --git a/modules/vec3.lua b/modules/vec3.lua index a457d08..ed8ea05 100644 --- a/modules/vec3.lua +++ b/modules/vec3.lua @@ -2,6 +2,7 @@ -- @module vec3 local modules = (...):gsub('%.[^%.]+$', '') .. "." +local precond = require(modules .. "_private_precond") local private = require(modules .. "_private_utils") local sqrt = math.sqrt local cos = math.cos @@ -50,18 +51,18 @@ vec3.zero = new(0, 0, 0) function vec3.new(x, y, z) -- number, number, number if x and y and z then - assert(type(x) == "number", "new: Wrong argument type for x ( expected)") - assert(type(y) == "number", "new: Wrong argument type for y ( expected)") - assert(type(z) == "number", "new: Wrong argument type for z ( expected)") + precond.typeof(x, "number", "new: Wrong argument type for x") + precond.typeof(y, "number", "new: Wrong argument type for y") + precond.typeof(z, "number", "new: Wrong argument type for z") return new(x, y, z) -- {x, y, z} or {x=x, y=y, z=z} elseif type(x) == "table" or type(x) == "cdata" then -- table in vanilla lua, cdata in luajit local xx, yy, zz = x.x or x[1], x.y or x[2], x.z or x[3] - assert(type(xx) == "number", "new: Wrong argument type for x ( expected)") - assert(type(yy) == "number", "new: Wrong argument type for y ( expected)") - assert(type(zz) == "number", "new: Wrong argument type for z ( expected)") + precond.typeof(xx, "number", "new: Wrong argument type for x") + precond.typeof(yy, "number", "new: Wrong argument type for y") + precond.typeof(zz, "number", "new: Wrong argument type for z") return new(xx, yy, zz) @@ -374,20 +375,20 @@ function vec3_mt.__eq(a, b) end function vec3_mt.__add(a, b) - assert(vec3.is_vec3(a), "__add: Wrong argument type for left hand operand. ( expected)") - assert(vec3.is_vec3(b), "__add: Wrong argument type for right hand operand. ( expected)") + precond.assert(vec3.is_vec3(a), "__add: Wrong argument type '%s' for left hand operand. ( expected)", type(a)) + precond.assert(vec3.is_vec3(b), "__add: Wrong argument type '%s' for right hand operand. ( expected)", type(b)) return a:add(b) end function vec3_mt.__sub(a, b) - assert(vec3.is_vec3(a), "__sub: Wrong argument type for left hand operand. ( expected)") - assert(vec3.is_vec3(b), "__sub: Wrong argument type for right hand operand. ( expected)") + precond.assert(vec3.is_vec3(a), "__sub: Wrong argument type '%s' for left hand operand. ( expected)", type(a)) + precond.assert(vec3.is_vec3(b), "__sub: Wrong argument type '%s' for right hand operand. ( expected)", type(b)) return a:sub(b) end function vec3_mt.__mul(a, b) - assert(vec3.is_vec3(a), "__mul: Wrong argument type for left hand operand. ( expected)") - assert(vec3.is_vec3(b) or type(b) == "number", "__mul: Wrong argument type for right hand operand. ( or expected)") + precond.assert(vec3.is_vec3(a), "__mul: Wrong argument type '%s' for left hand operand. ( expected)", type(a)) + precond.assert(vec3.is_vec3(b) or type(b) == "number", "__mul: Wrong argument type '%s' for right hand operand. ( or expected)", type(b)) if vec3.is_vec3(b) then return a:mul(b) @@ -397,8 +398,8 @@ function vec3_mt.__mul(a, b) end function vec3_mt.__div(a, b) - assert(vec3.is_vec3(a), "__div: Wrong argument type for left hand operand. ( expected)") - assert(vec3.is_vec3(b) or type(b) == "number", "__div: Wrong argument type for right hand operand. ( or expected)") + precond.assert(vec3.is_vec3(a), "__div: Wrong argument type '%s' for left hand operand. ( expected)", type(a)) + precond.assert(vec3.is_vec3(b) or type(b) == "number", "__div: Wrong argument type '%s' for right hand operand. ( or expected)", type(b)) if vec3.is_vec3(b) then return a:div(b)