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

Problem accessing global variables from an API

Started by Rybec, 18 December 2013 - 08:35 PM
Rybec #1
Posted 18 December 2013 - 09:35 PM
In my program, I have a global table called h that holds the wrapped handles for all monitors attached to my wired network. I have an Input API which is meant to temporarily take control of a monitor and display either a keyboard or numpad so you can set variables via touchscreen. I do this by having the API redirect the term to the monitor and run it's own internal event loop until you've finished.

Probelm is, when calling the text input function, I get an "attempt to call nil" error on the line where it references the global handle table.

Here's the actual text input function (Edit for clarity; this code is from my API, h[] is defined in the main program and is NOT a local variable)
function text(id,s) --id is the ID number of the monitor, s is the initial string to display so you can edit an existing string
	-- These are variables local to the API, used to prevent excessive parameter passing
	device = "monitor_" .. id
	txt = s
-----------------------
	term.redirect(h[id]) --This is the hiccup line; h[] is the global monitor handle table, and the API doesnt seem to have access to it.
-----------------------
	drawTxt() -- draws the text input screen
	dispStr() -- draws the string
	for k,v in pairs(btnsTxt) do k:draw() end --btnsTxt is a table of buttons made with my own button API; it contains a cancel, accept, backspace and shift button
	
	repeat
		local caps = btnsTxt.shift.active
		if loop() then -- loop() is an os.pullEvent that returns true if the current monitor was touched and false on any other event. mx and my are local API variables
			if button.handle(btnsTxt,mx,my) then -- button handler
				for k,v in pairs(btnsTxt) do k:draw() end
			else
				keyboard(mx,my) -- keyboard finds which letter you pressed and appends it to txt
			end -- if not handled
			dispStr()
		end -- clicked on loop
		if caps ~= btnsTxt.shift.active then drawKeyboard() end -- redraws the keyboard to reflect caps lock
	until complete == true
	
	term.restore()
	if canceled then return s else return txt end
end -- function()


I thought APIs had access to global variables? If not, is there a way to give it access without converting it from an API to a dofile code chunk or passing the handle table as an argument? I tried using _G.h[ID] but that doesn't work either.

I apologize that I cannot post all of the code, but this is part of a large project and the input API alone almost exceeds the stated limit for code post length.
Edited on 18 December 2013 - 09:01 PM
theoriginalbit #2
Posted 18 December 2013 - 09:54 PM
APIs do have access to global variables otherwise nothing would work, ever, you'd never be able to use any system APIs.

I have a feeling that the reason is because when you use os.loadAPI nothing within that file is added to the global table unless explicitly defined with _G, any function or variable within the API that is localised cannot be accessed, anything without it is accessed via the filename.variable. But without seeing more code, or more examples for you this is just speculation, however just incase my assumption is correct, here is an example:

API

local name = "foo"

local function getName()
  return name + " bar"
end

function whatIsYourName()
  return getName()
end

Main program

os.loadAPI("/API")

print(API.name) --# this does not work, it is localised
print(API.getName()) --# this also does not work, it is localised
print(API.whatIsYourName()) --# this will output "foo bar"
Rybec #3
Posted 18 December 2013 - 10:08 PM
You misunderstand, h[] is a table defined in my main program that I am trying to access in a function inside my API. I initialize h[] at the start of my main program by simply writing "h = {}" (and as such it should be a global variable), then populate it later.

When attempting to call input.text, the function defined in my API, it behaves as though it does not have access to h[] despite it being a global variable.

If I pass h[] to it as a parameter, it works just fine. However, that is more of a hack than a solution.
Edited on 18 December 2013 - 09:09 PM
jay5476 #4
Posted 19 December 2013 - 01:08 AM

local function getName()
  return name + " bar"
end
Cuz this works in lua :P/>, looks like you were having a bad day…
Bomb Bloke #5
Posted 19 December 2013 - 01:21 AM
To the best of my understanding (which I still feel is incomplete in this particular matter), it's because "h" is not defined as a table when you define the functions in the API. Are you calling os.loadAPI() before or after declaring "h"?
theoriginalbit #6
Posted 19 December 2013 - 02:18 AM
Cuz this works in lua :P/>, looks like you were having a bad day…
whoops. yeh, the heat is getting to me, its sending me crazy *eye twitches*, its currently 44ºC (111ºF) inside my house…

To the best of my understanding (which I still feel is incomplete in this particular matter), it's because "h" is not defined as a table when you define the functions in the API. Are you calling os.loadAPI() before or after declaring "h"?
shouldn't matter, only local variables require forward declarations due to the fact that any variable that fails to resolve in the local space is resolved in the global space.
Edited on 19 December 2013 - 01:19 AM
Bomb Bloke #7
Posted 19 December 2013 - 05:55 AM
Down south here it's a bit cooler, and I'm still enjoying the novelty of the clouds and cold going away. Even so, I haven't bothered to put the sides on my desktop machine in years.

shouldn't matter, only local variables require forward declarations due to the fact that any variable that fails to resolve in the local space is resolved in the global space.
Ah, that makes sense.

I had a bit of a tinker, and it looks like the API gets its own global environment. As far as it's concerned, any variables outside that environment don't exist, other then the ones in _G (bearing in mind _G doesn't contain the "user's" global variables).

Oddly enough, this seems to be the case even if the calling script tries to define new variables directly into the API's table. I assume there's a way to get the API to detect the desired environment and tap into the variables there, but I can't say I know how that's done.

It looks like the API should be able to pull "h" from _G so long as you specifically place it in _G. If you immediately follow up your "h = {}" declaration with a "_G.h = h", then the API should be able to access the original "h" via the "_G.h" pointer.
theoriginalbit #8
Posted 19 December 2013 - 06:13 AM
What I'd give to be over the bass strait right now!

It looks like the API should be able to pull "h" from _G so long as you specifically place it in _G. If you immediately follow up your "h = {}" declaration with a "_G.h = h", then the API should be able to access the original "h" via the "_G.h" pointer.
Did you mean _G.h = {}, 'cause there's really no point to declaring 2 pointers for it, esp since it could be referred to as h in the main program.
Bomb Bloke #9
Posted 19 December 2013 - 07:03 AM
I'm lying on my bed (as opposed to in it), with the window open, and I'm still just nicely warm. :)/>

Anyway, seems I'd got myself confused and had decided that the main program would need to use _G.h if he just made the one pointer. Triple-checking it now, it seems that creating that one pointer directly in _G means both the main program and the API get regular access to h, so that certainly seems like the easiest way to do it.
Rybec #10
Posted 19 December 2013 - 11:43 AM
Altered my code to declare h via _G.h, everything's confirmed working. Thanks guys.
Edited on 19 December 2013 - 10:58 AM