This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
TheOddByte's profile picture

Assert function with error level

Started by TheOddByte, 20 June 2015 - 12:35 AM
TheOddByte #1
Posted 20 June 2015 - 02:35 AM
I've been scratching my head about this for quite a while now, because I wanted to create a custom assert function that supported error levels.
It would be easy if it just would be basic calls like

assert( 1 == 2, "one is not equal to two!", 0 )

But what really makes this troublesome is when you try to use functions that return multiple values, because if you use an error message and/or level then it only returns one value, that is, if the condition is true ofcourse, like in this example, where you can see the difference when only supplying the first argument( condition ) versus supplying all the arguments( condition, message, level ).

--# Without error level
local args = { assert_level( foo( true ) ) }
print( unpack( args ) ) --# Output: bar

--# With error message and level
local args = { assert_level( foo( true ), "failed to get values", 2 ) }
print( unpack( args ) ) --# Output: b failed to get values 2
I've figured out that's because when you pass a function call as the first argument, and it returns more than one value, those values gets overwritten by the arguments message and level that you pass onto the assert_level function, but I still haven't figured out how to solve this( if it's even possible )

The code I'm usingYou can find the original code here: http://lua.2524044.n...39p7645366.html

This what the assert_level function looks like

local assert_level = function( condition, message, level )
	if not condition then
		if type( message ) ~= "string" then
			error( "string expected, got " .. type( message ), 2 )
		elseif level and type( level ) ~= "number" then
			error( "number expected, got " .. type( level ), 2 )
		end
		level = level == 0 and 0 or level == 1 and 1 or 2
		error( message, level )
	end
	return condition, message, level
end

And this is the foo function, not really needed as it was used for testing purposes

local function foo( condition )
	assert_level( type( condition ) == "boolean", "Must call foo() with a boolean", 2 )
	local a, b, c = "b", "a", "r"
	if condition then
		return a, b, c
	else
		return nil, "an error message"
	end
end

So I'm basically wondering, is there a way to make this work? Or have I come to a dead end?
Bomb Bloke #2
Posted 20 June 2015 - 03:15 AM
I've figured out that's because when you pass a function call as the first argument, and it returns more than one value, those values gets overwritten by the arguments message and level that you pass onto the assert_level function, but I still haven't figured out how to solve this( if it's even possible )

It's not so much that they get "overwritten", so much as "you end up not passing them at all".

If you want to pass a "single term" representation of multiple values, while actually passing all of those values, then it has to go at the end of your parameter list. Full stop. Otherwise, you'll only pass the first value in your list. For example, this sort of thing is valid:

print(...)           -- Prints val1, val2, etc
print("blah", ...)   -- Prints "blah", val1, val2, etc

… and this sort of thing is not:

print(..., "blah")   -- Prints val1 only, then "blah".

It doesn't matter whether you're using … or unpack() or foo(), if it resolves to multiple values, and you actually want to pass all those values, then it's gotta go at the end of your argument list.

Now in your case, you can't just re-order things; the assert_level function would have no way of knowing whether the first arguments were part of the condition, or whether they represented the error and level. So I'd say your answer is to simply rig the game so that you only need to pass one "condition" value to assert_level, eg:

local args = {foo(true)}
assert_level( #args == 3, "failed to get values", 2 )
print( unpack( args ) ) --# Output: b a r

Probably not the answer you're wanting, but it's either that, or make two different assert_level functions - one which expects an error message + level at the start of the argument list, and one which does not.
TheOddByte #3
Posted 20 June 2015 - 01:46 PM
- snip -
While the suggestion is not ideal, it's still better than nothing, I did not notice that I forgot to pass "…" in the post, as I am using it in the code on my PC, I didn't copy-paste from NP++ as destroys my indentation, so anyway, this is the code I'm using now and I haven't found any problems with it, thanks Bomb Bloke! :D/>

local assert_level = function( message, level, condition, ... )
    if not condition then
        if type( message ) ~= "string" then
            error( "string expected, got " .. type( message ), 2 )
        elseif level and type( level ) ~= "number" then
            error( "number expected, got " .. type( level ), 2 )
        end
        level = level == 0 and 0 or level == 1 and 1 or 2
        error( message, level )
    end
    return condition, ...
end