Hello!
I'm happy to announce Lua-cxx, a MIT-licensed, C++11 library I wrote to aid in writing modules and bindings for Lua.
# Why I wrote this thing
I loved using Lua in World of Warcraft, and I enjoyed working with Qt in C++, so I wanted to bring those two together. I also wanted to learn C++ templates, and I saw the potential in using them to push values on the stack.
# What Lua-cxx adds to Lua's C API
First of all, Lua-cxx doesn't provide a complete façade over Lua's C API. On the contrary, I find Lua's C API to be amazingly well-designed, so I've tried to ensure that Lua-cxx can be intermixed freely with Lua's C API. In fact, most of the Lua C API has no analog in Lua-cxx - I just use the original. ;)
// Add all arguments
int add_several(lua::state* const state)
{
// Get each argument
int sum = 0;
for (int i = 1; i <= lua_gettop(state); ++i) {
sum += lua::get<int>(state, i);
}
// Return the value
lua::push(state, sum);
lua_replace(state, 1);
return 1;
}
That being said, there are several places where Lua-cxx greatly simplify common tasks. For instance, Lua has a number of lua_push* functions that can be replaced
with Lua-cxx's lua::push template and appropriate specializations. You can extend this specialization with your own types, and Lua-cxx's other features will immediately support them.
C++11 added variadic templates, which can be used to provide a way to push a function of any arity into Lua without needing to write the marshalling code yourself or running a preprocessor:
// Standard C API is, of course, supported
int create_foo(lua::state* const);
lua::push(state, create_foo);
// Fundamental types work, too
int sum(int a, int b);
lua::push(state, sum);
// As are pointers to userdata and conversions to C++ strings.
void changeTitle(QWindow* window, const std::string& title);
lua::push(state, changeTitle);
// Even lambdas work too, with a bit of help
lua::push_function< int(int, int) >(state, [](int first, int second) {
return first + second;
});
Beyond this, Lua-cxx also has out-of-the-box support for Qt's QObject model, as well as rudimentary support for Gtk's GObject model, so you can push these types and their
properties and methods will automatically be exposed to Lua. You can then specialize further on a specific subtype to add behavior not provided by that library's metadata.
Here's a simple example of what can be done:
// Create a new Lua environment to play with.
auto env = lua::create();
// Introduce a global into Lua
env["foo"] = "No time";
// Run some Lua code directly
lua::run_string("assert(foo == 'No time')");
// Retrieve a global
auto value = env["foo"].get<std::string>();
And here's another, more complex variant that shows how a Lua module for Qt's QWindow class could be created:
#include <luacxx/stack.hpp>
#include <luacxx/type/standard.hpp>
#include <luacxx/type/function.hpp>
#include <QWindow>
int QWindow_new(lua::state* const state)
{
if (lua_gettop(state) > 1) {
auto parent = lua::get<QObject*>(state, 2);
lua_settop(state, 0);
if (parent) {
if (parent->inherits("QWindow")) {
lua::make<QWindow>(state, static_cast<QWindow*>(parent));
} else {
lua::make<QWindow>(state, static_cast<QScreen*>(parent));
}
return 1;
}
// Otherwise, fall through
}
// Create a QWindow within Lua
lua::make<QWindow>(state, static_cast<QWindow*>(nullptr));
return 1;
}
int luaopen_QWindow(lua::state* const state)
{
lua::thread env(state);
env["QWindow"] = lua::value::table;
env["QWindow"]["new"] = QWindow_new;
return 1;
}
// Within Lua
require "QWindow";
local window = QWindow:new();
window.width = 300;
window.height = 300;
window.title = "Hello, world";
window:show();
I've tried to document it well and keep things tidy. The source in total is almost 7,000 lines including documentation and unit tests. The documentation for each file can be formatted for easier viewing using the tiny bash script called cpod. It's in ./src. It just does a few regexes to make the source look like perldoc and calls that, so you'll need that too.
Thanks for reading; I hope you take a look! The documentation in src/stack.hpp or perhaps the unit tests in src/tests/core.cpp are good first places to start.
-- Aaron
P.S. I apologize in advance if my examples above get mangled by word-wrap. ;)