Lua does not natively provide try-catch syntax to capture exception handling, but provides an pcall/xpcall
interface for executing Lua functions in protected mode.
Therefore, the capture mechanism of the try-catch block can be realized by encapsulating these two interfaces.
We can first look at the use of try-catch after encapsulation:
try
{
-- try 代码块
function ()
error("error message")
end,
-- catch 代码块
catch
{
-- 发生异常后,被执行
function (errors)
print(errors)
end
}
}
In the above code, an exception is considered in the try block, and an error message is thrown, captured in the catch, and the error message is output and displayed.
In addition to pcall/xpcall
encapsulation, it is used to capture exception information. It also uses the function call syntax feature of Lua. In the case of only one parameter passing, Lua can directly pass a table type and omit it.()
In fact, the whole behind try {...}
is just a table, which is passed to the try function as a parameter, and its specific implementation is as follows:
function try(block)
-- get the try function
local try = block[1]
assert(try)
-- get catch and finally functions
local funcs = block[2]
if funcs and block[3] then
table.join2(funcs, block[2])
end
-- try to call it
local ok, errors = pcall(try)
if not ok then
-- run the catch function
if funcs and funcs.catch then
funcs.catch(errors)
end
end
-- run the finally function
if funcs and funcs.finally then
funcs.finally(ok, errors)
end
-- ok?
if ok then
return errors
end
end
You can see that it is used pcall
to actually call the function in the try block, so that even if there is an exception inside the function, the program pcall
will not be interrupted, and false will be returned to indicate a failure.
And return the actual error message. If you want to customize the formatting error message, you can replace pcall by calling xpcall. Compared with pcall, this interface has an additional error handling function:
local ok, errors = xpcall(try, debug.traceback)
You can pass debug.traceback
in directly , use the default traceback processing interface, or customize a processing function:
-- traceback
function my_traceback(errors)
-- make results
local level = 2
while true do
-- get debug info
local info = debug.getinfo(level, "Sln")
-- end?
if not info or (info.name and info.name == "xpcall") then
break
end
-- function?
if info.what == "C" then
results = results .. string.format(" [C]: in function '%s'\n", info.name)
elseif info.name then
results = results .. string.format(" [%s:%d]: in function '%s'\n", info.short_src, info.currentline, info.name)
elseif info.what == "main" then
results = results .. string.format(" [%s:%d]: in main chunk\n", info.short_src, info.currentline)
break
else
results = results .. string.format(" [%s:%d]:\n", info.short_src, info.currentline)
end
-- next
level = level + 1
end
-- ok?
return results
end
-- 调用自定义traceback函数
local ok, errors = xpcall(try, my_traceback)
Going back to try-catch, through the above implementation, you will find that there is actually a finally processing inside. This function is for the try{}
code block, regardless of whether the execution is successful or not, it will be executed in the finally block
In other words, in fact, the above implementation, the complete support syntax is: try-catch-finally
mode, in which catch and finally are both optional, provided according to their actual needs
E.g:
try
{
-- try 代码块
function ()
error("error message")
end,
-- catch 代码块
catch
{
-- 发生异常后,被执行
function (errors)
print(errors)
end
},
-- finally 代码块
finally
{
-- 最后都会执行到这里
function (ok, errors)
-- 如果try{}中存在异常,ok为true,errors为错误信息,否则为false,errors为try中的返回值
end
}
}
Or only finally block:
try
{
-- try 代码块
function ()
return "info"
end,
-- finally 代码块
finally
{
-- 由于此try代码没发生异常,因此ok为true,errors为返回值: "info"
function (ok, errors)
end
}
}
Processing can obtain the normal return value in try in finally, in fact, in the case of only try, the return value can also be obtained:
-- 如果没发生异常,result 为返回值:"xxxx",否则为nil
local result = try
{
function ()
return "xxxx"
end
}
As you can see, this pcall-based try-catch-finally
exception capture package is still very flexible in use, and its implementation is quite simple
This also fully shows that Lua is a very powerful, flexible, and very streamlined language.
In the development of custom scripts and plug-ins of xmake , it is also completely based on this exception capture mechanism
This makes the development of extended scripts very concise and readable, eliminating the need for tedious if err ~= nil then
return value judgments. When an error occurs, xmake will directly throw an exception to interrupt, and then highlight the detailed error message.
E.g:
target("test")
set_kind("binary")
add_files("src/*.c")
-- 在编译完ios程序后,对目标程序进行ldid签名
after_build(function (target))
os.run("ldid -S %s", target:targetfile())
end
Only one line os.run
is needed, and there is no need to return a value to judge whether the operation is successful, because after the operation fails, xmake will automatically throw an exception, interrupt the program and prompt an error
If you want to continue running without directly interrupting xmake after the operation fails, you can add a try yourself:
target("test")
set_kind("binary")
add_files("src/*.c")
after_build(function (target))
try
{
function ()
os.run("ldid -S %s", target:targetfile())
end
}
end
If you want to capture the error message, you can add another catch:
target("test")
set_kind("binary")
add_files("src/*.c")
after_build(function (target))
try
{
function ()
os.run("ldid -S %s", target:targetfile())
end,
catch
{
function (errors)
print(errors)
end
}
}
end
However, under normal circumstances, writing custom scripts in xmake does not need to manually add try-catch, directly call various apis, let xmake's default handler take over after an error, and just interrupt it directly. .
Finally try-catch-finally
, the relevant complete source code of the implementation is attached :
Personal homepage: TBOOX open source project
Original source: http://tboox.org/cn/2016/12/14/try-catch/