Selene
SEEKING NEW MAINTAINER
Selene would love to have a new home :) The project makes it very simple to interface from C++ to Lua but needs some love to manage pull requests, issues, Lua compatibility, and other improvements. I intend on creating a separate organization for Selene that I can continue to sit in and weigh in feedback as I have time, but will allow for multiple contributors. Please let me know if you're interested by emailing [email protected]. Of course, previous and current active maintainers will go to the front of the line, but everyone will be considered!
Best, Jeremy
Simple C++11 friendly header-only bindings to Lua 5.1+.
Requirements
- Cmake 2.8+
- Lua 5.1+
- C++11 compliant compiler
Usage
Selene is a headers-only library so you just need to include "selene.h" to use this project.
To build the tests, do the following:
mkdir build
cd build
cmake ..
make
This will build a test_runner
executable that you can run. If you wish to
include Lua from another location, you made pass the LUA_INCLUDE_DIR
option
to cmake (i.e. cmake .. -DLUA_INCLUDE_DIR=/path/to/lua/include/dir
).
Usage
Establishing Lua Context
using namespace sel;
State state; // creates a Lua context
State state{true}; // creates a Lua context and loads standard Lua libraries
When a sel::State
object goes out of scope, the Lua context is
automatically destroyed in addition to all objects associated with it
(including C++ objects).
Accessing elements
-- test.lua
foo = 4
bar = {}
bar[3] = "hi"
bar["key"] = "there"
sel::State state;
state.Load("/path/to/test.lua");
assert(state["foo"] == 4);
assert(state["bar"][3] == "hi");
assert(state["bar"]["key"] == "there");
When the []
operator is invoked on a sel::State
object, a
sel::Selector
object is returned. The Selector
is type castable to
all the basic types that Lua can return.
If you access the same element frequently, it is recommended that you cache the selector for fast access later like so:
auto bar3 = state["bar"][3]; // bar3 has type sel::Selector
bar3 = 4;
bar3 = 6;
std::cout << int(bar3) << std::endl;
Calling Lua functions from C++
-- test.lua
function foo()
end
function add(a, b)
return a + b
end
function sum_and_difference(a, b)
return (a+b), (a-b);
end
function bar()
return 4, true, "hi"
end
mytable = {}
function mytable.foo()
return 4
end
sel::State state;
state.Load("/path/to/test.lua");
// Call function with no arguments or returns
state["foo"]();
// Call function with two arguments that returns an int
// The type parameter can be one of int, lua_Number, std::string,
// bool, or unsigned int
int result = state["add"](5, 2);
assert(result == 7);
// Call function that returns multiple values
int sum, difference;
sel::tie(sum, difference) = state["sum_and_difference"](3, 1);
assert(sum == 4 && difference == 2);
// Call function in table
result = state["mytable"]["foo"]();
assert(result == 4);
Note that multi-value returns must have sel::tie
on the LHS and not std::tie
. This will create a sel::Tuple
as
opposed to an std::tuple
which has the operator=
implemented for
the selector type.
Calling Free-standing C++ functions from Lua
int my_multiply(int a, int b) {
return (a*b);
}
sel::State state;
// Register the function to the Lua global "c_multiply"
state["c_multiply"] = &my_multiply;
// Now we can call it (we can also call it from within lua)
int result = state["c_multiply"](5, 2);
assert(result == 10);
You can also register functor objects, lambdas, and any fully
qualified std::function
. See test/interop_tests.h
for details.
Accepting Lua functions as Arguments
To retrieve a Lua function as a callable object in C++, you can use
the sel::function
type like so:
-- test.lua
function add(a, b)
return a+b
end
function pass_add(x, y)
take_fun_arg(add, x, y)
end
int take_fun_arg(sel::function<int(int, int)> fun, int a, int b) {
return fun(a, b);
}
sel::State state;
state["take_fun_arg"] = &take_fun_arg;
state.Load("test.lua");
assert(state["pass_add"](3, 5) == 8)
The sel::function
type is pretty much identical to the
std::function
type excepts it holds a shared_ptr
to a Lua
reference. Once all instances of a particular sel::function
go out
of scope, the Lua reference will automatically become unbound. Simply
copying and retaining an instance of a sel::function
will allow it
to be callable later. You can also return a sel::function
which will
then be callable in C++ or Lua.
Running arbitrary code
sel::State state;
state("x = 5");
After running this snippet, x
will have value 5 in the Lua runtime.
Snippets run in this way cannot return anything to the caller at this time.
Registering Classes
struct Bar {
int x;
Bar(int x_) : x(x_) {}
int AddThis(int y) { return x + y; }
};
sel::State state;
state["Bar"].SetClass<Bar, int>("add_this", &Bar::AddThis);
bar = Bar.new(5)
-- bar now refers to a new instance of Bar with its member x set to 5
x = bar:add_this(2)
-- x is now 7
bar = nil
--[[ the bar object will be destroyed the next time a garbage
collection is run ]]--
The signature of the SetClass
template method looks like the
following:
template <typename T, typename... CtorArgs, typename... Funs>
void Selector::SetClass(Funs... funs);
The template parameters supplied explicitly are first T
, the class
you wish to register followed by CtorArgs...
, the types that are
accepted by the class's constructor. In addition to primitive types,
you may also pass pointers or references to other types that have been
or will be registered. Note that constructor overloading
is not supported at this time. The arguments to the SetClass
function are a list of member functions you wish to register (callable
from Lua). The format is [function name, function pointer, ...].
After a class is registered, C++ functions and methods can return pointers or references to Lua, and the class metatable will be assigned correctly.
Registering Class Member Variables
For convenience, if you pass a pointer to a member instead of a member function, Selene will automatically generate a setter and getter for the member. The getter name is just the name of the member variable you supply and the setter has "set_" prepended to that name.
// Define Bar as above
sel::State state;
state["Bar"].SetClass<Bar, int>("x", &Bar::x);
-- now we can do the following:
bar = Bar.new(4)
print(bar:x()) -- will print '4'
bar:set_x(-4)
print(bar:x()) -- will print '-4'
Member variables registered in this way which are declared const
will not have a setter generated for them.
Registering Object Instances
You can also register an explicit object which was instantiated from C++. However, this object cannot be inherited from and you are in charge of the object's lifetime.
struct Foo {
int x;
Foo(int x_) : x(x_) {}
int DoubleAdd(int y) { return 2 * (x + y); }
void SetX(int x_) { x = x_; }
};
sel::State state;
// Instantiate a foo object with x initially set to 2
Foo foo(2);
// Binds the C++ instance foo to a table also called foo in Lua along
// with the DoubleAdd method and variable x. Binding a member variable
// will create a getter and setter as illustrated below.
// The user is not required to bind all members
state["foo"].SetObj(foo,
"double_add", &Foo::DoubleAdd,
"x", &Foo::x);
assert(state["foo"]["x"]() == 2);
state["foo"]["set_x"](4);
assert(foo.x == 4);
int result = state["foo"]["double_add"](3);
assert(result == 14);
In the above example, the functions foo.double_add
and foo.set_x
will also be accessible from within Lua after registration occurs. As
with class member variables, object instance variables which are
const
will not have a setter generated for them.
Writeups
You can read more about this project in the three blogposts that describes it:
There have been syntax changes in library usage but the underlying concepts of variadic template use and generics is the same.
Roadmap
The following features are planned, although nothing is guaranteed:
- Smarter Lua module loading
- Hooks for module reloading
- VS support