C++-Bindung an Lua
Lizenziert unter der Boost-Softwarelizenz
Kaguya ist eine Lua-Bindungsbibliothek für C++
Fügen Sie das Verzeichnis „kaguya/include“ zum „Header-Suchpfad“ Ihres Projekts hinzu.
Oder generieren Sie eine einzelne Header-Datei und fügen Sie sie Ihrem Projekt hinzu.
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') " );
Der LuaRef-Typ ähnelt einem Variant-Typ. Sie können es zum Speichern eines Lua-Werts in nativem Code verwenden.
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,
Wenn der Rückgabetyp der Funktion ein Tupel ist, gibt sie mehrere Ergebnisse an Lua zurück
state[ " multireturn " ] = kaguya::function([]() { return std::tuple< int , int >( 32 , 34 ); });
state ( " print(multireturn()) " ); // 32 34
Luas nil
konvertiert in nullptr
oder 0 für Zeigertypen, kann mit isNilref()
überprüft werden und unterscheidet sich von der Ganzzahl 0
. Wenn Sie nil
an Lua übergeben möchten, übergeben Sie entweder nullptr
/ (void*)0
oder 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 und std::vector werden standardmäßig in eine Lua-Tabelle konvertiert
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 " );
Ausgabe:
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 " );
Ausgabe:
table
catfood 644
dog 5
cat 124
apple 3
Wenn Sie die Typkonvertierung von/nach Lua anpassen möchten, spezialisieren Sie sich auf kaguya::lua_type_traits
Beispiel: (dies ist standardmäßig bereits für std::string implementiert)
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 ;
}
};
Festgestellte Lua-Fehler werden standardmäßig in die Konsole geschrieben, Sie können dies jedoch ändern:
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
Wenn Sie nicht die Standardbibliothek (Systembibliothek) verwenden möchten, fügen Sie diese drei Optionen zum Befehl cmake hinzu
cmake -DLUA_INCLUDE_DIRS=path/to/lua/header/dir -DLUA_LIBRARY_DIRS=/abspath/to/lua/library/dir -DLUA_LIBRARIES=lualibname