Continuing the post about lua integration with C++. Now to more serious stuff. Let’s try to write a wrapper for a std::list<int>
. Imagine that you have a std::list<int>
in your C++ that you want to share with the Lua enviroment. So both C++ and Lua can access the list.
Keep in mind, that I’m going to use a std::list<int>
as an example but you could apply the same idea to any other type: user-defined or builtin.
Basics
First, let’s get the Makefile
in place
1 2 3 4 5 6 |
|
Lua script
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
This script when executed will empty the_list
printing its contents and will fill it again with new content. That will illuestrate that Lua can access the underlyint std::list<int>
backing up the_list
.
Note that we use the colon notation to call methods on the_list
. So when I write arg:pop()
it’s tranlated to arg.pop(arg)
. The first argument to the function will be the object itself (Think on that argument like the implicit *this
in C++ methods or self
argument in python).
Note: that I wrote an iterator for the list in lua. That is a function that returns a closure. the for
will call this returned function over and over until it returns nil
.
The arg:pop()
will pop a element from the front of the std::list<int>
and will return nil
when the list is empty.
The C++ host application
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
|
The idea is to set up the basic lua enviroment thought the luaL_newstate and luaL_openlibs.
Then we create a metatable with luaL_newmetatable. A metatable is just a regular table that can be associated with lua values such as userdata. The metatable is where Lua goes to search for metamethods. You can see the list of available metamethods in Lua Reference. In this case we define the metamethod __index
, which is the metamethod used by Lua when in cannot find a given index in a table or userdata. So imagine that a
is a userdata and we type a.elem
. a
has no elem
in it so it invokes __index
on a’s metatable to see what to do. It’s kind of method_missing
in Ruby if you are familiar with Ruby. Now the __index
metamethod it’s a little bit special in the sense that I doesn’t need to be a method/function at all. If Lua finds out that the __index
field of the metatable is actually a table and not a function it will just use that table to find the key. So going back to a.elem
example, that will be translated to getmetatable(a)["__index"].elem
.
We will use the "ListMT"
metatable to hold the methods for lists. We associate the metatable entries for push
and pop
with two static C functions l_list_push
and l_list_pop
. This functions must be of type lua_CFunction
. that is they should take a lua_State *
as paramter and return an integer. That’s how the Lua communicates with C++, via the lua_State
and its stack.
The functions themselves are quite straighforward. They must be defined as extern "C"
because Lua is compiled as a C library and it will call all the lua_CFunction
with a C linkage (that determines the order in which the function parameters will be pushed into the machine’s stack, etc.) so we need to make sure that the function that we are generating here can be called from C.
The function are designed so that the first parameters is always the “object” in this case a userdata of type “ListMT”. The lua function luaL_checkudata will check that the metatable of the userdata matches and it will provide the pointer to the userdata. When Lua call the funciton the arguments to the functions are always pushed into the stack so that the first parameter lands on the stack position 1. So arguments are easy to address.
Finally the Lua resources are freed with lua_close.
Output
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Things to remember
- Understand userdata, metatables and metamethods
- Lua and C++ communicate though the Lua stack
- check the number of arguments with `assert(lua_gettop(L) == x);
- empty the lua stack or assert that it’s empty where do you know that the stack should be empty