2

I am using Lua in a very resource constrained system, in conjunction with C.

A library that I am using creates some references (as pointers) to the objects it creates which are useful for later access to these objects. In order to expose this library's functionality to Lua, when such an object is created, this reference is returned to the Lua script.

The user has the ability to store this reference to their liking, as this makes later calls very convenient.

Example cases:

ref = MyLib.createObject("some", arguments)
local ref = MyLib.createObject("some", arguments)
table_of_refs[45] = MyLib.createObject("some", arguments)
-- etc...

Unfortunately these references may be destroyed outside of the Lua script's scope (from within C). Thus these references may become invalid.

For the time, my code can handle these invalid references with no problem. All these pointers are validated before actual use in the library, so the code is safe.

However it seems a bit confusing on the Lua user's perspective, since there is no way to tell if this reference is still valid or not (this is not very important but still I want to improve it).

What I want is the following. I want to iterate from C all lightuserdata that Lua has stored. If the lightuserdatum is not valid anymore, set it to nil. This way, on next usage the variable will be either valid or nil, providing a much better API to users.

Is there any way to achieve this? Can I iterate from C all lightuserdata that Lua knows about (regardless of where they are stored, local / global / table etc), and modify them?

2 Answers2

2

There is no way to access local lua variables from C. So even if you did iterate over a certain type of lua object, you wouldn't have access to at least one area (the local variables).

You can iterate through everything in the global table, as shown in Max's answer here: Loop through all Lua global variables in C++

If you were to test against lua_islightuserdata and then compare the value of the lightuserdata to the pointer that you are deleting, you should be able to recognize what global values you want.

lua_pushglobaltable(L); 
lua_pushnil(L);    
while (lua_next(L,-2) != 0) 
{
    if (lua_islightuserdata(L, -1))
    {
        //TODO: compare against the lightuserdata pointer value you are looking for
        void* mypointer = lua_touserdata(state, -1);

        name = lua_tostring(L,-2);  // Get key(-2) name
        //TODO: do whatever with the value; probably set to nil by name in the global table
    }
    lua_pop(L,1);
}
lua_pop(L,1); 

If you want to search for it in tables, however, you will have to include an additional check of lua_istable and then recursively iterate over the values of the table in the same way.

...
else if (lua_istable(L, -1)
{
    //TODO: iterate each value in the table searching for lightuserdata
    // or tables with further levels of recursion
}
...

I think, however, it would be far more direct to provide a method called "isValid" on MyLib which returns whether the value is still valid or not before using it.

local ref = MyLib.createObject("some", arguments)
...
local isvalid = MyLib.isValid(ref)

Then you aren't limited by the local scope. Also, assuming that you would be doing a nil check anyway in your lua code (to see if the value got changed out from under you), this costs you basically nothing in terms of code.

avariant
  • 2,234
  • 5
  • 25
  • 33
  • Sorry, "MyLib" is a poor choice for name... In fact it is not my library rather a 3rd party library, so I want to avoid any modifications. But I got the point. – Fotis Panagiotopoulos Apr 02 '19 at 08:07
0

I think you are on the wrong way in concept already.

How comes that an object created can be destroyed from within C? It was the Lua script that triggered creation, so it should be the Lua script, too, that triggers destruction.

Instead of destroying the object, you might just set a flag indicating the object's invalid state. You might additionally implement a callback mechanism so that the C library can inform the Lua script about objects being invalidated.

If there's a lot of data to be maintained and you want to be able to reuse the memory as soon as possible, then the Lua object might just be a wrapper around a pointer to the true data. Then you could delete the data independently and set the pointer to NULL, which would at the same time serve as flag for being invalid.

Be aware that checking the pointer validity only by pointer value itself can badly fail:

SomeStruct* ptr = malloc(sizeof(SomeStruct));
// don't forget if(!ptr) error handling

SomeStruct* copy = ptr; // here C only, but might be stored in Lua!

SomeStruct* newPtr = malloc(sizeof(SomeStruct));
// by accident same address re-used as ptr once had!!!

if(isValid(copy))
{
    // but the struct originally referenced died long ago...
}
Aconcagua
  • 24,880
  • 4
  • 34
  • 59