Whenever an error occurs in a Lua script, I'd like it to write the values of all local and global variables to the screen/optionally to a file - in addition to the usual stack trace.
How could I get this to be the default behavior for all errors?
If you're using the standard Lua interpreter, replace debug.traceback with your own function. If you're embedding Lua in your program, use your traceback function in lua_pcall.
The StackTracePlus module does what you want, displaying local variables at each level of the stack trace. It doesn't dump the entire globals table, but that is probably overkill.
To install it with LuaRocks, use
luarocks install stacktraceplus
Then in your code, do:
local STP = require "StackTracePlus"
debug.traceback = STP.stacktrace
In Lua 5.1 this will automatically convert all stack traces; for Lua 5.2 code you need to wrap your code with an xpcall as suggested in other answers.
A more proper solution would be to use xpcall around your whole code.
local function myerrhandler ( errobj )
print(debug.traceback())
for k,v in pairs(_G) do print("GLOBAL:" , k,v) end
return false
end
xpcall( function ()
--Your code here
end , myerrhandler )
Your error handler may be overwritten. If you're calling Lua from C, to always print the stack you can hook in to the luaG_errormsg function.
In lua, write :
local _HandlingError = 0
function _ErrorHandler ( errobj )
if( _HandlingError == 0 ) then
_HandlingError = 1
local errStr = tostring(errobj) or ""
if( type(errobj)=='table' ) then
errStr = "Table: {" .. table.concat(errobj, ',') .. "}"
end
print("Error: \"" .. errStr .. "\"")
--for k,v in pairs(_G) do print("GLOBAL:" , k,v) end
if( type(errobj)=='thread' ) then
print(debug.traceback(errobj))
else
print(debug.traceback('',2))
end
_HandlingError = 0
end
return false
end
Then in ldebug.c, add to luaG_errormsg after if(L->errfunc != 0)
else
{
lua_getfield(L, LUA_GLOBALSINDEX, "_ErrorHandler");
if (!lua_isfunction(L, -1)) {
lua_pop(L, 1);
}
else {
lua_pushvalue(L, 1);
lua_call(L, 2, 1);
lua_pop(L, 1);
}
}
Related
I am using this script as a template to rename a file in VLC: https://github.com/surrim/vlc-delete/
The Script works as intended.
My code looks like this:
function descriptor()
return {
title = "VLC Rename";
version = "0.1";
author = "I";
shortdesc = "Rename current file";
description = [[
<h1>vlc-rename</h1>"
When you're playing a file, use VLC Rename
to rename the current file]];
}
end
function removeItem()
local id = vlc.playlist.current()
vlc.playlist.delete(id)
vlc.playlist.gotoitem(id + 1)
vlc.deactivate()
end
function activate()
local item = vlc.input.item()
local uri = item:uri()
oldFile = vlc.strings.decode_uri(string.gsub(uri, "^file:///", ""))
d = vlc.dialog( "Rename Dialog" )
d:add_label("Filename")
w = d:add_text_input(oldFile, 1, 5,200 ,30)
d:add_button("OK", click_ok)
d:show()
end
function click_ok()
local newFile = w:get_text()
vlc.msg.info("[vlc-rename] renaming: " .. oldFile .. " with " .. newFile)
if newFile ~= oldFile then
removeItem()
retval, err = os.rename(oldFile,newFile)
vlc.msg.info("[vlc-rename] end rename")
if (retval == nil) then
vlc.msg.err("[vlc-rename] fail: " .. err)
end
end
d:delete()
vlc.deactivate()
end
function deactivate()
vlc.deactivate()
end
function close()
deactivate()
end
function meta_changed()
end
This code outputs an error from the os.rename() function:
lua error: [vlc-rename] fail: [my filename] Permission denied
Regardless of elevation level.
I am using windows 10 64bit and VLC 3.03.
Since this is my first lua script I welcome any input.
I might be wrong, but maybe the file you are trying to rename is already opened elsewhere or by VLC (you said you want to rename the "current file").
I would like to for-loop through multiple tables in parallel in Lua. I could just do:
for i in range(#table1)
pprint(table1[i])
pprint(table2[i])
end
But I'd rather something like python's zip:
for elem1, elem2 in zip(table1, table2):
pprint(elem1)
pprint(elem2)
end
Is there such a thing in standard Lua (or at least in whatever comes packaged with torch?).
If you want something in Lua that's similar to some Python function, you should look at Penlight first. For this specific case there is the seq.zip function. It seems that Penlight is installed together with Torch, but you can also get it via LuaRocks (which again is bundled with at least one Torch distribution).
Anyway, the seq.zip function in Penlight only supports zipping two sequences. Here is a version that should behave more like Python's zip, i.e. allowing more (or less) than two sequences:
local zip
do
local unpack = table.unpack or unpack
local function zip_select( i, var1, ... )
if var1 then
return var1, select( i, var1, ... )
end
end
function zip( ... )
local iterators = { n=select( '#', ... ), ... }
for i = 1, iterators.n do
assert( type( iterators[i] ) == "table",
"you have to wrap the iterators in a table" )
if type( iterators[i][1] ) ~= "number" then
table.insert( iterators[i], 1, -1 )
end
end
return function()
local results = {}
for i = 1, iterators.n do
local it = iterators[i]
it[4], results[i] = zip_select( it[1], it[2]( it[3], it[4] ) )
if it[4] == nil then return nil end
end
return unpack( results, 1, iterators.n )
end
end
end
-- example code (assumes that this file is called "zip.lua"):
local t1 = { 2, 4, 6, 8, 10, 12, 14 }
local t2 = { "a", "b", "c", "d", "e", "f" }
for a, b, c in zip( {ipairs( t1 )}, {ipairs( t2 )}, {io.lines"zip.lua"} ) do
print( a, b, c )
end
--------------------------------------------------------------------------------
-- Python-like zip() iterator
--------------------------------------------------------------------------------
function zip(...)
local arrays, ans = {...}, {}
local index = 0
return
function()
index = index + 1
for i,t in ipairs(arrays) do
if type(t) == 'function' then ans[i] = t() else ans[i] = t[index] end
if ans[i] == nil then return end
end
return unpack(ans)
end
end
--------------------------------------------------------------------------------
-- Example use:
--------------------------------------------------------------------------------
a = {'a','b','c','d'}
b = {3,2,1}
c = {7,8,9,10,11}
for a,b,c,line in zip(a,b,c,io.lines(arg[0])) do
print(a,b,c,line)
end
print '\n--- Done! ---'
Made a version with variable number of lists based on the other responses and added coroutines, it's not the fastest but I think it's very readable. I'm gonna use it but maybe someone else also finds it usefull.
Also this was made for use with neovim's LuaJit.
local zipgen = function(args)
-- find smallest iterator's size
local min = #args[1]
for i = 2, #args, 1 do
min = #args[i] < min and #args[i] or min
end
-- create list with 'i'th element from all iterators
for i=1, min do
local ans = {}
for j=1, #args do
-- get 'j'th iterator's 'i'th element
ans[j] = args[j][i]
end
-- return list of 'i'th elements
coroutine.yield(unpack(ans))
end
end
-- python like zip iterator
zip = function(...)
local args = {...}
-- return a function that resumes the coroutine when called
return coroutine.wrap(function() zipgen(args) end)
end
Well, I'm trying to get the value of packets to be read by the recv with ioctlsocket(FIONREAD), But the function is not returning any value.
Look:
IOCtlSocket = Win32API.new('ws2_32', 'ioctlsocket', 'llp', 'i')
ret_val = -1
result = IOCtlSocket.call #descriptor, 0x4004667f, ret_val
if ret_val > 0
print "Há pacotes a receber"
end
ret_val does not change, no matter what value I place on it .
Anyone have tip's or a solution?
The last parameter is a pointer. Pointers are implemented as strings in Win32API. So you need to do the following:
IOCtlSocket = Win32API.new('ws2_32', 'ioctlsocket', 'llp', 'i')
ret_val_buf = " " * 4 #Prepare a 4-byte buffer
result = IOCtlSocket.call #descriptor, 0x4004667f, ret_val_buf
ret_val = ret_val_buf.unpack("L")
UPDATE: Thank you for the help. Writing a demo that you could run did help me solve the issue but not in the way that I expected. I think this is a compiler optimization rather than a bug. When the code inside if ( z == true ) is commented out, the conditional statement is skipped entirely and the control returns to if (x). If I put actual code in, we hit the conditional statement when appropriate.
ORIGINAL QUESTION:
I have a std::string and I am iterating through it to determine whether it contains certain characters. If any of these characters are found, I want to exit the for loop and proceed with the next conditional statement. Here is an example:
#include <iostream>
using namespace std;
int main()
{
bool x = true;
bool y = true;
bool z = true;
std::string str;
cout << "Enter string: ";
cin >> str;
if ( x )
{
if ( y )
{
std::string::iterator i;
for ( i = str.begin(); i != str.end(); i++ )
{
cout << "Enter for" << endl;
if ( *i == 'a' || *i == 'b' || *i == 'c' )
{
z = false;
cout << "Exit for" << endl;
break;
}
}
if ( z == true )
{
//cout << "z == true" << endl;
}
}
}
}
The issue is that the program never hits if (z == true). When it breaks out of the loop, control returns to the first conditional statement ( if (x) ).
I tried removing the break and instead setting i = ( str.end() - 1 ). This resulted in the same behavior - it goes back to the for, determines that it's at the last character of the string, and then returns control to the first conditional statement again, skipping the if ( z == true ) as intended.
What am I doing wrong here?
"When it breaks out of the loop, control returns to the first conditional statement ( if (something) )."
When it breaks out of the loop, it has just set b to false, so the if (b == true) code won't run. What am I misunderstanding?
Edit: I think you have oversimplified your code. Clearly that's not the code you're actually running because str never has a value. You need to post an example that we can actually run (but once you have created such an example, you'll probably find the error for yourself. :-) )
I want to call the lua function debug.traceback() in c++ to get the trackback information in c++, so I added the function in c++ like this:
int luaErrorHandler(lua_State *m_state) {
if (!lua_isstring(m_state, 1))
return 1;
lua_getfield(m_state, LUA_GLOBALSINDEX, "debug");
if (!lua_istable(m_state, -1)) {
lua_pop(m_state, 1);
return 1;
}
lua_getfield(m_state, -1, "traceback");
if (!lua_isfunction(m_state, -1)) {
lua_pop(m_state, 2);
return 1;
}
lua_pushvalue(m_state, 1); /* pass error message */
lua_pushinteger(m_state, 2);
lua_call(m_state, 2, 1); /* call debug.traceback */
return 1;
}
and then I use lua_pushcfunction(L, luaErrorHandler) to push it into the stack , and use lua_insert() to move the function to the bottom, then lua_pcall(L, nArgs, 1, errIndex) to call the lua function. The stack should be like this at this time: ..luaErrorHandler , func, arg1 , arg2....
The problem is when I called the function, somehow the function changed the stack inside itself( I think..) , so I get the error "attempt to call a number value", and it goes correctly when I was not using the error handler function.
Is there any advise on how to use debug.traceback() correctly? Or how can I debug this problem cause I totally have no idea how it goes wrong.
C functions registered with a lua_State should return the number of return values they are pushing to the stack. Your function is not returning anything (to the Lua stack), but is telling Lua that it pushed a single value. As a result Lua will pop a value after your function finished and pass it to the caller. Probably the value that is popped is what you intended to call, but instead ended up calling the next thing on the stack - a number.
Try replacing the appropriate return 1 statements with return 0 and think carefully about the stack contents before each return to make sure you don't pop something.