FANDOM


The pcall command

The pcall command wraps a function call and catches any error that occurs during the function. The return values of pcall are either true, followed by the return values of the function, or false, followed by an error message. The arguments to pcall are the function name, followed by each argument that would normally be passed to the function.

In this example, we use a short (and error-prone) function to band a segment to free space. (For a correct version of this function, see band.Add().)

function BandSegment(segment, length)
    local bandID = band.Add(segment,segment-1,segment+1,length,0.0,0.0)
    band.SetGoalLength(bandID, length)
    return bandID
end
print(pcall(BandSegment,2,1.0)) -- okay
print(pcall(BandSegment,2,0.0)) -- error, zero length
print(pcall(BandSegment,1,1.0)) -- error, needs a segment before and after

true 1
false [string "..."]:7: bad argument #4 to 'Add' (rho must be > 0 and <= 10000)
false [string "..."]:7: bad argument #2 to 'Add' (segment index out of bounds)

Anonymous functions (closures)

An actual function definition can stand in for the function name. This can be convenient for simple functions. The function has no name, hence it is "anonymous". Here, we create a band and set its strength and goal length in one step, without checking for success.

function AddBandSafe(segment)
    return_code, bandID = pcall(
        function(segment)
            local bandID = BandSegment(segment,0.001)
            band.SetStrength(bandID,10.0)
            return bandID
        end,
    segment)
    if return_code then return bandID
    else return 0
    end
end
print(AddBandSafe(2)) -- okay
print(AddBandSafe(1)) -- error, needs segments on both sides

1 (success, band is # 1)
0 (failure, no band number)

The error command

The error command allows you to create your own errors, which can be caught by the error handler or appear in the output window. You can use the error command to resend an error that you have caught. This allows your error handler to perform some cleanup before continuing the error-handling process upwards. The optional second argument will cause Lua to report the line number of one of the calling functions instead of the actual line number of the error. This is useful if the error was caused by a bad argument passed in by the calling function.

In this example, we create two bands, but delete the first band if the second band fails. We also report the error at the calling location (one step above, arg = 2) instead of at band.Add (one step below) or at the error statement (arg = 1 or omitted). We also check for the possibility of band.Add returning 0 (some band functions do this).

function BandTwoSegments(seg1, seg2, length)
    local rc, bandID1, bandID2
    rc, bandID1 = pcall(BandSegment,seg1,length)
    if rc == false then
        error(bandID1,2) -- bandID1 contains the error
    elseif bandID1 == 0 then
        error("Failed to create band 1",2) -- custom error message
    end                                    -- don't know what went wrong exactly
    rc, bandID2 = pcall(BandSegment,seg2,length)
    if rc == false then
        band.Delete(bandID1) -- delete the first band
        error(bandID2,2) -- bandID2 contains the error
    elseif bandID2 == 0 then
        band.Delete(bandID1) -- delete first band
        error("Failed to create band 2",2) -- custom error message
    end
    return bandID1, bandID2
end
BandTwoSegments(2,3,1.0) -- okay
BandTwoSegments(2,1,1.0) -- error, should create then delete first band

The xpcall command and catching Cancel

The pcall command lets you catch errors that would normally terminate the script, and continue executing. However, pcall cannot catch the "Cancelled" error, because the script stops before the return code can be checked. To catch "Cancelled" you must use the xpcall command. Xpcall calls an error handling function instead of simply returning an error code. Cancel will still stop the program, but only after the error handler has returned.

Xpcall has the same return values as pcall. It is called differently, however. The first argument is the name of the function you want to protect. This function cannot accept any arguments, so you may want to wrap your function inside an anonymous function. The second argument is the name of the error handling function. The error handler accepts one argument, the error message, and returns one value, a possibly different error message.

You should print the error message either in the message handler or after checking the return code; otherwise, it won't appear in the output window at all.

You should take care to ensure your error handler and cleanup routines are reliable. If they have an error, the program will exit with an "Error in error handler" message.

This is how a typical script that catches cancel might look:

-- Various User Function
-- Save best result in slot 3
_best_score = -math.huge
function SaveBest()
    if current.GetScore() > _best_score then
        _best_score = current.GetScore()
        save.Quicksave(3)
    end
end

-- Change to loops, wiggle n times, saving best
function MainLoop(n) -- these routines need to be cleaned up after
    selection.SelectAll()
    structure.SetSecondaryStructureSelected("L")
    while n > 0 do
        print(n,"left")
        structure.WiggleAll(2)
        n = n - 1
        SaveBest()
    end
end

-- Delete any bands the script added, leaving originals untouched
NOriginalBands = band.GetCount()
function DeleteAddedBands()
    if NOriginalBands == 0 then band.DeleteAll() end
    while band.GetCount() > NOriginalBands do
        band.DeleteBand(band.GetCount())
    end
end

-- Do this when things end normally or user cancels
function CleanUp()
    save.LoadSecondaryStructure()
    selection.DeselectAll()
    DeleteAddedBands()
end

-- This is the function that handles the error message
function ErrorHandler(errmsg)
    if string.find(errmsg,"Cancelled") then
        print("User cancel.") -- print this instead of error message 
        save.Quickload(3) -- best stable solution in slot 3
        CleanUp()
    else -- it's a real error, not a cancel
        -- You may also want to do cleaning up stuff here
        print(errmsg) -- one place to print the error
    end
    return errmsg
end
-- Main User Code

-- This code does not need to be cleaned up after
print(puzzle.GetName())
save.SaveSecondaryStructure()
NLoops = 5
SaveBest()

-- This code needs to be cleaned up after
rc, err = xpcall(
    function() MainLoop(NLoops) end,
    ErrorHandler)

-- Check if everything was ok (optional)
if rc == false then
    -- you may want to do cleaning up stuff here
    print(err) -- this is the other place to print the error
else
    CleanUp()
    print("Done.")
end

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.