24 posts
Posted 17 May 2017 - 09:42 PM
So Lua 5.1 normally has the newproxy function that can create userdata but ComputerCraft doesn't have it.
The reason I need this is that in the case the script is terminated or it errors or something I want to ensure I clean up everything correctly. The reason is that for my Logger class it only opens the file once and stores the handle. The problem though is since tables don't fire the __gc metamethod in 5.1 I cannot guarantee this file is closed.
My original solution was to return an object created from newproxy and the __gc metamethod would be fired in the case that went out of scope.
I know I could use os.pullEventRaw and handle it when the script is terminated but then that prevents me from using rednet.receive which is a wrapper function I really like. Also it doesn't entirely handle the case when the logger object goes out of scope (although I can ensure that won't happen I guess).
Is there another way to do what I need?
Edited on 17 May 2017 - 07:45 PM
2427 posts
Location
UK
Posted 18 May 2017 - 10:02 AM
You could overwrite os.pullEventRaw so that it listens for terminates and calls a cleanup function when it hears one. You will be able to continue using rednet.receive in your main code then.
24 posts
Posted 19 May 2017 - 01:00 AM
Don't all scripts share the same global environment though? If I overwrite it in one script wouldn't it affect the rest?
I might just end up not using rednet.receive if last comes to last but I'm curious to if there is a way to do this more nicely. I don't really understand why `newproxy` was removed since it'd be perfect in this scenario.
I was thinking I might be able to set the metatable of the file object returned to have __gc but io.open and fs.open return tables so that's also a no go.
Edit: Nope never mind they don't share the same env. although they fallback to the same table with the built-ins. If I call rednet.receive after writing over os.pullEventRaw, wouldn't it change all script in that case. Although this could be a good thing, I can have it take an optional "onTerminate" function.
Edited on 18 May 2017 - 11:16 PM
7083 posts
Location
Tasmania (AU)
Posted 19 May 2017 - 01:29 AM
If you have control over your script's termination, then it'd just be a matter of having your rigged os.pullEventRaw function restore the original function pointer at the same time it's ensuring the file handle is closed. This won't help cases where another script is ran simultaneously (eg in another
multishell tab), though.
Personally I'd just call fileHandle.flush() immediately after every fileHandle.writeLine() call and forget about trying to close the handles upon an error. Ideally you'll be using your logged data to clean up all the errors anyways (and then disabling the logging system unless a special debug flag is set or something), so it should be a moot point by the time you release.
Edited on 18 May 2017 - 11:31 PM
24 posts
Posted 19 May 2017 - 01:49 AM
Yeah this specific computer is running multiple scripts at the same time via multishell tabs.
Anyways, the problem with not closing it is that the file will stay open until the computer is rebooted/shutdown. Although yeah I mean my solution currently is to wrap os.pullEvent and os.pullEventRaw to accept a callback function that is called if the signal was terminate.
Seems like a clean way to do it, no?
Edited on 18 May 2017 - 11:53 PM
7083 posts
Location
Tasmania (AU)
Posted 19 May 2017 - 04:54 AM
So long as you're using
multishell.getCurrent() to ensure the termination event was targeted at the same tab which defined the wrapper functions, sure.
2427 posts
Location
UK
Posted 19 May 2017 - 09:28 AM
Question to other pros, can function overrides be local?
7083 posts
Location
Tasmania (AU)
Posted 19 May 2017 - 03:16 PM
Depends on the function.
Remember that functions themselves are not variables, and only variables can be localised. If you've got a function pointer assigned to a variable, eg "print", then sure, you can make a new local variable called "print" and assign a new function pointer to that. Other scripts won't see it.
But if you're talking about a function pointer assigned to, say, a table key - such as the "pullEvent" key in the "os" table - then you're in trouble, as table keys can't be localised. If you override that key then every script which refers to that same "os" table is going to see your change, even when your script ends (unless you change it back beforehand). Attempting to localise a key will crash you out during the compilation stage - it's recognised as invalid syntax.
2427 posts
Location
UK
Posted 19 May 2017 - 03:32 PM
But if you're talking about a function pointer assigned to, say, a table key - such as the "pullEvent" key in the "os" table - then you're in trouble, as table keys can't be localised. If you override that key then every script which refers to that same "os" table is going to see your change, even when your script ends (unless you change it back beforehand). Attempting to localise a key will crash you out during the compilation stage - it's recognised as invalid syntax.
So if I wanted to emulate the desired behaviour I could make a local copy of the OS table, have most of it forward to the original values but change pullEven to my needed function.
24 posts
Posted 19 May 2017 - 08:30 PM
You could probably just wrap the entire OS table if you want, no need to copy it. You'd put any of your custom functions in that table but then have indexing it fallback to the real OS table (via __index metafield).
2427 posts
Location
UK
Posted 19 May 2017 - 09:58 PM
I keep forgetting about metatables
7083 posts
Location
Tasmania (AU)
Posted 19 May 2017 - 11:46 PM
Of course, if you want other pre-defined functions (eg rednet.receive) to use your custom keys, then localising your wrapper table isn't going to work out so well. You'd have to redefine those too. By which point you might as well just stop using table keys altogether.
Edited on 19 May 2017 - 09:49 PM
6 posts
Posted 20 May 2017 - 04:46 AM
You could write a function like this:
local logFilePath = "/log"
function log(data)
--Temporarily disable termination
local temp = os.pullEvent
os.pullEvent = os.pullEventRaw
--Log the data
local logFile = fs.open(logFilePath,"a")
logFile.writeLine(data)
logFile.close()
--re-enable termination
os.pullEvent = temp
end
Then you can still terminate your program, just not when it is writing something to a file. You would not have to worry about files being left open with this kind of approach.
24 posts
Posted 20 May 2017 - 06:37 AM
The problem with that is I don't want to keep opening my file just to write to it. I'm storing the handle until the program terminates or they close it themselves. (ideally, until it goes out of scope but that's not possible on CC since __gc doesn't fire on tables)
1426 posts
Location
Does anyone put something serious here?
Posted 20 May 2017 - 08:33 AM
Dan recently changed CC so that the file should be closed when it is GCed, which should fix this problem (unless you need to do other stuff on the
__gc function too). It hasn't been released yet but you can
grab a build from here or wait a little :)/>.
Sadly, the Lua implementation CC uses doesn't support
__gc. As it is built on top of Java, you don't have as much control over the garbage collector.
24 posts
Posted 20 May 2017 - 10:52 AM
Oh… well that is incredibly useful and convenient :o/>
Edited on 20 May 2017 - 08:52 AM