←  Tutorials

ComputerCraft | Programmable Computers for Minecraft

»

Advanced function replacement

KaoS's Photo KaoS 19 Sep 2012

View PostCranium, on 18 September 2012 - 06:52 PM, said:

Spoiler

I must admit to being behind Cranium on this one... please post to help or ask, that is pretty much the point of the forum
Quote

Jajnick's Photo Jajnick 19 Sep 2012

Uh... Did I say something wrong?
I'm just asking why the original post is so long, while it can be shortened and be even more understandable... Why are you raging at me?
Quote

KaoS's Photo KaoS 19 Sep 2012

View PostJajnick, on 18 September 2012 - 06:15 PM, said:

This article in a nutshell:

Quote

Functions are values
I don't really see the point of this wall of text.

I apologise if I offended you, we were just looking at various methods of replacing existing functions, most functions are stored in a table and I wanted to be able to locally change a function, not globally. this caused multiple issues, I have worked it out now though. that was the point for me, not sure about the other people. what I was saying is that these forums are here so we can assist each other in learning CC and showing others what we have learned. please try to avoid criticising other threads. that was pretty much my point
Quote

ElvishJerricco's Photo ElvishJerricco 05 Oct 2012

View PostJajnick, on 19 September 2012 - 09:46 AM, said:

Uh... Did I say something wrong?
I'm just asking why the original post is so long, while it can be shortened and be even more understandable... Why are you raging at me?

Sorry about being late to this conversation...

The reason it's so long is to show examples and give a very clear understanding of the concept. In most compiled languages, functions and methods aren't values in the same way they are in lua. It's a concept that's weird to grasp coming from C or Java. But lua is a scripting language, and in most scripting languages functions work the way they do here. If i told a novice lua programmer that functions are values and said the symbols can be redefined, they wouldn't really know what to do with it. The tutorials forum is meant to have deep explanation of each topic. That's what i tried to do.
Quote

FunshineX's Photo FunshineX 05 Oct 2012

Be careful when you replace functions and save a backup pointer. If you run the program multiple times you'll get unexpected results. Best to os.reboot() in between to restore the originals
Quote

ElvishJerricco's Photo ElvishJerricco 06 Oct 2012

View PostFunshineX, on 05 October 2012 - 09:18 PM, said:

Be careful when you replace functions and save a backup pointer. If you run the program multiple times you'll get unexpected results. Best to os.reboot() in between to restore the originals

Where would the issue arise? Only thing I can think of is if you don't check to see if someFunction already equals myReplacementFunction, then when you do the replacement again it ends up replacement calling old (identical besides the pointer to the function) replacement calling original. And yea I guess that's a pretty big problem...
Quote

FunshineX's Photo FunshineX 07 Oct 2012

View PostElvishJerricco, on 06 October 2012 - 04:43 AM, said:

Where would the issue arise? Only thing I can think of is if you don't check to see if someFunction already equals myReplacementFunction, then when you do the replacement again it ends up replacement calling old (identical besides the pointer to the function) replacement calling original. And yea I guess that's a pretty big problem...

The 4th block of code in the original post suffers from this
Quote

Tiin57's Photo Tiin57 07 Oct 2012

As long as this post is revived, good job, ElvishJerrico.
Quote

ElvishJerricco's Photo ElvishJerricco 08 Oct 2012

View PostFunshineX, on 07 October 2012 - 08:00 AM, said:

View PostElvishJerricco, on 06 October 2012 - 04:43 AM, said:

Where would the issue arise? Only thing I can think of is if you don't check to see if someFunction already equals myReplacementFunction, then when you do the replacement again it ends up replacement calling old (identical besides the pointer to the function) replacement calling original. And yea I guess that's a pretty big problem...

The 4th block of code in the original post suffers from this

In that case it's actually intentional. You'll notice I said that I'd only do that code if I were writing my own OS, where I want to manage all the Apis and I want to control the print statement for all of runtime. The replacement should only get called once for the entire lifetime of the computer from bootup to shutdown

View Posttiin57, on 07 October 2012 - 08:39 AM, said:

As long as this post is revived, good job, ElvishJerrico.

Thanks :3
Quote

Cabu's Photo Cabu 21 Nov 2012

Hi,

I am new to lua and computercraft, but i am writing an API for advanced turtle functions like turnTo(direction) and moveTo(X, Y, Z, direction). They work pretty well now. But i want to integrate my API deeper into the system.

For now to keep track of the turtle position, I read its GPS position once and the api user should not use the turtle.forward() / turtle.back() / turtle.up() / turtle.down() / turtle.turnLeft() / turtle.turnRight() anymore but the my API equivalents one.

I would like to override the turtle functions by my own, but i still need in my own function be able to call the original function and the local variable of my API.

Here is an extract of my API:
-- Cache of the current turtle position and direction
local cachedX, cachedY, cachedZ, cachedDir
-- Directions
North, West, South, East, Up, Down = 0, 1, 2, 3, 4, 5
local deltas = {[North] = {0, 0, -1}, [West] = {-1, 0, 0}, [South] = {0, 0, 1}, [East] = {1, 0, 0}, [Up] = {0, 1, 0}, [Down] = {0, -1, 0}}
-- cache world geometry
local cachedWorld = {}
...
function forward()
  local D = deltas[cachedDir]
  local x, y, z = cachedX + D[1], cachedY + D[2], cachedZ + D[3]
  local idx_pos = x..":"..y..":"..z

  if turtle.forward() then
	cachedX, cachedY, cachedZ = x, y, z -- update the position cache
	cachedWorld[idx_pos] = 0 -- clear the world cache
	return true
  else
	cachedWorld[idx_pos] = (turtle.detect() and 1 or 0.5)  -- update the world cache
	return false
  end
end
...

An you use it like that:
os.loadAPI("egps")
if egps.startGPS() then
  local x, y, z, d = egps.locate()
  print(x, ' ', y, ' ', z, ' ', d)
  egps.forward()
  x, y, z, d = egps.cachedLocate()
  print(x, ' ', y, ' ', z, ' ', d)
end
Quote

louitzie's Photo louitzie 22 Nov 2012

for my new os I want to override shell.run globaly so i can apply the right display settings depending on my own programs or default (rom) programs

tried code

local run=shell.run
_G.shell.run=function(command,...)
--configuration code here
return run(command,...)

i also tried without the _G

but it only changes localy.

any ideas are welcome.

found it.

if you ever want to do this override os.run instead
Quote

Orwell's Photo Orwell 22 Nov 2012

View Postlouitzie, on 22 November 2012 - 10:09 AM, said:

for my new os I want to override shell.run globaly so i can apply the right display settings depending on my own programs or default (rom) programs

tried code

local run=shell.run
_G.shell.run=function(command,...)
--configuration code here
return run(command,...)

i also tried without the _G

but it only changes localy.

any ideas are welcome.

found it.

if you ever want to do this override os.run instead
You're missing an end. ;)/> And shell.run overrides the global environment with an empty one (except for a reference to the parent shell). Maybe that's your problem?
Quote

Expenox's Photo Expenox 25 Nov 2012

Thank you for this! :D/>
Quote

CoolisTheName007's Photo CoolisTheName007 25 Nov 2012

View PostCabu, on 21 November 2012 - 04:02 AM, said:

snip]
do something like
old_turtle=turtle
turtle={}
turtle.forward= function (args)
stuff
-here you want to go forward, so you do:
  old_turtle.forward()
ect

Also, for the main discussion of altering code locally, I would arg that just replacing functions is incomplete, because internal functions in the API won't refer to the new one, but to the one that is still in the environment. A complete solution would be a dofile and tampering with the environment directly.
Quote

KaoS's Photo KaoS 25 Nov 2012

also: I should mention that you should be careful with stuff like this because sometimes you will run your program twice and it will double the code on the turtle commands. here is an example to explain

local oldT=turtle
turtle.forward=function(...)
  print('whoah! you just moved forwards!')
  return oldT.forward(...) --you want to always return whatever the oldT command would do so you still get back the output of the move CMD
end

then if you say turtle.forward() it would move forward and output 'whoah! you just moved forwards!', then if you ran the above code again it would take the current turtle.forward() (including the print bit) and append another print onto it, then if you move forward it will print 'whoah! you just moved forwards!' twice which of course is a problem, the way to get around this is to make a variable recording if you have changed the command and prevent it from modifying it again

if not mod then
  local oldT=turtle
  mod=true
  turtle.forward=function(...)
    print('whoah! you just moved forwards!')
    return oldT.forward(...) --you want to always return whatever the oldT command would do so you still get back the output of the move CMD
  end
end

even easier is just to use the oldT table

if not oldT then
  oldT=turtle
  turtle.forward=function(...)
    print('whoah! you just moved forwards!')
    return oldT.forward(...) --you want to always return whatever the oldT command would do so you still get back the output of the move CMD
  end
end

some would call that un-neat as the oldT table is no longer local but I find it useful in case you need to use the original commands
Quote

CoolisTheName007's Photo CoolisTheName007 25 Nov 2012

View PostKaoS, on 25 November 2012 - 10:44 PM, said:

snip
You can save oldT/mod in the turtle table; that way, everything is still contained and os.loadAPI, which always reloads the API, won't result in problems:
if not turtle.old then
   local old=turtle
   turtle={}
   turtle.old=old
--modifications
end
Quote

KaoS's Photo KaoS 26 Nov 2012

AHAH, and behold I present unto you unparalleled genius. thanks for that idea CoolisTheName007, just remember not to do that is you are going to use a for loop to parse the turtle table and replace all functions unless you are careful and include a type(v)=='function' in it
Quote

CoolisTheName007's Photo CoolisTheName007 26 Nov 2012

View PostKaoS, on 26 November 2012 - 02:19 AM, said:

snip
Your previous code does not work, in fact it creates an infinite loop, because you never create a new table for turtle and consequently oldT.forward==turtle.forward.
When using an old table , if you don't want to copy the API manually you may be interested in a deepcopy function. However, those often leave details such as metatables out, so I would rather use a replace on necessity method, such as
old_forward=turtle.forward
turtle.forward=function ...
Quote

KaoS's Photo KaoS 26 Nov 2012

yes, you are most correct.... an unforgivably n00b mistake on my side. don't forget to create a new table for turtle guys, I'm gonna go end my stupid misery lol, can't believe I just did that... when it comes to copying the other functions I would just use a metatable like so

turtle=setmetatable({},{__index=oldT})
Quote

ChunLing's Photo ChunLing 30 Nov 2012

Why not just check if oldT already exists, so "if not oldT then oldT=turtle ...andotherstuff... end". Also, that way oldT is global so you don't lose it by going out of scope/unexpected termination.

No, nevermind, I see you already discussed all that.
Edited by ChunLing, 30 November 2012 - 12:40 PM.
Quote