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.
First, let’s get the
Makefile in place
1 2 3 4 5 6
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
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
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
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 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
We will use the
"ListMT" metatable to hold the methods for lists. We associate the metatable entries for
pop with two static C functions
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.
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