luacpp11 is a minimal C++11 lua binding library that tries to integrate with the design of the lua C API.
luacpp11 is designed to be used along with the lua C API. As such it doesn't
implement higher order abstraction like registration facilities for classes and
doesn't set up elaborate metatables for C++ types (it creates metatables but
only adds a __gc
metafunction). All the functionality is exposed through
function templates that mimic similar functions in the lua C API.
The library is header only and thus doesn't have to be compiled separately.
Since the library doesn't include any lua headers by itself it has to be
included after those. All public functions and types are placed into the
luacpp11
namespace.
push_callable
is an overloaded function that is used to push almost any
callable C++ object onto the lua stack. For function pointers (member and
non-member) and std::function
objects the return and argument types are
deduced automatically otherwise they have to be specified.
int foo(double arg) { ... }
struct A { void bar(); };
struct Ftor { int operator()(double arg); };
...
luacpp11::push_callable(L, foo);
luacpp11::push_callable(L, &A::bar);
luacpp11::push_callable(L, std::function<int(double)>(Ftor()));
luacpp11::push_callable<int(double)>(L, Ftor());
functions that have to extract the arguments themselves from the lua state
should take exactly one argument of type lua_State*
at the end of their
parameter list. Functions that handle the return values themselves should be
declared with return type luacpp11::luareturn
. Notice that pushing a
lua_CFunction
with push_callable
will not give the desired result since it
has return type int
(just keep using lua_pushcfunction
for that).
Multiple return values can also be returned to lua by returning a std::tuple
.
luacpp11::luareturn foo(int k, lua_State *L)
{
std::cout << lua_gettop(L) << " arguments" << std::endl;
lua_pushinteger(L, k);
return 1;
}
...
luacpp11::push_callable(L, foo); // ok
push
works the same way as the lua_pushXYZ
functions. It copy constructs
or moves its second argument into a userdata that is created on top of the lua
stack. The lifetime of these objects is then controlled by lua (a __gc
method
is added to their metatable). Notice that for pointers this means only that the
pointer itself is "collected" but it is not deleted.
Additionally objects can be constructed in place in userdata memory by use of
emplace
.
struct A { A(double pi) {...} };
...
A a(3.14159);
luacpp11::push(L, A); // copy constructs into a userdata
luacpp11::push(L, A(3.14159)); // move constructs into a userdata
luacpp11::emplace<A>(L, 3.14159); // emplace into a userdata
luacpp11::push(L, new A(3.14159)); // the lua garbage collector will not delete this!
luacpp11::push(L, &a); // which means this is ok too
is<T>
returns true if the object at a given index is of type T
. Notice that
that T
, const T
, T*
and const T*
are different types in this context
(&T
is interpreted as T
though).
luacpp11::emplace<A>(L);
luacpp11::is<A>(L, -1); // true
luacpp11::is<const A>(L, -1); // false
luacpp11::is<A*>(L, -1); // false
luacpp11::is<const A*>(L, -1); // false
luacpp11::is<B>(L, -1); // false
isconvertible<T>
returns true if the object at a given index is of type T
or
can be converted to it by to
.
luacpp11::emplace<A>(L);
luacpp11::isconvertible<A>(L, -1); // true
luacpp11::isconvertible<const A>(L, -1); // true
luacpp11::isconvertible<A*>(L, -1); // true
luacpp11::isconvertible<const A*>(L, -1); // true
luacpp11::isconvertible<B>(L, -1); // false
to<T>
retrieves a object of type T
from a given index and throws an exception
in case of a type mismatch. This function will "convert" from and to pointer
types as well as to const. Additionally it will also convert shared_ptr to pointer
or value types.
A a;
luacpp11::push(L, &a);
luacpp11::to<A>(L, -1); // ok
luacpp11::to<const A>(L, -1); // ok
luacpp11::to<A*>(L, -1); // ok
luacpp11::to<const A*>(L, -1); // ok
luacpp11::emplace<A>(L);
luacpp11::to<A>(L, -1); // ok
luacpp11::to<const A>(L, -1); // ok
luacpp11::to<A*>(L, -1); // ok
luacpp11::to<const A*>(L, -1); // ok
luacpp11::emplace<const A>(L);
luacpp11::to<A>(L, -1); // exception
luacpp11::to<const A>(L, -1); // ok
luacpp11::to<A*>(L, -1); // exception
luacpp11::to<const A*>(L, -1); // ok
toexact<T>
retrieves a object of type T
from a given index without trying to
perform any conversions.
A a;
luacpp11::push(L, &a);
luacpp11::toexact<A>(L, -1); // exception
luacpp11::toexact<const A>(L, -1); // exception
luacpp11::toexact<A*>(L, -1); // ok
luacpp11::toexact<const A*>(L, -1); // exception
tounchecked<T>
retrieves a object of type T
from a given index without
trying to perform any conversions or type checks. This may result in undefined
behavior if the object at the given stack index isn't of the correct type.
getmetatable<T>
pushes the metatable associated with T onto the stack. Again
T
, const T
, T*
and const T*
are different types in this context and will
all give different metatables. Comparing this metatable with the table returned
by lua_getmetatable
for a given object can be used to test wether that object
is of type T
(this is what is
does).
luacpp11::getmetatable< std::vector<int> >(L);
// luacpp11 only adds a __gc method to the metatable
lua_getfield(L, -1, "__gc");
if(lua_isfunction(L, -1))
std::cout << "found __gc function" << std::endl;
lua_pop(L, 2);
luacpp11::ref
holds a reference to any object in lua. ref
objects
can be obtained by using the to...
functions on any object on the
stack and pushed back into it without requiring knoweldge of the object
type. It internally uses the luaL_ref
mechanism, so it can also
be used to prevent collection of lua created objects.
luacpp11::newthread
should be used instead of lua_newthread
if
luacpp11 types are involved. It has the same behavior with the exception
that it allows luacpp11 to associate thread states with their original
states. Otherwise custom types will get their own meta table in each
state and userdata types created in one thread will not be recognized
in other threads that share the same global environment.
The register_hook
trait can be used to execute code whenever luacpp11 internally
creates a metatable for a given type. This happens once per type and lua_State
on first use of the type. This can be used to perform more elaborate type
registration (like adding metamethods etc.) on demand.
namespace luacpp11 {
template<class T>
struct register_hook< std::vector<T> > {
static void on_register(lua_State *L)
{
lua_pushstring(L, "__len");
// __len can have a second nil argument so we can't use ::size directly
luacpp11::push_callable<size_t(std::vector<T>*, lua_State*)>(L,
[](std::vector<T> *v, lua_State*){ return v->size(); }
);
lua_rawset(L, -3);
}
};
}
//...
luacpp11::emplace< std::vector<int> >(L, 13);
lua_setglobal(L, "vec");
luaL_dostring(L, "print(#vec)\n"); // prints vector size (13)