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!