Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Issue with calling Lua function from C/C++

This thread is locked; no one can reply to it. rss feed Print
Issue with calling Lua function from C/C++
Kevin Adrian
Member #7,087
April 2006
avatar

Hi,

for my current Allegro project I want to use Lua as scripting language. But for the moment I have an issue with calling Lua functions in C/C++. It seems that the metatable of an object is overwritten or deprecated when a second object with a different metatable is used in the same script.

I want to implement a Lua function 'update(m,h)' which is called from C/C++ in my project. This function will expect two light userdata objects of different types (this means two different metatables):
m: map object
h: hero object
Then I tested three scenarios:

Scenario 1)

function update(m)
  print("[Lua] map-id: "..m:get_id())    -- this works
end

Scenario 2)

function update(h)
  print("[Lua] hero name: "..h:name())  -- this works
end

Scenario 3)

function update(m,h)
  print("[Lua] hero name: "..h:name())  -- this works
  print("[Lua] map-id: "..m:get_id())    -- this does not work anymore! m is available, but get_id() cannot be called.
end

On C/C++ side, this Lua function is called the following way:

#SelectExpand
1void __lua_update(lua_State* L,_HERO* h,MAP* m) { 2 3 lua_getglobal(L,"update"); 4 5 // push map object 6 lua_pushlightuserdata(L,(void*) m); 7 luaL_getmetatable(L,"L_map"); 8 lua_setmetatable(L,-2); 9 10 // push hero object 11 lua_pushlightuserdata(L,(void*) h); 12 luaL_getmetatable(L,"L_hero"); 13 lua_setmetatable(L,-2); 14 15 lua_pcall(L,2,0,0); 16}

NOTE: Both metatables 'L_map' and 'L_hero' have been created in an earlier init phase.

Does anybody have some advices how to solve this issue, so that both map and hero object can be accessed within the Lua script?

Best regards

My mouth will speak words of wisdom; the utterance from my heart will give understanding. (Psalm 49:3)

Edgar Reynaldo
Member #8,592
May 2007
avatar

According to the docs, a table (like the one for update) can only have one metatable. So you're overwriting the metatable for your update function when you call setmetatable(L , -2) twice. Somehow you need to bind the metatable for each object to a separate object.

Each table in Lua may have its own metatable. (As we will see later, userdata also can have metatables.)

bamccaig
Member #7,536
July 2006
avatar

I'm confused by what this __lua_update function is supposed to do. It doesn't seem to make sense to associate a metatable with a table during an "update" function. :-/ I've barely learned the basics of Lua and that was probably 8 years ago, but this C function appears to:

  • Load the global variable "update" onto the stack (presumably your update function in Lua).

  • Load a 'userdata' object (pointer) to your C object onto the stack.

  • Load the "L_map" metatable from Lua onto the stack (I'm not sure if that means loading a typed-variable or something more specific).

  • Associate this userdata object (at -2 index on the stack?) with the metatable on the top of the stack? I'm not sure anymore how this works... It's been too long. This also pops off the metatable, I think.

  • Now load the second 'userdata' object onto the stack (the first is still there, along with the "update" function).

  • Associate that userdata object with another metatable and pop the metatable off of the stack.

  • Finally attempt to call the function with the two userdata objects as arguments.

It sounds vaguely like it should work, assuming that you don't care about destroying any other "metatable" associated with these objects whenever you call this function... Presumably, if it's the same one there's no harm done if Edgar is right about overwriting (as opposed to chaining) them. It also sounds like a very strange pattern. If anything, if you were to do this kind of "hack", I'd expect you to restore the original metatables before returning...

So tell me, how far off am I compared to your understanding?

Polybios
Member #12,293
October 2010

Without looking at the code:

This function will expect two light userdata objects of different types (this means two different metatables)

IIRC, light userdata cannot even have individual metatables.

Lua manual said:

Userdata represent C values in Lua. A light userdata represents a pointer, a void*. It is a value (like a number): you do not create it, it has no individual metatable, and it is not collected (as it was never created). A light userdata is equal to "any" light userdata with the same C address.

Maybe it would be better to use a full userdata object for storing your pointers.

Your code looks fine to me, although I am a bit confused by your update variants.

Generally, it could be a good idea to keep important Lua values in the registry using luaL_ref and luaL_unref. Pushing them again is quite fast, so you don't have to recreate the metatables on every update.

Interfacing Lua can be a pain... A hack that I used once was to use light userdata values as index to a table with weak values. So the C++ pointers were the keys and full userdata with proper metatables set that referenced the same objects were the values. So I could push those by pointer. Not elegant, not very fast, but it worked...

Kevin Adrian
Member #7,087
April 2006
avatar

Thanks a lot for your advices. I have not that much experience in Lua and binding to C/C++.

I will check my code and rework it.

My mouth will speak words of wisdom; the utterance from my heart will give understanding. (Psalm 49:3)

Go to: