Sharing locals with a function created using "load"

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Sharing locals with a function created using "load"

Ignacio Burgueño-2
Hi.

I'm writing some code where I need to generate functions based on a string template.

For instance, in the following template:
[[
function (cpu)
  logger.debug("RLC %s")
  cpu.%s = RLC(cpu, cpu.%s)
  cpu.PC = (cpu.PC + 2) & 0xffff
end
]]

"logger" and "RLC" are locals. I'm loading that template with load and when running the resulting function, it complains about "logger" being nil.

I could provide a table with those variables, but I have many besides those three, and they depend on the given template. Also, I'd like to avoid those variables being resolved as globals. 

Is there any way I could "inject" locals into the function?

I thought about changing the template to:
[[
local logger, RLC
function (cpu)
  logger.debug("RLC %s")
  cpu.%s = RLC(cpu, cpu.%s)
  cpu.PC = (cpu.PC + 2) & 0xffff
end
]]
and then using debug.getupvalue / setupvalue to bind those locals. This seems to be feasible, but I'd like to know if there is an easier way.

Regards,
Ignacio


Reply | Threaded
Open this post in threaded view
|

Re: Sharing locals with a function created using "load"

Philipp Janda
Am 04.05.2015 um 04:16 schröbte Ignacio Burgueño:
> Hi.

Hi!

>
> I'm writing some code where I need to generate functions based on a string
> template.
>
> For instance, in the following template:
> [[
> function (cpu)
>    logger.debug("RLC %s")
>    cpu.%s = RLC(cpu, cpu.%s)
>    cpu.PC = (cpu.PC + 2) & 0xffff
> end
> ]]
>
> "logger" and "RLC" are locals. I'm loading that template with load and when
> running the resulting function, it complains about "logger" being nil.
>
> I could provide a table with those variables, but I have many besides those
> three, and they depend on the given template. Also, I'd like to avoid those
> variables being resolved as globals.
>
> Is there any way I could "inject" locals into the function?
>
> I thought about changing the template to:
> [[
> local logger, RLC
> function (cpu)
>    logger.debug("RLC %s")
>    cpu.%s = RLC(cpu, cpu.%s)
>    cpu.PC = (cpu.PC + 2) & 0xffff
> end
> ]]
> and then using debug.getupvalue / setupvalue to bind those locals. This
> seems to be feasible, but I'd like to know if there is an easier way.

I usually use something like

     local s = [[
     local logger, RLC = ...
     return function (cpu)
       -- some code
     end
     ]]

     local f = assert(load(s, s, "t", {}))(logger, RLC)

for this.

>
> Regards,
> Ignacio
>

Philipp



Reply | Threaded
Open this post in threaded view
|

Re: Sharing locals with a function created using "load"

彭 书呆
In reply to this post by Ignacio Burgueño-2
在 2015/5/4 10:16, Ignacio Burgueño 写道:

> Hi.
>
> I'm writing some code where I need to generate functions based on a string template.
>
> For instance, in the following template:
> [[
> function (cpu)
>   logger.debug("RLC %s")
>   cpu.%s = RLC(cpu, cpu.%s)
>   cpu.PC = (cpu.PC + 2) & 0xffff
> end
> ]]
>
> "logger" and "RLC" are locals. I'm loading that template with load and when running the resulting function, it complains about "logger" being nil.
>
> I could provide a table with those variables, but I have many besides those three, and they depend on the given template. Also, I'd like to avoid those variables being resolved as globals.
>
> Is there any way I could "inject" locals into the function?
>
> I thought about changing the template to:
> [[
> local logger, RLC
> function (cpu)
>   logger.debug("RLC %s")
>   cpu.%s = RLC(cpu, cpu.%s)
>   cpu.PC = (cpu.PC + 2) & 0xffff
> end
> ]]
> and then using debug.getupvalue / setupvalue to bind those locals. This seems to be feasible, but I'd like to know if there is an easier way.
>

if you do not want to use the debug library, the two ways I know to talk to a chunk are:
  1) globals/`_ENV` (via the `load` function or `setfenv` for Lua 5.1);
  2) chunk arguments (a chunk is essencially a vararg function)

otherwise, the debug library is your only choice. you must first declare locals in the chunk
to allocate slots for them, then use `debug.setlocal` or `debug.setupvalue` or `debug.upvaluejoin`
to modify them.

> Regards,
> Ignacio
>
>
--
the nerdy Peng / 书呆彭 / Sent from Thunderbird



Reply | Threaded
Open this post in threaded view
|

Re: Sharing locals with a function created using "load"

Michal Kottman-2
In reply to this post by Philipp Janda
On 4 May 2015 at 05:02, Philipp Janda <[hidden email]> wrote:
I usually use something like

    local s = [[
    local logger, RLC = ...
    return function (cpu)
      -- some code
    end
    ]]

    local f = assert(load(s, s, "t", {}))(logger, RLC)

for this.

And using this approach, you can even hide the fact that you are creating locals:

local input = [[
function (cpu)
  logger.debug("RLC %s")
  cpu.%s = RLC(cpu, cpu.%s)
  cpu.PC = (cpu.PC + 2) & 0xffff
end
]]

... somewhere in your code, this can be "ugly" with as many "local" variables as you want:
local s = [[local logger, RLC = ...\n]] .. input
local f = assert(load(s, s, "t", {}))(logger, RLC)

Reply | Threaded
Open this post in threaded view
|

Re: Sharing locals with a function created using "load"

Tomás Guisasola-2
Hi Ignacio

Can't you use upvalues as in the code below:

function generate (key)
   local debug_message = "RLC "..key
   return function (cpu)
     logger.debug(debug_message)
     cpu[key] = RLC(cpu, cpu[key])
     cpu.PC = (cpu.PC + 2) & 0xffff
   end
end

It seems simpler to me...

Regards,
Tomás

Reply | Threaded
Open this post in threaded view
|

Re: Sharing locals with a function created using "load"

Ignacio Burgueño-2
Indeed, passing arguments to the loaded chunk was the simpler solution, but Tomas answer made me reconsider the problem and I think that I can simplify it enough so I don't need to use string templates to build the code.
I think that I was making things harder than they need to be.

Anyway, I think I'll hit some cases were I'd need to use templates, so the varargs suggestion will definitely be used.

Thanks everyone for your help.