Привязка C++ к Lua
Лицензия Boost Software License.
Kaguya — это библиотека привязки Lua для C++.
добавьте каталог «kaguya/include» в «путь поиска заголовка» вашего проекта.
Или создайте один файл заголовка и добавьте его в свой проект.
cd utils
python generate_one_header.py > ../kaguya.hpp
# include " kaguya/kaguya.hpp "
int main ()
{
kaguya::State state;
state. dofile ( " /path/to/script.lua " );
}
extern " C " int luaopen_modulename (lua_State *L)
{
kaguya::State state (l);
kaguya::LuaTable module = state. newTable ();
module[ " function " ] = kaguya::function (somefunction);
return module. push (); // number of return lib
}
kaguya::State state;
state ( " a = 'test' " ); // load and execute from string
state.dofile( " path/to/luascript.lua " ); // load and execute from file
kaguya::LuaFunction f1 = state.loadfile( " path/to/luascript.lua " ); // load file without executing it
f1 (); // execute
kaguya::LuaFunction f2 = state.loadstring( " a = 'test' " ); // load string without executing it
f2 (); // execute
kaguya::State state;
state ( " a = " test " " );
std::string a_value = state[ " a " ];
assert (a_value == " test " );
state[ " tbl " ] = kaguya::NewTable(); // tbl ={};
state[ " tbl " ][ " value " ] = 1 ; // tbl.value = 1 in lua
state[ " tbl " ] = kaguya::TableData{ 23 , " test " ,{ " key " , " value " }}; // using initializer list(C++11)
state ( " assert(tbl[1] == 23) " );
state ( " assert(tbl[2] == 'test') " );
state ( " assert(tbl['key'] == 'value') " );
Тип LuaRef похож на тип Variant. Вы можете использовать его для хранения значения Lua в собственном коде.
kaguya::State state;
state[ " a " ] = " test " ;
kaguya::LuaRef a = state[ " a " ];
assert (a == " test " );
state[ " tbl " ] = kaguya::NewTable(); // tbl ={};
kaguya::LuaTable tbl = state[ " tbl " ]; // holding Lua Table
tbl[ " value " ] = 1 ; // tbl.value = 1 in lua
state ( " assert(tbl.value == 1) " );
int ret = state[ " math " ][ " abs " ](- 32 ); // call math.abs of Lua function
assert (ret == 32 );
// or
auto ret = state[ " math " ][ " abs " ].call< int >(- 32 ); // call math.abs of Lua function
assert (ret == 32 );
state ( " multresfun =function() return 1,2,4 end " ); // registering multiple results function
int a, b, c;
kaguya::tie (a, b, c) = state["multresfun"]();
assert (a == 1 && b == 2 && c == 4 );
// or
std::tuple< int , int , int > result_tuple = state[ " multresfun " ].call<std::tuple< int , int , int >>();
TEST_EQUAL (std::get< 0 >(result_tuple), 1);
TEST_EQUAL (std::get< 1 >(result_tuple), 2);
TEST_EQUAL (std::get< 2 >(result_tuple), 4);
struct ABC
{
ABC ():v_( 0 ) {}
ABC ( int value) :v_(value) {}
int value () const { return v_; }
void setValue ( int v) { v_ = v; }
void overload1 () {std::cout << " call overload1 " <<std::endl; }
void overload2 ( int ) {std::cout << " call overload2 " <<std::endl; }
private:
int v_;
};
state[ " ABC " ].setClass(kaguya::UserdataMetatable<ABC>()
.setConstructors<ABC(),ABC( int )>()
.addFunction( " get_value " , &ABC::value)
.addFunction( " set_value " , &ABC::setValue)
.addOverloadedFunctions( " overload " , &ABC::overload1, &ABC::overload2)
.addStaticFunction( " nonmemberfun " , [](ABC* self, int ){ return 1 ;}) // c++11 lambda function
);
-- Lua
abc = ABC . new () -- call default constructor
assert ( 0 == abc : get_value ())
abc = ABC . new ( 42 ) -- call (int) constructor
assert ( 42 == abc : get_value ())
abc : set_value ( 30 )
assert ( 30 == abc : get_value ())
abc : overload () -- call overload1
abc : overload ( 1 ) -- call overload2
struct Base
{
int a;
};
struct Base2
{
int a2;
};
struct Derived :Base
{
int b;
};
struct MultipleInheritance :Base,Base2
{
int b;
};
int base_function (Base* b) {
b-> a = 1 ;
return b-> a ;
}
//
kaguya::State state;
state[ " Base " ].setClass(kaguya::UserdataMetatable<Base>()
.addFunction( " a " , &Base::a)
);
state[ " Derived " ].setClass(kaguya::UserdataMetatable<Derived, Base>()
.addFunction( " b " , &Derived::b)
);
// can use kaguya::MultipleBase<BaseTypes...> for multiple inheritance class
state[ " MultipleInheritance " ].setClass(kaguya::UserdataMetatable<MultipleInheritance, kaguya::MultipleBase<Base, Base2> >()
.addFunction( " b " , &MultipleInheritance::b)
);
state[ " base_function " ] = &base_function;
Derived derived;
state[ " base_function " ](&derived); // Base arguments function
state ( " assert(1 == derived:a()) " ); // accessing Base member
state[ " ABC " ].setClass(kaguya::UserdataMetatable<ABC>()
.setConstructors<ABC(),ABC( int )>()
.addFunction( " get_value " , &ABC::value)
.addFunction( " set_value " , &ABC::setValue)
);
ABC abc ( 43 );
// register object pointer
state[ " abc " ] = &abc;
state ( " assert(43 == abc:get_value()) " );
// or copy instance
state[ " copy_abc " ] = abc;
state ( " assert(43 == copy_abc:get_value()) " );
// or registering shared instance
state[ " shared_abc " ] = kaguya::standard::shared_ptr<ABC>( new ABC( 43 )); // kaguya::standard::shared_ptr is std::shared_ptr or boost::shared_ptr.
state ( " assert(43 == shared_abc:get_value()) " );
state[ " Base " ].setClass(kaguya::UserdataMetatable<Base>());
Base base;
// registering pointer. lifetime is same base
state[ " b " ] = &base;
state[ " b " ] = kaguya::standard::ref(base);
// registering copy instance. copied instance lifetime is handling in lua vm(garbage collection).
state[ " b " ] = base;
state[ " b " ] = static_cast <Base const &>(base);
void c_free_standing_function ( int v){std::cout << " c_free_standing_function called: " << v << std::endl;}
state[ " fun " ] = &c_free_standing_function;
state[ " fun " ]( 54 ); // c_free_standing_function called:54
state ( " fun(22) " ); // c_free_standing_function called:22
state[ " lambda " ] = kaguya::function([]{std::cout << " lambda called " << std::endl;}); // C++11 lambda
state ( " lambda() " ); // lambda called
state[ " overload " ] = kaguya::overload(
[]( int ) {std::cout << " int version " << std::endl; },
[]( const std::string&) {std::cout << " string version " << std::endl; },
[]() {std::cout << " no arg version " << std::endl; }
);
state ( " overload() " ); // no args version
state ( " overload(2) " ); // int version
state ( " overload('2') " ); // string version
// free function
int defargfn ( int a = 3 , int b = 2 , int c = 1 )
{
return a*b*c;
}
KAGUYA_FUNCTION_OVERLOADS (defargfn_wrapper, defargfn, 0 , 3 )
state["defarg"] = kaguya::function(defargfn_wrapper());
state.dostring( " assert(defarg() == 6) " );
state.dostring( " assert(defarg(6) == 12) " );
state.dostring( " assert(defarg(6,5) == 30) " );
state.dostring( " assert(defarg(2,2,2) == 8) " );
// member function
struct TestClass
{
int defargfn ( int a = 3 , int b = 2 , int c = 1 )
{
return a*b*c;
}
};
KAGUYA_MEMBER_FUNCTION_OVERLOADS (defargfn_wrapper, TestClass, defargfn, 0 , 3 )
state["TestClass"].setClass(kaguya::UserdataMetatable<TestClass>()
.setConstructors<TestClass()>()
.addFunction( " defarg " , defargfn_wrapper())
);
state.dostring( " test = TestClass.new() " );
state.dostring( " assert(test:defargfn() == 6) " );
state.dostring( " assert(test:defargfn(6) == 12) " );
state.dostring( " assert(test:defargfn(6,5) == 30) " );
state.dostring( " assert(test:defargfn(2,2,2) == 8) " );
state[ " va_fun " ] = kaguya::function([](kaguya::VariadicArgType args) { for ( auto v : args) { std::cout << v. get <std::string>() << " , " ; }std::cout << std::endl; }); // C++11 lambda
state ( " va_fun(3,4,6, " text " ,6,444) " ); // 3,4,6,text,6,444,
Если тип возвращаемого значения функции — кортеж, она возвращает в Lua несколько результатов.
state[ " multireturn " ] = kaguya::function([]() { return std::tuple< int , int >( 32 , 34 ); });
state ( " print(multireturn()) " ); // 32 34
Luas nil
преобразуется в nullptr
или 0 для типов указателей, его можно проверить с помощью isNilref()
, и он отличается от целого числа 0
. Если вы хотите передать nil
в lua, либо передайте nullptr
/ (void*)0
, либо kaguya::NilValue
.
state[ " value " ] = kaguya::NilValue();
// or state["value"] = nullptr;
// or state["value"] = (void*) 0;
state ( " assert(value == nil) " );
state ( " assert(value ~= 0) " );
assert (state[ " value " ].isNilref());
assert (state[ " value " ] == kaguya::NilValue());
assert (state[ " value " ] == nullptr );
state[ " value " ] = 0 ;
state ( " assert(value ~= nil) " );
state ( " assert(value == 0) " );
assert (!state[ " value " ].isNilref());
assert (state[ " value " ] != kaguya::NilValue());
assert (state[ " value " ] != nullptr );
kaguya::LuaThread cor = state.newThread();
state ( " corfun = function(arg) "
" coroutine.yield(arg) "
" coroutine.yield(arg*2) "
" coroutine.yield(arg*3) "
" return arg*4 "
" end " ); // define corouine function
kaguya::LuaFunction corfun = state[ " corfun " ]; // lua function get
// exec coroutine with function and argument
std::cout << int (cor(corfun, 3 )) << std::endl; // 3
std::cout << int (cor()) << std::endl; // 6
// resume template argument is result type
std::cout << cor.resume< int >() << std::endl; // 9
std::cout << int (cor()) << std::endl; // 12
kaguya::LuaThread cor2 = state.newThread();
// 3,6,9,12,
while (!cor2.isThreadDead())
{
std::cout << cor2. resume < int >(corfun, 3 ) << " , " ;
}
std::map и std::vector по умолчанию будут преобразованы в lua-таблицу.
kaguya::State s;
std::vector< int > vect = { 2 , 4 , 6 , 1 };
s[ " vect " ] = vect;
s ( " print(type(vect)) " ); // table
s ( " for i,v in ipairs(vect) do print(v) end " );
выход:
table
1 2
2 4
3 6
4 1
kaguya::State s;
std::map<std::string, int > map = { { " apple " , 3 },{ " dog " , 5 },{ " cat " , 124 },{ " catfood " , 644 } };
s[ " map " ] = map;
s ( " print(type(map)) " ); // table
s ( " for i,v in pairs(map) do print(i,v) end " );
выход:
table
catfood 644
dog 5
cat 124
apple 3
Если вы хотите настроить преобразование типов из/в lua, специализируйтесь на kaguya::lua_type_traits.
пример: (это уже реализовано для std::string по умолчанию)
template <> struct lua_type_traits <std::string> {
typedef std::string get_type;
typedef const std::string& push_type;
static bool strictCheckType (lua_State* l, int index)
{
return lua_type (l, index ) == LUA_TSTRING;
}
static bool checkType (lua_State* l, int index)
{
return lua_isstring (l, index ) != 0 ;
}
static get_type get (lua_State* l, int index)
{
size_t size = 0 ;
const char * buffer = lua_tolstring (l, index , &size);
return std::string (buffer, size);
}
static int push (lua_State* l, push_type s)
{
lua_pushlstring (l, s. c_str (), s. size ());
return 1 ;
}
};
Встречающиеся ошибки Lua по умолчанию будут записываться в консоль, но вы можете это изменить:
void HandleError ( int errCode, const char * szError)
{ // customize your error handling, eg. write to file...
}
kaguya::State l;
l.setErrorHandler(HandleError);
l.dofile( " ./scripts/custom.lua " ); // eg. accessing a non-existing file will invoke HandleError above
mkdir build
cd build
cmake ..
make
ctest
Если вы не хотите использовать библиотеку по умолчанию (системную), добавьте эти 3 параметра в команду cmake.
cmake -DLUA_INCLUDE_DIRS=path/to/lua/header/dir -DLUA_LIBRARY_DIRS=/abspath/to/lua/library/dir -DLUA_LIBRARIES=lualibname