Reputation: 93
I am calling a lua function from a c++ script. Within the lua function, I need to update certain variables and tables based on the data that I am passing to the function from C++. The data has to be passed to the lua function in a continuous loop, and each successive update to the lua variables and tables must take into account the changes made by the previous updates. How to accomplish this in a robust way? One clumsy way might be just to pass the values around between C and lua, or save and load them repeatedly, but since my data tables are expected to be really large that really may not be a good option. Please suggest solutions.
PS: I cannot simply write the entire code in lua since the C++ code is meant to run rosservices and subscribe to rostopics. I think it may be difficult to achieve the same in lua, and although there is a package called roslua apparently it is not compatible with ros indigo.
function test_upvalues(a)
print(a)
local x=10
local function increment(t)
x=x+t
end
--increment(a)
print(x)
return(increment)
end
Calling this from C++
lua_getglobal(L, "test_upvalues");
lua_pushvalue(L, -1);
lua_pushnumber(L,2); //push a value to increment state
lua_call(L,1,0);
lua_pushvalue(L, -1);
lua_pushnumber(L,3); //push another value to increment state further
lua_call(L,1,0);
Here variable x is the state whose state I want to retain after incrementing it by the value passed from C++. What I am not getting is where is the local function increment(t)
being invoked? should be within the outer lua function or elsewhere? when I comment out the line increment(t) within the outer lua function, I simply get 2, 10, 3, 10 as the output, whereas on with increment(t) I am just getting 2, 12, 3, 13. Clearly the static variable behaviour is not getting implemented correctly. Where am I going wrong? Thanks.
Upvotes: 3
Views: 3280
Reputation: 3083
Expanding on the use of upvalues to carry state, here is some pseudo-code to get you started.
Lua function factory which returns a function (myfunction
) which has persistent state as an upvalue (mystate
, mystate2
):
function create_myfunction ()
local mystate = {} -- initial value of state
local mystate2 = 0 -- Can have more than one upvalue
local function myfunction (args)
-- modify mystate and mystate2 based on args here
end
return myfunction
end
Creating an instance of myfunction
with state from Lua:
called_from_c_function = create_myfunction();
Creating an instance of myfunction
wih state from C:
lua_getglobal(L, "create_myfunction");
lua_call(L, 0, 1); // Zero arguments, one result
Calling this function from C in a loop
// Assume myfunction is on the top of the stack
for (...) { // Loop
lua_pushvalue(L, -1); // Copy function on stack (so we can use it again, as it gets consumed by the call)
// Now push an argument to the function on the stack here
lua_pushfoo(L, ...); // Just an example
lua_call(L, 1, 0); // Call myfunction with 1 argument, returning 0
// We should be stack-neutral at this point, with myfunction on top
}
Let me know if anything here is unclear. I'm making some assumptions about what sort of state you are trying to maintain, and there are a bunch of other things that can be handled with a little extra work (like returning the state to C, for example).
In your example calling the Lua code from C++, there are a couple of problems. Here is what I believe you wanted:
lua_getglobal(L, "test_upvalues"); // Put factory function on stack
lua_pushnumber(L, 1); // Argument 'a' (not sure what you are using it for)
lua_call(L, 1, 1); // Call factory. Consumes argument 'a', returns function 'increment'
lua_pushvalue(L, -1); // Copies 'increment' on stack
lua_pushnumber(L, 2); //push a value to increment state
lua_call(L,1,0); // Calls 'increment' function with increment
lua_pushvalue(L, -1); // Copies 'increment' on stack
lua_pushnumber(L, 3); //push another value to increment state further
lua_call(L,1,0); // Calls 'increment' function with increment
In order to see the results of these increments, you'll want to print them out inside the increment
function. The factory function gets called once. The function it returns gets called multiple times.
If you want to be able to set the initial state and retrieve the value afterward, you might want a lua function something like this:
function increment_factory(value)
local function get_state()
return value
end
local function increment(x)
value = value + x
end
return get_state, increment
end
Here the factory function returns two functions, one to increment the state, and the other to retrieve it. value
is an upvalue to both get_state
and increment
. (If you want the value of the state after each increment, it is easier to return it from increment
.) This would be used like this:
lua_getglobal(L, "increment_factory");
lua_pushnumber(L, INITIAL_STATE);
lua_call(L, 1, 2); // One argument, two returned functions
lua_pushvalue(L, -1); // Copy 'increment'
lua_pushnumber(L, 2); // Push value to increment
lua_call(L, 1, 0); // Call 'increment'
lua_pushvalue(L, -1); // Copy 'increment'
lua_pushnumber(L, 3); // Push value to increment
lua_call(L, 1, 0); // Call 'increment'
lua_pushvalue(L, -2); // Copy 'get_state'
lua_call(L, 0, 1); // Call 'get_state'
// Now the current state is on the top of the stack
Upvotes: 1