Checking the Type of many Variables/Parameters all at once

User avatar
SegFault22
Member
 
Posts: 870
Joined: Mon May 21, 2012 03:17

Checking the Type of many Variables/Parameters all at once

by SegFault22 » Mon Oct 31, 2016 07:09

I'm currently working on a mod which involves functions. Some of these functions can take many parameters. For debugging purposes, the functions always "verify" the type of each parameter, and if any are incorrect, it will throw an error message. Currently, this is done with code that looks something like this:
Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
function mod.add_itemtype(id,name,groups,stackmax)
   if type(id) == "string" and type(name) == "string" and type(groups) == "table" and type(stackmax) == "number" then
      -- important stuff here
   else
      -- throw an error
      error("add_itemtype() was called with incorrect parameters")
   end
end

However, I soon realized that it is probably quite inefficient to check all of those parameters this way, and that there is probably a better way, either saving loading time or resulting in code which is much shorter in the type-checking part.

If there is no better way, I am considering removing type checking entirely. This would give less information in the error message about which function produced the error, and you would have to check the stack trace - the error message would be something cryptic like "Table expected, got nil" and not of much use, except when you have to determine which parameter got the wrong type, and that is after you find in the stack the function which produced the error. I would prefer that my mod's functions give some helpful debug information via the error message in the event that such an error occurs, instead of producing a cryptic message and requiring the user to examine the stack before looking at the code where they called the function. You could say that you should still use the stack to find which line of which script has the function call which caused the error, which is true - but some users may not be that "advanced", and would possibly find it somewhat more straightforward to go look at where they called that function mentioned in the error message, and find which parameter is an incorrect type.

So is there a better way to check the type of many parameters at once (iteratively, like ipairs)?
If there is not any better way (or even if there is a better way), would it be best to remove type checking entirely?
 

Byakuren
Member
 
Posts: 441
Joined: Tue Apr 14, 2015 01:59
GitHub: raymoo
IRC: Hijiri

Re: Checking the Type of many Variables/Parameters all at on

by Byakuren » Mon Oct 31, 2016 08:52

You could have some kind of function wrapper, something like
Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
local function typechecked(types, f)
  return function(...)
    local args = {...}
    for i, desired_type in ipairs(types) do
      local actual_type = type(args[i])
      if actual_type ~= desired_type then
        error("Your value makes me mad")
      end
    end
    f(...)
  end
end


Usage:
Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
mod.add_itemtype = typechecked({"string", "string", "table", "number"}, function(id, name, groups, stackmax)
  -- important stuff here
end)
Every time a mod API is left undocumented, a koala dies.
 

User avatar
rubenwardy
Member
 
Posts: 4500
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy

Re: Checking the Type of many Variables/Parameters all at on

by rubenwardy » Mon Oct 31, 2016 10:42

Tip:

Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
assert(cond, msg)


Works like

Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
if not cond then
    error(msg)
end


Should make your code nicer.

You could define a like:

Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
local function a(name, var, type)
    assert(type(var) == type, "incorrect parameter type: " ..  name ..  " should be " ..  type)
end


Used like:

Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
a("foo", foo, "string")
 

User avatar
SegFault22
Member
 
Posts: 870
Joined: Mon May 21, 2012 03:17

Re: Checking the Type of many Variables/Parameters all at on

by SegFault22 » Mon Oct 31, 2016 20:19

rubenwardy wrote:Tip:

Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
assert(cond, msg)


Works like

Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
if not cond then
    error(msg)
end


Should make your code nicer.

You could define a like:

Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
local function a(name, var, type)
    assert(type(var) == type, "incorrect parameter type: " ..  name ..  " should be " ..  type)
end


Used like:

Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
a("foo", foo, "string")

Thank you. I will use assert() in type checking, because it looks better than how I was using error() directly. For some reason in the past I decided to not use assert(), but now I don't remember what it was, and the code has changed so much since then that I probably won't have any problems using assert().

I like that last function you suggested, because I can put it somewhere in a script that is run before the other functions load, and call it from within any of those functions, instead of checking the type directly from within those functions. This way is especially good because the error message can be different for each parameter, instead of the same message when any of the parameters are wrong.
 

User avatar
kaeza
Member
 
Posts: 2141
Joined: Thu Oct 18, 2012 05:00
GitHub: kaeza
IRC: kaeza diemartin blaaaaargh
In-game: kaeza

Re: Checking the Type of many Variables/Parameters all at on

by kaeza » Tue Nov 01, 2016 01:24

You should not worry about the performance of those checks. It's highly unlikely you will get any meaningful performance improvement.

That said, I suggest something like this:
Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
local function do_something_fast(x, y)
  return x+y
end

local function typecheck(t, v)
  local vt = type(v)
  return assert(t == vt, t.." expected, got "..vt)
end

local function do_something_safe(x, y)
  typecheck("number", x)
  typecheck("number", y)
  return do_something_fast(x, y)
end

Expose both functions and let callers choose between "fast" (no type check), or "safe" versions.
Your signature is not the place for a blog post. Please keep it as concise as possible. Thank you!

Check out my stuff! | Donations greatly appreciated! PayPal | BTC: 1DFZAa5VtNG7Levux4oP6BuUzr1e83pJK2
 

User avatar
SegFault22
Member
 
Posts: 870
Joined: Mon May 21, 2012 03:17

Re: Checking the Type of many Variables/Parameters all at on

by SegFault22 » Tue Nov 01, 2016 18:08

kaeza wrote:You should not worry about the performance of those checks. It's highly unlikely you will get any meaningful performance improvement.

That said, I suggest something like this:
Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
local function do_something_fast(x, y)
  return x+y
end

local function typecheck(t, v)
  local vt = type(v)
  return assert(t == vt, t.." expected, got "..vt)
end

local function do_something_safe(x, y)
  typecheck("number", x)
  typecheck("number", y)
  return do_something_fast(x, y)
end

Expose both functions and let callers choose between "fast" (no type check), or "safe" versions.

This will be very useful. I could make a configuration option to use type checking or do functions the fast way, and allow mods to use the type-checking functions for whatever purposes on their end. Something like this:

Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
mod = {}
mod.safemode_params = true

function mod.typecheck(v,t)
  local vt == type(v)
  return assert(vt == t, t.." expected, got "..vt
end

function mod.add_item(itemtype,material)
  if mod.safemode_params then mod.typecheck(itemtype,"string") mod.typecheck(material,"string") end
  local id     -- and so on
end

That would be much cleaner, than something like what I was doing before:

Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
mod = {}
function mod.add_item(itemtype,material)
  if type(itemtype) == "string" and type(material) == "string" then
    local id    -- and so on
  else
    error("some parameter is not the right type")
  end
end

It would also be better than something like this:

Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
mod = {}
function mod.add_item(itemtype,material)
  assert(type(itemtype) == "string", "string expected, got "..type(itemtype)
  assert(type(material) == "string", "string expected, got "..type(material)
  local id --    and so on

I will put the "safe type check" function in a separate lua script (the one for utilities) in the mod, so that it doesn't clutter up some other script.
Thank you.
 


Return to Modding Discussion

Who is online

Users browsing this forum: No registered users and 63 guests

cron