Old 05-26-2017, 06:30 PM   #1
Airal
Banned
 
Join Date: Nov 2015
Posts: 406
Default Lua terminate script immediately

I have the need to terminate a script immediately whens something goes wrong yet none of the methods work:

reaper.atexit(exit) with exit calling gfx.exit()(some somewhere on this site, I think exit is just a callback when the script terminates normally, so this is useless).

os.exit, shell.exit: neither os nor shell are defined.
Airal is offline   Reply With Quote
Old 05-26-2017, 07:16 PM   #2
kawa_
Human being with feelings
 
kawa_'s Avatar
 
Join Date: Mar 2016
Posts: 117
Default

Hi!

that may good working with use "label" of lua. ( Goto Statement )
document : lua-users.org ( http://lua-users.org/wiki/GotoStatement )

Code:
local count = 0;

while( true)
do
    if ( count > 20)
    then 
        goto exit; -- when timing of want to finish script.
    end
    reaper.ShowConsoleMsg(tostring(count) .. "\n");
    count = count +1;
end


-- label at last line
::exit::
thanks!
__________________
web | kawaScripts | donate | twitter |
kawa_ is offline   Reply With Quote
Old 05-26-2017, 07:19 PM   #3
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,968
Default

Another way is to use a return statement preventing further instructions in the current function (or global) from executing.

Code:
function someFunctionThatMayFail()
  if success then
    return true
  else
    return false
end

if not success then
  reaper.ShowConsoleMsg("Error 234: Violin is badly tuned.")
  return
end

if not someFunctionThatMayFail()
  reaper.ShowConsoleMsg("Error 432: Tempo has gone offline.")
  return
end
cfillion is offline   Reply With Quote
Old 05-26-2017, 07:30 PM   #4
kawa_
Human being with feelings
 
kawa_'s Avatar
 
Join Date: Mar 2016
Posts: 117
Default

"return" ! nice work it.
__________________
web | kawaScripts | donate | twitter |
kawa_ is offline   Reply With Quote
Old 05-26-2017, 09:29 PM   #5
Airal
Banned
 
Join Date: Nov 2015
Posts: 406
Default

None of these are solutions. They do not work in general. I need a direct way to exit at any point in code.

I have a global error handler that does nifty stuff and when it is called to do it's thing, it needs to exit, else reaper continues executing code and runs in to a problem and then throws an error in a modal dialog box(which is redundant because I already showed more error info than reaper's error handler). I simply need my error handler to have a way to terminate the script directly.

1. labels don't work because you can't escape to different scopes. Since my error function is in a function I can't use goto in it to call escape. Proof: That was one of the first things I tried and lua say that the label did not exist.(though it did, just outside the scope of the function).

2. return don't work because it would require every function that is called to return if any function it called had an error. This would be extremely noisy in the source, would obfuscate function returns, and would be very error prone.

Both have the same problem, I can't use it them in functions. Both simply exit the current scope. I need to exit the global scope(terminate the script).
Airal is offline   Reply With Quote
Old 05-26-2017, 11:19 PM   #6
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,968
Default

Quote:
Originally Posted by Airal View Post
2. return don't work because it would require every function that is called to return if any function it called had an error. This would be extremely noisy in the source, would obfuscate function returns, and would be very error prone.
This is pretty much how well-written C code works.

Yet another way to (not cleanly) abort the execution of a Lua program is the assert function. The diagnostic message it displays can be silenced using Lua's protected execution mode.

Last edited by cfillion; 05-27-2017 at 01:46 AM.
cfillion is offline   Reply With Quote
Old 05-27-2017, 01:29 AM   #7
Airal
Banned
 
Join Date: Nov 2015
Posts: 406
Default

Quote:
Originally Posted by cfillion View Post
This is pretty much how well-written C code works.

Yet another way to (not cleanly) abort the execution of a Lua program is the assert function. The diagnostic message it displays can be silenced using Lua's protected execution mode.
Fortunately I don't program in C. I tried assert and it does work but still an error(the assert error). I tried wrapping that in a pcall and it does nothing(ignores the assert completely)(I assume that is what you meant, more or less?).

So, the assert is better than nothing but I'd still like to get rid of the error display from reaper and use my own.
Airal is offline   Reply With Quote
Old 05-27-2017, 01:48 AM   #8
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,968
Default

Example using the error function to pass around a more useful message than assert does:

Code:
local SCRIPT_NAME = ({reaper.get_action_context()})[2]:match('([^/\\_]+).lua$')
local MB_OK = 0

local ok, err = pcall(function()
  reaper.ShowConsoleMsg("abc\n")
  error({message="Lights are out in the quantum tunnel"})
  reaper.ShowConsoleMsg("def\n") -- this instruction will not run
end)

if not ok then
  local body = string.format('The following error occurred: "%s".', err.message)
  reaper.MB(body, SCRIPT_NAME, MB_OK)
end

Last edited by cfillion; 05-27-2017 at 02:03 AM.
cfillion is offline   Reply With Quote
Old 05-27-2017, 10:43 AM   #9
Airal
Banned
 
Join Date: Nov 2015
Posts: 406
Default

This doesn't terminate the script and I still get reapers original error box about the original error in the program. So it actually makes it worse because I end up with two error boxes.

I simply want to terminate a script immediately with no error boxes popping up(reapers or otherwise(as I can add my own))... ;/ It shouldn't be that hard ;/
Airal is offline   Reply With Quote
Old 05-27-2017, 12:12 PM   #10
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,968
Default

Have you tried with the same code? Here abc is displayed in the console and the custom error is displayed in the message box. The normal Lua error message and def aren't shown.

cfillion is offline   Reply With Quote
Old 05-27-2017, 01:07 PM   #11
Airal
Banned
 
Join Date: Nov 2015
Posts: 406
Default

Quote:
Originally Posted by cfillion View Post
Have you tried with the same code? Here abc is displayed in the console and the custom error is displayed in the message box. The normal Lua error message and def aren't shown.

Yes, I copied and pasted your code.

I think the problem is, is that your code doesn't actually throw an error anywhere! So it just passes through without triggering the internal error handler of reaper.

Try calling a reaper command with an invalid argument that would normally cause reaper to pop up a script error.

Put it as the last command to be called in your script. It should be called(cause your script doesn't terminate after you post your error box. Try using os.exit(), assert, etc(which none work as desired, the only point of the original post)).

Then you should see the problem.
Airal is offline   Reply With Quote
Old 05-27-2017, 01:15 PM   #12
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,968
Default

You can do something like this to restore the default behavior of Lua errors:

Code:
local SCRIPT_NAME = ({reaper.get_action_context()})[2]:match('([^/\\_]+).lua$')
local MB_OK = 0

function main()
  -- all of the script code here

  reaper.hello_world()
  --error({message="custom error"})

  -- this line won't run if there has been an error above
end

local ok, err = pcall(main)

-- anything here after pcall will continue running after an error

if not ok then
  if type(err) == 'table' and err.message then
    -- handle a custom error
    local body = string.format('The following error occurred: "%s".', err.message)
    reaper.MB(body, SCRIPT_NAME, MB_OK)
  else
     -- handle a standard Lua error
    error(err, 2)
  end
end

Last edited by cfillion; 05-27-2017 at 01:27 PM.
cfillion is offline   Reply With Quote
Old 05-27-2017, 01:30 PM   #13
heda
Human being with feelings
 
heda's Avatar
 
Join Date: Jun 2012
Location: Spain
Posts: 7,268
Default

Airal,
calling gfx.quit() should work to terminate the script at any point
heda is offline   Reply With Quote
Old 05-27-2017, 02:47 PM   #14
Airal
Banned
 
Join Date: Nov 2015
Posts: 406
Default

Heda: It doesn't, that was another thing I tried.

if I do

gfx.quit();
assert(nil, "error");

I get the assert.

Possibly it requires actually creating some graphical window for it to work?


If I try

gfx.init("", 200, 200, 1)
gfx.quit();

at the top of my program, it still runs so gfx.quit() is not ending the script.



e.g.,

gfx.init("Test", 200, 200);
reaper.ShowConsoleMsg("asdfasdf");
gfx.quit();
reaper.ShowConsoleMsg("1234324");


gfx.quit simply closes the gfx window, it does not terminate the script.

Any other ideas?
Airal is offline   Reply With Quote
Old 05-27-2017, 03:39 PM   #15
heda
Human being with feelings
 
heda's Avatar
 
Join Date: Jun 2012
Location: Spain
Posts: 7,268
Default

ok I've read more carefully in further replies now... sorry.

I think solutions given by kawa and cfillion are good
Maybe you are overcomplicating things.
Rethink your structure of functions. if you need to exit, you can use a flag variable, and in the next loop or whatever you do, just don't loop if the flag is set. Then the flow will continue after the loop and reach the end and exit naturally. I think I can't help more if no example code or specific function is given.
heda is offline   Reply With Quote
Old 05-27-2017, 03:53 PM   #16
Airal
Banned
 
Join Date: Nov 2015
Posts: 406
Default

Quote:
Originally Posted by heda View Post
ok I've read more carefully in further replies now... sorry.

I think solutions given by kawa and cfillion are good
Maybe you are overcomplicating things.
Rethink your structure of functions. if you need to exit, you can use a flag variable, and in the next loop or whatever you do, just don't loop if the flag is set. Then the flow will continue after the loop and reach the end and exit naturally. I think I can't help more if no example code or specific function is given.
No, you don't understand. 1st. You can't say someone elses solution is good when it doesn't work(of course, you can but then that makes you wrong). 2. You can't tell me I should rewrite my entire program and make it more bloated because of missing functionality that almost all languages have.

It's much simpler to say "Ok, we need a viable method to terminate the script(or in generate, our current ideas about things are inadequate and we need to improve them) and then add that functionality instead of needless and inane work arounds. Else, with that kinda logic, we might as well go back to using the abacus.
Airal is offline   Reply With Quote
Old 05-27-2017, 03:57 PM   #17
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

You could execute your script as a coroutine so you can yield anywhere and not do anything if yielded...
Code:
local function DBG(str)
  reaper.ShowConsoleMsg(tostring(str) .. "\n")
end


local function isError(case)
   if case then coroutine.yield(-1) end
   DBG("Hasn't stopped")
end


local function myCoolScript()
   DBG("This is a very cool script")
   isError(true)
   DBG("Continuing...")
end


local function main()
  local co = coroutine.create(
    function ()
      myCoolScript()
      DBG("Continuing from main coroutine")
    end
  )
  coroutine.resume(co)
  if coroutine.status(co) ~= "suspended" then reaper.defer(main) end
end

main()
snooks is offline   Reply With Quote
Old 05-27-2017, 04:23 PM   #18
Airal
Banned
 
Join Date: Nov 2015
Posts: 406
Default

Quote:
Originally Posted by snooks View Post
You could execute your script as a coroutine so you can yield anywhere and not do anything if yielded...
Code:
local function DBG(str)
  reaper.ShowConsoleMsg(tostring(str) .. "\n")
end


local function isError(case)
   if case then coroutine.yield(-1) end
   DBG("Hasn't stopped")
end


local function myCoolScript()
   DBG("This is a very cool script")
   isError(true)
   DBG("Continuing...")
end


local function main()
  local co = coroutine.create(
    function ()
      myCoolScript()
      DBG("Continuing from main coroutine")
    end
  )
  coroutine.resume(co)
  if coroutine.status(co) ~= "suspended" then reaper.defer(main) end
end

main()

Thanks! You win the prize! First working solution and somewhat out of the box thinking! I'd give you a gold star if I had one! While I have to integrate it in to my code, and requiring a wrapper around main is a slight hindrance(since I'm trying to do this in library code).

Unfortunately, I'm gonna have to take that gold star away and give a silver star instead! The code doesn't work across modules so I can't wrap up all the overhead in a library solution because the library can't call the main function to start the program.


I was able to hack this though, So I'll give you a gold star with one point removed!

require "library"

main = function()
...
end

require "libraryhack"


Here libraryhack effectively has your code in in it and it can see main while library can't(for some reason, even though main is global).

But now my error handling code doesn't display the call stack correctly for some reason ;/ This might be fixable but I'll have to investigate some(seems that coroutines screw up debug.getinfo). All this trouble simply because we don't have a single function that actually can terminate a script ;/ amazing... It's like preparing a huge and expensive feat and forgetting the forks.

Last edited by Airal; 05-27-2017 at 04:28 PM.
Airal is offline   Reply With Quote
Old 05-27-2017, 04:57 PM   #19
Airal
Banned
 
Join Date: Nov 2015
Posts: 406
Default

The solution seems to be to wrap the main entry point with xpcall. This creates a custom error that can handle any of the errors and prevents reapers error(since xpcall intercepts the error).

It still requires me to use the "libraryhack" to access main which is an undesirable due requiring a main and an extra require.

Last edited by Airal; 05-27-2017 at 06:51 PM.
Airal is offline   Reply With Quote
Old 08-23-2022, 12:05 AM   #20
tnkr
Human being with feelings
 
Join Date: Apr 2022
Posts: 1
Default

To those of you after seeing this thread in the future.
You are all aware of the addition in REAPER 6.29 of the ReaScriptError() function's ! prefix, which allows you to stop the script at any time. (It is also described in the current API documentation)
tnkr is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 12:41 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.