This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
steven6282's profile picture

loadstring() not working as expected

Started by steven6282, 30 May 2014 - 02:40 PM
steven6282 #1
Posted 30 May 2014 - 04:40 PM
I've got a program I'm writing that I have a file with a serialized table stored for an input handler type thing with lables and fields and such. I want to be able to specify custom handler functions for some things and so I figured, I'd just put the function name as a string in the table and then use loadstring() to be able to call the function. Well it's not working…. This is an example of what I'm doing:

Serialized Table in file:

{
	 fields = {
		  {
			   name="a_field",
			   x=1,
			   y=1,
			   default="Value",
			   handler="aHandler"
		  },
	 },
}

Now in the code I may do something like this, we'll say the unserialized table.fields is loaded into the variable fieldList for this example:

local fieldHandler=nil
if fieldList[1].handler then fieldHandler=loadstring(fieldList[1].handler) end
if fieldHandler then
	 fieldHandler(arg1,arg2)
end

function aHandler(arg1,arg2)
	 --Do stuff
end

The problem I'm having is loadstring(fieldList[1].handler) is always returning nil. Even when I know that fieldList[1].handler is returning "aHandler" (and I have another function in the program called aHandler). Obvious the code above is just example code to show the problem I'm having because my program is a lot longer and I didn't want to post all of it atm. But I can assure you I've tested and the arg I'm using in loadstring definitely has the anticipated and correct value in it that is the name of a function in my program that I intend to be used as a handler function.

All of the examples I can find of loadstring show it working just like that. What am I doing wrong here?
Edited on 30 May 2014 - 05:53 PM
CometWolf #2
Posted 30 May 2014 - 04:51 PM
loadstring runs in the global environment, so if your variables are local it won't have access to them

		 setfenv(fieldHandler,getfenv())
		 fieldHandler(arg1,arg2)

the more logical solution would be to stick the function in a table, and call it with it's name. Something like

fieldHandlers[fieldList[1].handler]()
SquidDev #3
Posted 30 May 2014 - 05:42 PM
This may not be what you want but is another way of doing it: you can use string.dump to 'serialise' a function, and then loadstring(dumped)() to call it. I may be mis understanding your 'handlers' but it would be interesting. :)/>
Lyqyd #4
Posted 30 May 2014 - 05:46 PM
Are you needing to save these files by serializing the table again and writing it to disk, or are these static data files that will only be read? If they are the latter, you can declare the entire function in them and it will load up fine.
steven6282 #5
Posted 30 May 2014 - 08:08 PM
loadstring runs in the global environment, so if your variables are local it won't have access to them

		 setfenv(fieldHandler,getfenv())
		 fieldHandler(arg1,arg2)

the more logical solution would be to stick the function in a table, and call it with it's name. Something like

fieldHandlers[fieldList[1].handler]()

The reason I'm not using your "more logical solution" is because if you store a function in a table and then try to serialize that table in order to store it in a file, you get an error that you cannot serialize a table. I will have to look into the setfenv. I saw it mentioned when I was searching for information on loadstring, but I thought it was for going the opposite way than what you said. I thought it was to give functions global environment, not local environment. Although maybe the functions in my program are global by default? Haven't really considered that.

This may not be what you want but is another way of doing it: you can use string.dump to 'serialise' a function, and then loadstring(dumped)() to call it. I may be mis understanding your 'handlers' but it would be interesting. :)/>

I'll have to look into this string.dump in order to serialize a function.

Are you needing to save these files by serializing the table again and writing it to disk, or are these static data files that will only be read? If they are the latter, you can declare the entire function in them and it will load up fine.

Yeah, I need to save the files. What I'm working on right now is actually a small off shoot of something I wanted in another program. I'm collecting a lot of user input for some things and wanted a nice way of handling it rather than just a series of questions and then read(). So instead I'm creating a full form handling system with labels and input fields that can be tabbed through, including default values and verification settings on submission. Part of this is having a program also written to allow me to create these forms more easily than simply typing up the entire table by hand every time I need a different form. So, the function would need to be serialized and saved.


Although, one thing I didn't try that might work that I didn't think of until a minute ago. I wonder if I can just store the entire function in a string and serialize that and use loadstring on it? Example:


{
	fields = {
		 {
				 name="a_field",
				 x=1,
				 y=1,
				 default="Value",
				 handler="function fieldHandler(arg1,arg2) print(arg1,arg2) end"
		 },
	},
}


I'll have to try that, of course, if I did it that way, and it worked, the handler function would not get stored in the field at that point, I'd have to have another table of functions so that multiple fields could reference the same function.
Edited on 30 May 2014 - 06:12 PM
apemanzilla #6
Posted 30 May 2014 - 08:10 PM
If you want to convert a function to a string just use string.dump. Using that, you can easily modify textutils.serialize and unserialize to fit your needs.
CometWolf #7
Posted 30 May 2014 - 08:39 PM
The reason I'm not using your "more logical solution" is because if you store a function in a table and then try to serialize that table in order to store it in a file, you get an error that you cannot serialize a table. I will have to look into the setfenv. I saw it mentioned when I was searching for information on loadstring, but I thought it was for going the opposite way than what you said. I thought it was to give functions global environment, not local environment. Although maybe the functions in my program are global by default? Haven't really considered that.
Well yes serializing a function does not work, but you would need to store the function in your script to somehow call it with loadstring anyways, no? So logically, you could just store it in a table of functions, under whatever name you want. setfenv can be used to change the environment of a given function to whatever you want, be it global or a newly created table. Personally though, i'd actually go with Lyqyd's approach. Im already doing something similar using loadfile in one of my programs.