luabind deboostified is a fork of the luabind project that helps exposing APIs to Lua. As the name implies, it replaces the boost dependency with modern C++, which makes it a lot more pleasant to work with.
Here are a few tips and tricks I learned while working with it. Some tricks might be applicable to the original luabind – I do not know.
1. Splitting module registration
You can split the registration code for different classes. I usually add a register function per class, like this:
struct A { void doSomething(); static luabind::scope registerWithLua(); }; struct B { void goodStuff(); static luabind::scope registerWithLua(); };
You can then combine their registration code into a single module on the Lua side:
void registerAll(lua_State* L) { luabind::module(L)[ A::registerWithLua(), B::registerWithLua()]; }
The implementation of a registration function looks like this:
luabind::scope A::registerWithLua() { return luabind::class_<A>("A") .def("doSomething", &A::doSomething); }
2. Multiple policies and multiple return values
Unlike C++, Lua has real multiple return values. You can use that by utilizing the return value policies that luabind offers. Lets say, you want to write this in Lua:
local x, y = a.getPosition()
The C++ side could look like this:
void getPosition(A const& a, float& x, float& y);
The deboostified fork needs its policies supplied in a type list. Let’s use a small helper meta-function to build that:
template <typename... T> using joined = typename luabind::meta::join<T...>::type;
Once you have that, you can expose it like this:
luabind::def("getPosition", &getPosition, joined< luabind::pure_out_value<2>, luabind::pure_out_value<3> >());
3. Specialized data structures using luabind::object
Using the converters in luabind is not the only way to make Lua values from C++. Almost everything you can do in Lua itself, you can do with luabind::object
. Here is a somewhat contrived example:
luabind::object repeat(luabind::object what, int count) { // Create a new table object auto result = luabind::newtable( what.interpreter()); // Fill it as an array [1..N] for (int i = 1; i <= count; ++i) result[i] = what; return result; }
This function can then be exported via luabind::def
and used just like any other function. This is just the tip of the iceberg, though. For example, you can also write functions that, at runtime, behave differently when a number is passed in as when a table is passed in. You can find out the Lua type with luabind::type(myObject)
.
Of course, as soon as you want to create new objects to return to Lua, you need the lua_State pointer in that function. Using the interpreter from a passed-in luabind::object is one way, but I have yet to find another pleasant way to do this. It is probably possible to use the policies to do this, and have them pass that in as a special parameter, but for now I am using some complicated machinery to bind lambda functions that capture the Lua interpreter.
That’s it for now..
Keep in mind that these are not thoroughly researched best-practices, but patterns I have used to solve actual problems. There might be better solutions out there – if you know any, please let me know. Hope this helped!