Binding Lua to C++: Think Twice Before Eating That Glue

by Nox

on 08.12.12

Lua fanboys would have you believe that the process of binding C/C++ to Lua is a metaphysical experience, steeped in satyr semen and fairy farts, but the reality is far less magical. The Lua C API is hardly a “pleasure to work with”, “so easy a child could do it,” or “extremely easy to integrate.” It’s definitely better than the Python API, but that doesn’t mean that the Lua C API is good, it just means that it’s better than some other shit. Topping shit with Nutella doesn’t make it taste good—there’s shit in your mouth!

I've read through some parts of Programming in Lua more times than I care to count, but only very recently have I ventured into even attempting to bind C++ to Lua by hand. As one would expect, the level at which two languages coalesce is going to be a rather dirty place. The API is undeniably daunting, no matter what everyone else seems to think. And libraries that aim to simplify and abstract the API have made it easy to do something that, in many cases, shouldn’t be done at all. They do make binding significantly less painful, but I don’t think that’s always a good thing. If shooting heroin weren’t intimidating, more people would OD. In the same way, there’s an abundance of pretty horrible uses of Lua out in the wild, due in large part to the ease afforded by binding libraries.

Knowing that most open source projects suck more dick than a vietnamese ladyboy inspires great caution in my choice of what libraries I will leverage and what libraries I can do without. With this in mind, exploring the existing libraries for binding Lua yielded no surprises. Not only do they suck, but in my pursuit of creating something better in C++11, I discovered that they will always suck, by the very nature of what it is that they're doing—abstracting shitloads of suboptimal glue code.

If you feel like you need the help of an automated binding library, you're approaching Lua in the wrong way. Binding Lua in non-trivial ways is hard for a reason, because it’s an unavoidably difficult problem. This difficulty should be an inspiration to you, not to cover the problem with clever template metaprogramming and macros, but rather to be a little less clever, and to limit the surface area of the binding in the first place.

C and C++ operate in a completely different modality than any scripting language, and necessarily converting between these modalities at every binding point can be both expensive and counterproductive. It seems obvious to me, in retrospect, that trying to fit the square peg of dynamic typing into the round hole of static typing should be avoided whenever possible. The question isn’t “can it be done?” as much as “should it be done?” Programming in Lua can, among other things, decrease iteration times, provide an in-game REPL, and allow for live reloading of scripts. For a small team though, where knowledge of the code base is high and builds are trivial, I'm not sure that a scripting language adds enough to justify its inclusion, except to satisfy the status quo. Additionally, those who think that scripting languages have a low enough barrier to entry to allow artists and designers to make meaningful contributions are kidding themselves.

I'm not saying, “Don’t integrate scripting,” as much as I'm saying “Be very cautious when integrating scripting.” You must carefully plan your binding points and limit their scope immensely if you desire to have anything remotely performant or maintainable. Also, in general, don’t bother trying to make Lua understand C types. In Lua parlance, strongly prefer light userdata to full userdata. You would be best served by tailoring an appropriate abstraction of your core engine to bind on a per game basis, rather than trying to build a comprehensive binding that forces Lua into being no more than just a naive interpreted wrapper around the compiled core. Bitsquid has done the latter, but it’s still the cleanest binding I've ever personally seen.

Sidestepping the Lua C API with automatic binding generators and helper libraries is a bit like gouging your eyes out with a rusty teaspoon, while direct use of the Lua C API is rather like being crucified. You'll never get your eyes back, but if you're tacked to a cross, there’s a chance you'll rise again, in just 3 days, as a god.