I have just started delving into Lua, and I learnt that C++ object properties could be accessible through metatables.
I am trying to access such an object's functions in a game's script: "GameLib". It is available in Lua as a global variable, but getmetatable() returns nil:
-- example usage elsewhere in the scripts:
local pPlayer = GameLib.GetLocalPlayer();
-- my tried code:
local mt = getmetatable(GameLib);
print("Metatable type:" .. type(mt)); -- "Metatable type: nil"
What could be the problem? Are there cases, when a C++ object has no metatable? If so, is there another way to access its properties?
From the Lua 5.4 Reference Manual:
2.4 Metatables and Metamethods:
Every value in Lua can have a metatable.
By default, a value has no metatable, but the string library sets a metatable for the string type
So there are cases where values, even userdata have no metatable. In fact that's default.
6.1 Basic Functions: getmetatable
If object does not have a metatable, returns nil. Otherwise, if the
object's metatable has a __metatable field, returns the associated
value. Otherwise, returns the metatable of the given object.
So the that leaves us with two options why getmetatable(GameLib) returns nil:
GameLib does not have a metatable
getmetatable is not Lua's getmetatable. It has been overwritten by a function that returns nil for at least some values. Trivial function getmetatable() end
Related
I want to add some static fields to my Lua userdata objects coming from C++. The objects in question are vectors, they're created in C++ land and they work as they are but I've tried lua_setfield on my userdata but I get a attempt to index a Vector value error and I don't really want to have to use .x(), .y(), .z() due to the cost of having to call a function, push to stack and then read on the Lua side.
Is there any way to register fields on userdata for Lua access?
Userdata doesn't have "fields". What it can have is a metatable, for which you can define the __index and __newindex metamethods. Given such a metatable, the former function is called when reading the value of a field of the userdata, while the latter is called when assigning a value to a field from the outside (ud.some_field = 4 or an equivalent). If you want fields to be read only (to some degree), you can just implement __index, and attempts by the user to use __newindex will fail.
So in your C++ code, after creating the userdata, you can use lua_setmetatable to assign it a table that has these methods defined within it. Of course, these functions will need to access the actual C++ object from the userdata and fetch the specific values you're interested in.
So with the Lua C API you can save a Lua value in the registry and retrieve it later. There are different ways to do it, you can create a variable and use it's pointer as the key in the registry as it's always unique. You would push the pointer as light userdata.
You can also create a reference using LuaL_ref(L, LUA_REGISTRYINDEX). What is the advantage of one over the other? When to use references and when to use pointers?
Also with references, as it is called a reference, if the Lua garbage collector collects the Lua value, will the value in the registry be nil? What if Lua updates the Lua value, will the value in the registry also change?
Lua registry is just another lua table, easily accessible via predefined "special" index. I guess you don't need explanations on how Lua table is different from light userdata.
It doesn't really matter how you will index registry table, as long as you can store that key on C/C++ side. For your convenience there's already functions (luaL_ref/luaL_unref) giving you integer key that is easy to store and move around.
About garbage collection - rules are always the same. As long as value is stored in table that wasn't marked as weak table (registry is not weak table), that value won't be cleared. You must explicitly remove value from the registry.
Changing value will obey normal Lua rules. Assigning new immutable value to some variable won't change value stored in registry, i.e. registry won't follow updates to some variable. But changing content of mutable value (table etc) is ok, since registry and variable will refer same value.
In addition to previous answer:
Differences between Lua lightuserdata and userdata
lightuserdata is a special Lua type (as well as nil, boolean, number, string, table, thread etc.) containing C pointer. Nothing more. You can't assign metatable to lightuserdata. On the contrary, you can assign metatable to userdata type. For example see Lua File operations, where file handle is userdata with methods. f:read("*all") f is userdata the command is equivalent to f.read(f, "*all")
Indexing LUA_REGISTRYINDEX with integer or C pointer
There are two methods that are widely used on registry table.
Create new reference to Lua value with luaL_ref and store the return integer value somewhere in your code. I.e. to access the Lua value you'll need to read C variable holding the reference and index registry table with lua_rawgeti(L, LUA_REGISTRYINDEX, i) where is that integer value. lua_rawseti(L, LUA_REGISTRYINDEX, i) is also possible, but don't try rewrite to a nil value with this method!
You creating a static C variable static int myvar; and then use lua_rawgetp(L, LUA_REGISTRYINDEX, &myvar) and lua_rawsetp(L, LUA_REGISTRYINDEX, &myvar) for manipulation of stored Lua value straight-forward.
Unfortunately I can't compare the performance of both methods. I think they are almost the same.
A simple example of producing.
protocol.onConnect(function() end, function () end, ...)
Now in c, i want to get the functions which are in args #1, #2.
In strings, numbers,... we can get them using (lua_getstring,..), But I at-least didn't found how to get a function.
int luaProtocolOnConnect(lua_State* L)
{
int base_func // func #1
int call_func // func #2
....
}
You cannot really "get" a Lua function. Lua functions, like Lua tables, are pure-Lua objects. As such, they have no C or C++ analog. If you want to call a Lua function, that's done through lua_call, lua_pcall or similar functions. This is done in-situ on the Lua stack.
So you can't take a Lua function and turn it into a C++ value. What you can do is take a Lua function and manipulate it in the various ways that all Lua objects can be manipulated.
For example, let's say you want to store a Lua function in a C++ object, then later call whatever Lua function was stored there. Well obviously, you can't convert the Lua function directly into a C++ value. What you can do is store that Lua function in a place which C++ can access. You use some value which does have a C++ analog to reference that stored Lua function. The value must be unique for every object you want to store like this. The value you get when storing the object will be saved in your C++ object. When the time comes to retrieve the Lua function, you simply use the stored value to retrieve it.
Because this is an exceedingly common operation, Lua has ways to facilitate this. The first is the Lua registry, a table that C++ can access but Lua code cannot (not unless you give it access).
The second is the luaL_ref series of functions. luaL_ref takes whatever is at the top of the stack and sticks it in a table you provide, returning to you an integer key that can be used to retrieve it later. lua_rawgeti can be used to retrieve the function from the table by the key, and luaL_unref takes the table and the integer key, removing the referenced function from the table when you're done with it.
So if you want to store such functions, you simply need to create such a table, stick it in a known place in the registry (so that you can fetch it whenever you need to), and then use luaL_ref to store those functions. When it comes time to call them, retrieve them with lua_rawgeti. When you're finished using them, destroy them with luaL_unref.
You can use lua_isfunction to check if it's a function, use lua_pushvalue to push its value on top of the stack and then use luaL_ref (luaL_ref(L,LUA_REGISTRYINDEX);) to turn it into a unique key you can later reference to retrieve the value (lua_rawgeti(L,LUA_REGISTRYINDEX,ref)) and call the function.
Is it possible that luabind checks, if a member function call to an exported class (object) is for a valid object?
lets assume that i have a Class called Actor exposed using luabind to lua. Im calling a lua function from C++ with an actor object as parameter. Now before the function finishes, a script write would put the actor object in a global lua reference to be accessed later.
Later on, the actor object is deleted from the C++ site, another function is called which tries to access the invalidated actor object (any method from it) - and obviously since it has been deleted, it results in a crash (access violation)
sample:
local myObjRef = nil
function doSomethingWithActor(actor)
-- save, still valid object
actor:Say("hello")
myObjRef = actor
end
function calledAfterActorWasDeleted()
--- will crash if the c++ object has been deleted meanwhile, works fine if it still exists
myObjRef:Say("Crash...")
end
A NIL check doesnt help here, is this something that can be checked on luabinds site? The functions are executed using lua_pcall(....) and the stacktrace shows the error at luabinds call.hpp results = maybe_yield(L, lua_gettop(L) - arguments, (Policies*)0);
If not, is there another solution how to make sure somebody who writes a script cannot create these issues?
Now before the function finishes, a script write would put the actor object in a global lua reference to be accessed later.
That right there is where your problem is coming from. If you want Lua code to own the object (that is, preserve the existence of this object), then you need to use Luabind mechanics to tell Luabind that you want to do that. Otherwise, if you pass a pointer to some Lua function, Luabind will assume that the function will not be trying to gain ownership of it.
If you want ownership to be shared between Lua and Luabind, then you should wrap your objects in a boost::shared_ptr, and use Luabind's smart pointer mechanisms to do this.
You could also simply segregate your scripts better. If you have some script that operates on a particular actor, then that script and any functions it contains should be destroyed (ie: lose all references to it) along with the object. This requires proper coding discipline on the C++ side. It will also require that you use Lua environments to properly encapsulate each instance of a script, so that they can't sneak things out via globals. Lastly, you will need to have C++ maintain total control over when scripts are called and when they aren't.
Otherwise, ownership is something your scripters are simply going to have to know about and be careful of. They can't treat C++ parameters like any old Lua value.
If exercising disciplined programming practice is not possible or practical for you, then you will simply have to not pass Lua the actual C++ object. Instead, you need to pass Lua some proxy object, which is a reference to the original. boost::weak_ptr is a good example of such an object (though you wouldn't pass it exactly to Lua). The proxy would forward calls to the actual object. If the object has been deleted, the proxy would detect this and fail or do nothing or whatever.
I solved my issue the following way:
When im about to delete an object, i iterate through all lua functions from C++ (i have them in a list, they are bound to specific actor objects each). Then i inspect each upvalue (global/local vars accessable to a function) - then i compare the userdata pointer with my object im about to delete - if they match (and their classes) and NIL the upvalue. Optionally, i could just remove that offending function because it would not work well anymore anyway.
So the next the time the function is called, im just getting a soft lua error "trying to access xxx a nil value..." - no more access violations.
I know people would say "dont use lua_getupvalue/lua_setupvalue - they are only for debugging!" - but there is actually no documented or spoken side effect - and in my case its perfectly safe and works well - also there isnt the issue with left over proxy objects i could not delete.
I am using swig-lua. I have a function in Lua which I call and it returns me a pointer (userdata). right now I know what this pointer is, but how can I tell Lua from within Lua?
From the Lua Reference Manual:
setmetatable (table, metatable)
Sets the metatable for the given
table. (You cannot change the
metatable of other types from Lua,
only from C.)
You cannot "tell Lua" what a userdata is within Lua. It must be given a metatable or manipulated through bound function calls using the C API. See chapter 28.1 of Programming in Lua (Pil) for more information.
The very definition of userdata is that Lua does not, can not, and doesn't want to know what it is. It's your data- what it is is your problem. If you want to manipulate it, then you must call C functions with it (operator overloads available by metatable setting).
Tell SWIG about the data type pointed at by that void pointer. If SWIG is aware of the type, then it will pass it to Lua as a userdata with a suitable metatable attached that allows the Lua side to access and modify the individual data fields, (and if it is a class, call call its methods).
This might mean telling SWIG about some data types that aren't otherwise required by the library, but is probably worth the effort in the long run.
All Lua knows about what type a userdata is is contained in its metatable. Two userdata values are the same type if they have the same metatable. That metatable is responsible for mediating all access to the its content from the Lua side, and is usually made up of methods implemented in C so that is possible to do. Without such a metatable, then the Lua side can only treat a userdata as an opaque blob.