Log In  


I know pcall isn't part of pico 8, which means you can't do regular Lua exception handling. I noticed though that coroutines swallow errors, and can return the error message. So I leveraged that to create a C#/Java style try catch finally function.

function try(t,c,f)
	local co=cocreate(t)
	local s,m=true
	while s and costatus(co)!="dead" do
		s,m=coresume(co)
		if not s then
			c(m)
		end
	end
	if f then
		f()
	end
end

cls()

try(function()
	print("hi ")
	--print("hi " .. nil)
end,
function(e)
	print("an error occurred:\n"..e)
end,
function()
	print("finally")
end)

To use just use it like above. The finally function is optional, so most people will probably use it like this:

try(function()
	-- code
end,
function(e)
	-- exception handler
end)

Enjoy, and if you like it feel free to kick a buck to my Patreon.

9


This is exactly what I am looking for, Thanks! I still can't seem to understand how it works...


1

Are you confused about how to use it, or what trick it uses?

The trick it uses is that when an error happens in a coroutine, the coroutine silently fails and the second return value from 'coresume' contains the error message. So all this function does is run the code you give it as a coroutine, and keeps running it (because coroutines automatically yield sometimes in pico 8), and checks to see if an error happens.

When an error does happen, the second function gets called with the message. And regardless of whether or not an error happens the third function is called, if you pass it on.


1

Thanks, I was looking for this!

The string can too big to fit on the screen so I added the following code to prevent that:

try(function()
 --code
end,
function(e)
    local n,ee=0,""
    while (n<#e) do
        ee=ee..sub(e,n,n+31).."\n"
        n+=32
    end
    print("an error occurred:\n"..ee)
end,
function()
    print("finally")
end)

Thanks, that's helpful! :)


May I publish some or all of your code, or derivatives, in https://github.com/sparr/pico8lib ?


Sure, the stuff that's publicly available (doesn't require being a patron on Patreon), with credit and a link to my Patreon somewhere


Thanks for this @Eniko, it's working really nicely!

I just wanted to add my varags version, which also returns the result to the caller.
This allows your try function to take any number of parameters, and uses the coresume(co, ...) to pass them along. I've also removed the need for defining a catch, if c is nil, it'll just skip it.
Lastly after all the work is completed it returns the success flag, as well as the result of the try function (m is either an error message, or the return value of the try function from coresume).

function try(t,c,f, ...) 
    local co=cocreate(t)
    local s, m=true
    while s and costatus(co)!="dead" do
        s, m = coresume(co, ...)
        -- ignore catching if c is nil
        if not s and c then 
            c(m)
        end
    end
    if f then
        f()
    end

    -- return success and message/return value
    return s, m
end

Also wanted to say thanks: thanks!

Just in case it's useful to anyone, I cut this down to the bare minimum for what wanted it for like so:

if not coresume(cocreate(your_function),your_args) then
 -- fail code here
end

or perhaps (obv not v java-like):

function try(f,...)
 return not coresume(cocreate(f),...)
end

if try(your_function,your_args) then
 -- fail code here
end

Am I correct in concluding that the while loop is only necessary because t might yield() like a normal coroutine that needs to be resumed multiple times, and the loop will only ever run once otherwise?


@sparr, co-routines that take several frames to run also sometimes yield automatically. When that happens it's simpler to watch the cpu usage in the co-routine and yield yourself programmatically when near the arrival of the next frame, if your _draw and _update use the data produced by the ongoing co-routine. This way you can ensure a coherent (if incomplete) variables state and avoid nasty untraceable bugs.



[Please log in to post a comment]