3

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?

Resantic
  • 57
  • 1
  • 7

2 Answers2

2

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.

Vlad
  • 5,450
  • 1
  • 12
  • 19
  • Thank you, didn't understand the last part though. "But changing content of mutable value (table etc) is ok, since registry and variable will refer same value." What do you mean? – Resantic Oct 22 '16 at 11:34
  • There's immutable values - numbers, string, booleans. You can't change content of that value, you can only assign different value to variable. And there's mutable values - tables, lexical closures. When you change something in those values - assign new index, update upvalue - you don't change the binding of that entire table/closure. Variable or table element (like registry) only store reference to actual value. That's not always true, but you can safely assume there's only references in variables and table elements. Binding new value to a table element doesn't touch registry's link to table. – Vlad Oct 22 '16 at 13:20
2

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.

  1. 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!

  2. 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.

  • Regarding the performance, the luaL_ref/luaL_unref is more expensive than lua_rawsetp, but lua_rawgeti is usually less but could be the same as lua_rawgetp. It's important to point out that you don't need to create a static variable to use with lua_rawsetp, you just need to have a pointer. Generally if you have some object to store or lookup in the registry you have memory that holds the object and so you can use that address. In fact just about whenever you would store the result from luaL_ref you could use the address of the storage instead in lua_rawsetp and then store something else. – Parakleta Feb 07 '17 at 00:16
  • @Parakleta it still depends. If you gonna use `lua_rawsetp` all the time, and objects in your application is created and deleted frequently, you waste a lot of memory and CPU time in regard of garbage collection, while value storage accessed via `luaL_ref` / `luaL_unref` is reused. – Vyacheslav Napadovsky Feb 07 '17 at 08:54
  • `lightuserdata`, as used for the key in `lua_rawsetp`, is not garbage collected so there is no difference there. Regarding resize operations on the table I think it is hard to predict which will be worse. In terms of overall memory usage, having an integer to store the result of `luaL_ref` could cost up to 8 bytes extra per item on 64 bit platform with alignment considerations. – Parakleta Feb 08 '17 at 00:52
  • @Parakleta as "more memory" I mean "more slots in hashed part of the table (malloc) are created to hold values with pointer keys". – Vyacheslav Napadovsky Feb 08 '17 at 09:02
  • 1
    I had overlooked that the keys aren't stored in the array part, so in that case the slots in the hashed part are larger by 16 bytes on a 64 bit system so you are correct that it uses more memory, 16 bytes for the value regardless, and the 4-8 bytes for the key using `luaL_ref` vs 16 bytes for the key using `lua_rawsetp`. – Parakleta Feb 09 '17 at 02:15