Argcheck
A powerful (and blazing fast) argument checker and function overloading system for Lua or LuaJIT
Install / Use
/learn @torch/ArgcheckREADME
argcheck
A powerful function argument checker and function overloading system for Lua or LuaJIT.
argcheck generates specific code for checking arguments of a function. This
allows complex argument checking (possibly with optional values), with
little overhead (with LuaJIT). argcheck computes a
tree of all possible variants of arguments, allowing efficient overloading
and default argument management.
Installation
The easiest is to use luarocks.
If you use Torch, simply do
luarocks install argcheck
else
luarocks build https://raw.github.com/torch/argcheck/master/rocks/argcheck-scm-1.rockspec
You can also copy the argcheck directory where luajit (or lua) will
find it.
Changelog
-
Version 2.0 (git)
- Rewrote completely the code generation.
- Now creates a tree of possible argument paths (much more efficient).
- Thanks to the tree approach, many bugs have been fixed.
argcheckwill produce an error if there are ambiguous argument rules.- The feature
chainis still deprecated (but available). Useoverloadinstead. - True overloading is happening. Contrary to
chain,overloadfunctions must be overwritten. - Same functionalities than in 1.0, plus
- Handles named method calls
- Can generate a dot graphviz file of the argument paths for debugging purposes.
-
Version 1.0
- Simplified the way of calling
argcheck. - Same functionalities than before, except named method call were not handled anymore.
- Added the
calloption to call a function if the arguments match given rules. - Added the
chainoption to chain severalargcheckfunction calls (a cheap version of overloading). - Simplified the way of adding a type.
- Simplified the way of calling
-
Version 0.5
- Handling of default arguments (including defaulting to another argument), optional arguments (which might be
nil), named arguments, named only option. - Complicated way to handle method and functions.
- Calling function is mandatory.
- Handling of default arguments (including defaulting to another argument), optional arguments (which might be
Documentation
To use argcheck, you have to first require it:
local argcheck = require 'argcheck'
In the following, we assume this has been done in your script.
Note that argcheck does not import anything globally, to avoid cluttering
the global namespace. The value returned by the require is a function: for
most usages, it will be the only thing you need.
Note that in the following examples we do not use local variables for check functions or example functions. This is bad practice, but helpful if you want to cut-and-paste the code in your interactive lua to see how this is running.
The argcheck() function creates a fast pre-compiled function for checking
arguments, according to rules provided by the user. Assume you have a
function which requires a unique number argument:
function addfive(x)
print(string.format('%f + 5 = %f', x, x + 5))
end
You can make sure everything goes fine by creating the rule:
check = argcheck{
{name="x", type="number"}
}
function addfive(...)
local x = check(...)
print(string.format('%f + 5 = %f', x, x + 5))
end
If a user try to pass a wrong argument, too many arguments, or no arguments
at all, argcheck will complain:
arguments:
{
x = number --
}
stdin:2: invalid arguments
A rule must at least take a name field. The type field is optional
(even though it is highly recommended!). If type is not provided, argcheck will make
sure the given argument is not nil. If you want also to accept nil arguments, see the
opt option.
Default arguments
Arguments can have defaults:
check = argcheck{
{name="x", type="number", default=0}
}
In which case, if the argument is missing, argcheck will pass the default
one to your function:
> addfive()
0.000000 + 5 = 5.000000
Help (or doc)
argcheck encourages you to add help to your function. You can document each argument:
check = argcheck{
{name="x", type="number", default=0, help="the age of the captain"}
}
Or even document the function:
check = argcheck{
help=[[
This function is going to do a simple addition.
Give a number, it adds 5. Amazing.
]],
{name="x", type="number", default=0, help="the age of the captain"}
}
Then, if the user makes a mistake in the arguments, the error message becomes more clear:
> addfive('')
stdin:2: invalid arguments
This function is going to do a simple addition.
Give a number, it adds 5. Amazing.
arguments:
{
[x = number] -- the age of the captain [default=0]
}
Note that is (equivalently) possible to use the key doc= instead of help=.
Multiple arguments
Until now, our function had only one argument. Obviously, argcheck can
handle as many as you wish:
check = argcheck{
help=[[
This function is going to do a simple addition.
Give a number, it adds 5. Amazing.
]],
{name="x", type="number", default=0, help="the age of the captain"},
{name="msg", type="string", help="a message"}
}
function addfive(...)
local x, msg = check(...)
print(string.format('%f + 5 = %f', x, x + 5))
print(msg)
end
argcheck handles well various cases, including those where some arguments
with defaults values might be missing:
> addfive(4, 'hello world')
4.000000 + 5 = 9.000000
hello world
>
> addfive('hello world')
0.000000 + 5 = 5.000000
hello world
>
> addfive(4)
stdin:2: invalid arguments
This function is going to do a simple addition.
Give a number, it adds 5. Amazing.
arguments:
{
[x = number] -- the age of the captain [default=0]
msg = string -- a message
}
Default argument defaulting to another argument
Arguments can have a default value coming from another argument, with the
defaulta option. In the following
check = argcheck{
{name="x", type="number"},
{name="y", type="number", defaulta="x"}
}
function mul(...)
local x, y = check(...)
print(string.format('%f x %f = %f', x, y, x * y))
end
argument y will take the value of x if it is not passed during the function call:
> mul(3, 4)
3.000000 x 4.000000 = 12.000000
> mul(3)
3.000000 x 3.000000 = 9.000000
Default arguments function
In some more complex cases, sometimes one needs to run a particular function when
the given argument is not provided. The option defaultf is here to help.
idx = 0
check = argcheck{
{name="x", type="number"},
{name="y", type="number", defaultf=function() idx = idx + 1 return idx end}
}
function mul(...)
local x, y = check(...)
print(string.format('%f x %f = %f', x, y, x * y))
end
This will output the following:
> mul(3)
3.000000 x 1.000000 = 3.000000
> mul(3)
3.000000 x 2.000000 = 6.000000
> mul(3)
3.000000 x 3.000000 = 9.000000
<a name="argcheck.opt"/>
### Optional arguments
Arguments with a default value can be seen as optional. However, as they
do have a default value, the underlying function will never receive a nil
value. In some situations, one might need to declare an optional argument
with no default value. You can do this with the opt option.
check = argcheck{
{name="x", type="number", default=0, help="the age of the captain"},
{name="msg", type="string", help="a message", opt=true}
}
function addfive(...)
local x, msg = check(...)
print(string.format('%f + 5 = %f', x, x + 5))
print(msg)
end
In this example, one might call addfive() without the msg argument. Of
course, the underlying function must be able to handle nil values:
> addfive('hello world')
0.000000 + 5 = 5.000000
hello world
> addfive()
0.000000 + 5 = 5.000000
nil
Torch Tensors
argcheck supports Torch Tensors type checks.
Specific tensor types like Int, Float, or Double can be
checked with torch.<Type>Tensor.
Any tensor type can be checked with torch.*Tensor.
check = argcheck{
{name="anyTensor", type="torch.*Tensor"},
{name="fTensor", type="torch.FloatTensor"}
}
check(torch.IntTensor(), torch.FloatTensor()) -- Good.
check(torch.FloatTensor(), torch.FloatTensor()) -- Good.
check(torch.FloatTensor(), torch.IntTensor()) -- Invalid.
Specific per-rule check
It is possible to add an extra specific checking function for a given checking
rule, with the check option. This function will be called (with the corresponding argument)
in addition to the standard type checking. This can be useful for refined argument selection:
check = argcheck{
{name="x", type="number", help="a number between one and ten",
check=function(x)
return x >= 1 and x <= 10
end}
}
function addfive(...)
local x = check(...)
print(string.format('%f + 5 = %f', x, x + 5))
end
> addfive(3)
3.000000 + 5 = 8.000000
> addfive(11)
stdin:2: invalid arguments
arguments:
{
x = number -- a number between one and ten
}
Named arguments
argcheck handles named argument calls. Following the previous example, both
addfive(1, "hello world")
and
addfive{x=1, msg="hello world"}
are valid. However, ordered arguments are handled in a much faster way (especially with LuaJIT) than named arguments.
Method named arguments
The common way to define a "method" in Lua is by doing the following:
local object = {}
function object:foobar(x, msg) -- a method foobar
end
The syntax sugar call object:foobar(x, msg) is equivalent to the function call
object.foobar(object, x, msg)
Calling a method with named arguments would be done with
object:foobar{x=..., msg=...}
(where ... is the actual content of x and msg). This translates to
foobar(object, {x=..., msg=...}), which is not a regular named function
call, given that the object itself should not be treated as a named
argument. argcheck will handle such calls, provided the na
