Interfacing Lua With Templates in C++11 Conclusion
The code for this project is hosted here
This is the final writeup on my personal attempt to abstract the interface between Lua and modern C++11. The first part discussed the implementation of calling Lua functions from C++. The second part tackled the inverse problem, binding and calling C++ functions from Lua. To conclude the series, I will discuss the approach I take to binding C++ classes to Lua. Because many of the template metaprogramming methods were already discussed in the other two parts, this will be the shortest one of the series.
Lua as a language does not have objects built-in. Rather, it provides a powerful abstraction via tables and metatables. While it is possible to come up with elaborate schemes for mapping objects to Lua tables to support polymorphism, inheritance, and other OO concepts, this would likely require the Selene library to be bundled with a Lua package which is not the goal of the project (currently). As such, the implementation discussed here and provided by Selene opts for the simplest mapping possible. The hope is that this functionality will be sufficient for any user of Selene to implement whatever abstraction they may need on top of it with relative ease.
Following the pattern of the previous posts, we will first consider what the interface will look like as a guide.
To keep things simple, we’ll allow the user to register specific instances of C++ objects. If the user wishes to instantiate C++ objects from Lua, it is simple to expose a C++ function to do so. Thus, we will not handle construction/destruction and C++ object lifetime which may unecessarily complicate usage of the library. The class we will try to bind to Lua is the following:
1 2 3 4 5 6 7
C++ does not support any sort of class reflection so we have no choice but to have the user specify which member functions are to be exposed. This isn’t too problematic as the user may want to name the functions differently in the Lua context anyways.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Register function is overloaded to accept an name to bind an
instance to in Lua, a reference to the instance, and a variadic list
of alternating function names and pointers.
Tackling member functions
The first thing we need is a
sel::ClassFun object which allows us to call
member functions from Lua. The
sel::ClassFun class is actually very
similar to the
the only distinction being that instead of being bound to a global,
sel::ClassFun is bound to a field of our instance table. The
syntax for doing this is
lua_setfield(l, index, name) where
is the position on the stack where the table resides and
name is the
name of the field we wish to bind the function to. Recall that the way
sel::Fun worked was by binding a closure with a pointer to the
sel::Fun instance as an upvalue. The same approach works here.
Next, we’ll need a
sel::Class object to contain the instances of
sel::ClassFun. The skeleton of the class looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
To implement the member function registration, let’s start from the
bottom by providing a function which registers only a single function
1 2 3 4 5 6 7 8 9 10 11 12 13
The usage of the lambda allows us to convert a member function to a
non-member function by binding it to the pointer to the instance,
This is what allows us to leverage existing code used to describe
detail::_arity struct is simply a type trait struct
which looks at the return type to deduce how many values the function
is expected to return (0 in case of void, 1 in case of a single value,
or N in case of a size-N tuple). Its definition is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Since the user may opt to bind multiple member functions, we’ll need
to provide a function that recusively unpacks the arguments while
making call to
sel::Class::_register_fun. Call it
sel::Class::_register_funs for simplicity.
1 2 3 4 5 6 7 8 9 10
This is a slightly different pattern from what we’ve seen before.
Notice that the function will recursively peel of two arguments at a
time from the parameter pack, expecting the pack to be of the form
const char * and
F alternating. Code that attempts to call this
function with an odd number of arguments will fail to compile.
_register_funs function, implementing the constructor of
sel::Class is fairly trivial, requiring a call to
and a call to
The only thing left is to actually register the class with our
sel::State object. Because the function requires a reference to the
object instance, we can overload our existing
1 2 3 4 5 6 7 8
An interesting point to note is that in declaring a new instance of
sel::Class, the type arguments
Funs... needed to be passed
explicitly because template arguments cannot be deduced from
arguments passed to the constructor.
And that’s a wrap for this series! If you’ve been following along, you
now have a reasonably complete understanding of how
Selene is implemented, sans a
few more abstractions that have made the implementation easier in some
cases. The library is still a work in progress, but it is slim enough
that you should feel comfortable using it, experimenting with it, and
perhaps adding your own functionality. The main takeaways are that
clean interfaces can take a lot of work but they can be are absolute
time-savers in the long wrong by abstracting away much unecessary
boilerplate. If you come across similar boilerplate in the wild,
hopefully, some of the tools and patterns presented in this series
will help you. As always, feel free to leave a comment or question.