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

os.queueEvent won't return tables

Started by Pharap, 30 October 2012 - 09:49 AM
Pharap #1
Posted 30 October 2012 - 10:49 AM
I've been trying to handle my own events as part of a lua learning rampage, and I've found that when I tried to add a table as an argument to return, it wouldn't accept tables.

Am I right in thinking that this is intentional or is this an error with CC that needs to be fixed?

Personally I'm hoping it's not intentional, because that would make the os.queueEvent system incredibly lacking in my opinion.
KaoS #2
Posted 30 October 2012 - 10:52 AM
could you give us an example code? are you queuing a table event and then trying to pull that event?
Pharap #3
Posted 30 October 2012 - 11:05 AM
Right here:



os.queueEvent("abc", 2, "sometext") --using numbers and strings to test

local tb = {} --a new table
print(tostring(tb)) --returns a table code
print(tb) --returns the same code
os.queueEvent("tab",tb) --queue the table with an event

while true do
   local event, param1, param2 = os.pullEvent()
   if event == "abc" then
       print("ABC EVENT! Parameters: "..param1.." / " ..param2) --works fine
   elseif event == "tab" then
      print(tostring(param1)) --should print the table code, prints nil
 print(param1) --should print the table code, prints nothing
   else
       print("Event: "  .. event ) --from then on, every other event throws fine
   end
end

I'm running CC 1.46 on MC 1.4.2 with forge-universal 6.0.1.341, which should be up to date as of about Friday.
I have yet to test this error with older versions of MC, but I have a feeling this is unlikely to be a version-specific fault.
KaoS #4
Posted 30 October 2012 - 11:19 AM
I don't think the queueEvent function is meant to accept tables. what I would advise is using this


os.queueEvent("abc", 2, "sometext") --using numbers and strings to test
local tb = {} --a new table
print(tostring(tb)) --returns a table code
print(tb) --returns the same code
os.queueEvent("tab",textutils.serialize(tb)) --queue the table with an event
while true do
   local event, param1, param2 = os.pullEvent()
   if event == "abc" then
	   print("ABC EVENT! Parameters: "..param1.." / " ..param2) --works fine
   elseif event == "tab" then
	  param1=textutils.unserialize(param1)
	  print(tostring(param1)) --should print the table code, prints nil
print(param1) --should print the table code, prints nothing
   else
	   print("Event: "  .. event ) --from then on, every other event throws fine
   end
end

EDIT: actually it works fine my side…
Espen #5
Posted 30 October 2012 - 11:31 AM
Hey there, another take on it:

Java-side queueEvent takes an array of objects as an argument. Therefore you have to feed it multiple parameters instead of a table.
But it's fairly easy to unpack a table into its constituent parts, try the following:
os.queueEvent( "tab", unpack(tb) )

And if you want to pack the resulting parameters into a table again, that's equally simple:
local tResults = { os.pullEvent() }    -- Enclose pullEvent in curly brackets to pack all its return variables into a table.
local sEvent = results[0]    -- Extrac the event string.
table.remove(tResults, 0)    -- Now remove the event string from the table, so that only its parameters remain.

The second example can be done in different ways of course. E.g. you don't necessarily have to separate the parameters from the event string like I did in the last line, but could keep the result table intact and just pull out the parameters into new table instead.
The important part is the first line, which packs everything into a table again.

Hope this helps.
Cheers
Pharap #6
Posted 30 October 2012 - 12:01 PM
Hey there, another take on it:

Java-side queueEvent takes an array of objects as an argument. Therefore you have to feed it multiple parameters instead of a table.
But it's fairly easy to unpack a table into its constituent parts, try the following:
os.queueEvent( "tab", unpack(tb) )

And if you want to pack the resulting parameters into a table again, that's equally simple:
local tResults = { os.pullEvent() }	-- Enclose pullEvent in curly brackets to pack all its return variables into a table.
local sEvent = results[0]	-- Extrac the event string.
table.remove(tResults, 0)	-- Now remove the event string from the table, so that only its parameters remain.

The second example can be done in different ways of course. E.g. you don't necessarily have to separate the parameters from the event string like I did in the last line, but could keep the result table intact and just pull out the parameters into new table instead.
The important part is the first line, which packs everything into a table again.

Hope this helps.
Cheers

Well it would be easy were I not trying to pass a specific object as the argument since I use tables as objects more than arrays.
Surely though, would it not be easy enough for the Java side codes to allow the passing of tables? If it had an array of objects instead of an array of strings as an argument it would (in theory) be easy enough to return a table as an object instead of a string.
Pharap #7
Posted 30 October 2012 - 01:07 PM
Ultimately I found an easy method that I should have though of an hour ago:
Queue the table's tostring representation, then when comparing, just be sure to ref the other tables by their tostring since a table's tostring is technically it's reference value.
KaoS #8
Posted 30 October 2012 - 01:30 PM
but sometimes you have to use the table values in a function etc. here is an example (last code)
Pharap #9
Posted 30 October 2012 - 01:54 PM
If you're using a table as a table - a construct to hold several values - then use the table serialise and unserialise;
If you're using a table as an object, keep a collection of objects in a table then you can compare the reference code returned by the event to the reference codes of all the objects in the table, and if the objects are stored in the same location in memory (ie are the same object), their reference codes should match because it's simply a string representation of their location in memory. In a sense, they are like pointers, but you have to keep track of them yourself.
Espen #10
Posted 30 October 2012 - 02:12 PM
Well it would be easy were I not trying to pass a specific object as the argument since I use tables as objects more than arrays.
Surely though, would it not be easy enough for the Java side codes to allow the passing of tables? If it had an array of objects instead of an array of strings as an argument it would (in theory) be easy enough to return a table as an object instead of a string.

I guess that would be possible, but you can implement this functionality yourself Lua-side.
If you don't necessarily want to do this manually in every single program, then you could make little helper functions for queueEvent and pullEvent and make all the necessary table checks and wrapping for you in the future.
It could look something like this:

-- Override of queueEvent to provide a table as an argument.
local function queueEvent( sEvent, ... )
	local tParams = { ... }
	
	if #tParams > 0 and type(tParams[1]) == "table" then
		os.queueEvent( sEvent, "isTable", unpack(tParams[1]) )
	else
		os.queueEvent( sEvent, ... )
	end
end

-- Override of pullEvent to recognize if the parameter is a table.
local function pullEvent( sFilter )
	local nTableCheckPos = sFilter and 1 or 2	-- If sFilter was provided, nTableCheckPos is 1 (because we have one parameter less), else it is 2.
	local tResult = { os.pullEvent( sFilter ) } -- Wrap returned results in a table.
	
	if #tResult > 0 and tResult[nTableCheckPos] == "isTable" then
		local tParams = {}
		
		-- Store all the parameters after "isTable" into a table.
		for i = 3, #tResult, 1 do
			tParams[i - 2] = tResult[i]
		end
		
		-- Return the event string, as well as "isTable" and the return table that we just created in the prior step.
		return tResult[1], tResult[nTableCheckPos], tParams
	else
		-- Return the results as usual, i.e. multiple return values.
		return unpack( tResult )
	end
end
You will notice I used a string ("isTable") to mark an event as a table-event, so to speak.
But you can choose anything you like, really. A boolean value would be a good idea, although then you can not check for anything else than tables.
Because maybe in the future you also want to be able to queue and get customized types.
A mere boolean value would then not be enough. That's why I decided to go with the string "isTable" for this example.

Ok, and here now some example code to showcase how you could use the above two functions:

local myTable = { "One", "Two", "Tree" }

-- Test-Case 1: Queue a table.
print( "Test-Case 1: Queue and get a table." )
queueEvent( "Test", myTable )
local sEvent, isTable, a, b, c = pullEvent()

print( "Raw values:" )
print( sEvent.." - "..tostring(isTable).." - "..tostring(a).." - "..tostring(:P/>/>.." - "..tostring(c) )
print()
if isTable == "isTable" then
	print( "Table contents:" )
	-- Iterate over all entries in the table a and print them out.
	-- b and c should be nil and are therefore ignored here.
	for _, v in pairs(a) do
		print(v)
	end
end
print()
print()

-- Test-Case 2: Queue separate values.
print( "Test-Case 2: Queue and get separate values." )
queueEvent( "Test", "Honey", "Cinnamon", "Peanuts", "Garlic" )
local sEvent, isTable, a, b, c = pullEvent()

print( "Raw values:" )
print( sEvent.." - "..tostring(isTable).." - "..tostring(a).." - "..tostring(:D/>/>.." - "..tostring(c) )

I haven't tested it any further than that and I was not extensive with checks, etc.
If you decide to use something like this and have any problems, give us a holler. :P/>/>

Cheers

EDIT:
Added the quote I was referring to in this post, as there have been other responses in the meantime.
Edited on 30 October 2012 - 01:14 PM