Created
February 20, 2014 13:17
-
-
Save moteus/9113307 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* vim: set et sw=3 tw=0 fo=croqlaw cino=t0: | |
* | |
* Luaxx, the C++ Lua wrapper library. | |
* Copyright (c) 2006-2007 Matthew A. Nicholson | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to | |
* deal in the Software without restriction, including without limitation the | |
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
* sell copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
* IN THE SOFTWARE. | |
*/ | |
#ifndef LUAXX_H | |
#define LUAXX_H | |
#include <lua.hpp> | |
#include <string> | |
#include <vector> | |
#include <new> | |
#include <exception> | |
/** @mainpage Luaxx | |
* | |
* Luaxx is a thin wrapper around the Lua C API. The wrapper adds some | |
* convience functions and intergrates well with modern C++. This is a header | |
* only library and has no runtime dependencies besides Lua itself. | |
* | |
* Luaxx is not designed like toLua, instead Luaxx is more of a 1 to 1 | |
* logical mapping of the lua API in C++. For example: in C you would write | |
* 'lua_pushnumber(L, 3)', in C++ with Luaxx you would write | |
* 'L.push(3)'. | |
* | |
* Every thing is contained in the 'lua' namespace and exceptions are thrown | |
* when a lua API function returns an error. Most of the functionality is | |
* contained in the lua::state class, which can be passed directly to lua C API | |
* functions (the compiler will automatically use the internal lua_State | |
* pointer). See the documentation for that class for more information. | |
*/ | |
namespace lua | |
{ | |
template <typename T> | |
struct T_IS_LARGE{ | |
enum{value = (sizeof(long long)>sizeof(T))?0:1}; | |
}; | |
template < int V, typename T0, typename T1> | |
struct SELECT_TYPE; | |
template < typename T0, typename T1> | |
struct SELECT_TYPE<0,T0,T1> {typedef T0 type;}; | |
template < typename T0, typename T1> | |
struct SELECT_TYPE<1,T0,T1> {typedef T1 type;}; | |
template <typename T> | |
struct T_REF{ | |
typedef typename SELECT_TYPE<T_IS_LARGE<T>::value, T, const T&>::type const_reference; | |
typedef T& reference; | |
}; | |
/* forward declaration*/ | |
class state; | |
class reader{ | |
friend class lua::state; | |
static const char* lreader (lua_State *L, void *ud, size_t *sz){ | |
reader *this_ = reinterpret_cast<reader*>(ud); | |
return this_->next_chunk(L, sz); | |
} | |
public: | |
virtual ~reader(){} | |
virtual const char* next_chunk(lua_State *L, size_t *sz) = 0; | |
}; | |
class writer{ | |
friend class lua::state; | |
static int lwriter(lua_State *L, const void* p, size_t sz, void* ud){ | |
writer *this_ = reinterpret_cast<writer*>(ud); | |
return this_->next_chunk(L, p, sz ); | |
} | |
public: | |
virtual ~writer(){} | |
virtual int next_chunk(lua_State*L, const void* ptr, size_t sz) = 0; | |
}; | |
template<class T> | |
class vector_writer_basic : public lua::writer{ | |
enum{MIN_DELTA = 32}; | |
typedef vector_writer_basic<T> class_type; | |
public: | |
vector_writer_basic(std::vector<T> &b):buf(b){ | |
buf.clear(); | |
} | |
vector_writer_basic(const class_type& rhs):buf(rhs.buf){} | |
~vector_writer_basic(){ | |
if(false) typedef int size_must_be_one_byte[(sizeof(T) == 1)?1:0]; | |
} | |
int next_chunk(lua_State*L, const void *ptr, size_t sz){ | |
try{ | |
size_t req_sz = buf.size() + sz; | |
size_t new_sz = buf.capacity(); | |
while(new_sz < req_sz){ | |
new_sz += std::max<size_t>(MIN_DELTA, new_sz >> 1); // size *= 1.5 | |
} | |
buf.reserve(new_sz); | |
} | |
catch(const std::bad_alloc&){ | |
return LUA_ERRMEM; | |
} | |
const T *be = (const T*)ptr; | |
const T *en = be + sz; | |
buf.insert(buf.end(), be, en); | |
return 0; | |
} | |
private: | |
std::vector<T> &buf; | |
}; | |
template<class T> | |
vector_writer_basic<T> vector_writer(std::vector<T>& v){ | |
return vector_writer_basic<T>(v); | |
} | |
/** A generic lua exception. | |
*/ | |
class exception : public std::exception { | |
public: | |
/// Constructor. | |
exception() : std::exception() { } | |
/// Constructor. | |
explicit exception(const char* desc) : std::exception(), description(desc) { } | |
virtual ~exception() throw() { } | |
/** Get a description of the error. | |
* @returns a C-string describing the error | |
*/ | |
virtual const char* what() const throw() { | |
return description.c_str(); | |
} | |
private: | |
std::string description; | |
}; | |
/** A lua runtime error. | |
* This is thrown when there was an error executing some lua code. | |
* @note This is not an std::runtime error. | |
*/ | |
class runtime_error : public exception { | |
public: | |
/// Constructor. | |
runtime_error() : exception() { } | |
/// Constructor. | |
explicit runtime_error(const char* desc) : exception(desc) { } | |
virtual ~runtime_error() throw() { } | |
}; | |
/** A syntax error. | |
*/ | |
class syntax_error : public exception { | |
public: | |
/// Constructor. | |
syntax_error() : exception() { } | |
/// Constructor. | |
explicit syntax_error(const char* desc) : exception(desc) { } | |
virtual ~syntax_error() throw() { } | |
}; | |
/** An error loading a lua file. | |
* This is thrown when a call to lua::loadfile failed because the file could | |
* not be opened or read. | |
*/ | |
class file_error : public exception { | |
public: | |
/// Constructor. | |
file_error() : exception() { } | |
/// Constructor. | |
explicit file_error(const char* desc) : exception(desc) { } | |
virtual ~file_error() throw() { } | |
}; | |
/** A memory allocation error. | |
*/ | |
class bad_alloc : public exception, std::bad_alloc { | |
public: | |
/// Constructor. | |
bad_alloc() : lua::exception(), std::bad_alloc() { } | |
/// Constructor. | |
explicit bad_alloc(const char* desc) : lua::exception(desc), std::bad_alloc() { } | |
virtual ~bad_alloc() throw() { } | |
virtual const char* what() const throw() { | |
return exception::what(); | |
} | |
}; | |
/** An error converting a lua type. | |
*/ | |
class bad_conversion : public exception { | |
public: | |
/// Constructor. | |
bad_conversion() : exception() { } | |
/// Constructor. | |
explicit bad_conversion(const char* desc) : exception(desc) { } | |
virtual ~bad_conversion() throw() { } | |
}; | |
/// A Lua table (this class does not have any data). | |
class table { }; | |
/// A Lua nil (this class does not have any data). | |
class nil { }; | |
/// A lua function (not a cfunction). | |
class function { }; | |
/// A lua lightuserdata. | |
class lightuserdata { }; | |
typedef lua_CFunction cfunction; | |
typedef lua_Integer integer; | |
typedef lua_Number number; | |
const int multiret = LUA_MULTRET; | |
enum types{ | |
TNONE = LUA_TNONE, | |
TNIL = LUA_TNIL, | |
TBOOLEAN = LUA_TBOOLEAN, | |
TLIGHTUSERDATA = LUA_TLIGHTUSERDATA, | |
TNUMBER = LUA_TNUMBER, | |
TSTRING = LUA_TSTRING, | |
TTABLE = LUA_TTABLE, | |
TFUNCTION = LUA_TFUNCTION, | |
TUSERDATA = LUA_TUSERDATA, | |
TTHREAD = LUA_TTHREAD | |
}; | |
/** This is the Luaxx equivalent of lua_State. | |
* The functions provided by this class, closely resemble those of the Lua C | |
* API. | |
*/ | |
class state { | |
public: | |
class scoped_stack{ | |
public: | |
scoped_stack(state &L):LuaState(L),top(L.gettop()){} | |
~scoped_stack(){LuaState.settop(top);} | |
private: | |
const int top; | |
state &LuaState; | |
}; | |
public: | |
/// Construct our lua environment. | |
state() : L(luaL_newstate()), managed(true) { | |
if (L == NULL) | |
throw bad_alloc("Error creating lua state"); | |
} | |
/** Construct our lua environment from an existing lua_State. | |
* @param L the existing state to use. | |
* | |
* This function differs from the normal constructor as it sets a flag | |
* that prevents lua_close() from being called when this class is | |
* destroyed. | |
*/ | |
state(lua_State* L) : | |
L(L), managed(false) { | |
} | |
/// Destroy our lua environment. | |
~state() { | |
if (managed) | |
lua_close(L); | |
} | |
/** Convert a lua::state to a lua_State*. | |
* This operator allows lua::state to behave like a lua_State | |
* pointer. | |
* | |
* @note This should be used as a last result to interoperate with C | |
* code. This may be removed in future versions of Luaxx. | |
*/ | |
inline operator lua_State*() { | |
return L; | |
} | |
/** Push a nil onto the stack. | |
* @returns a reference to this lua::state | |
*/ | |
state& push() { | |
lua_pushnil(L); | |
return *this; | |
} | |
/** Push a nil onto the stack. | |
* @returns a reference to this lua::state | |
*/ | |
state& push(nil) { | |
lua_pushnil(L); | |
return *this; | |
} | |
/** Push a boolean onto the stack. | |
* @param boolean the value to push | |
* @returns a reference to this lua::state | |
*/ | |
state& push(bool boolean) { | |
lua_pushboolean(L, boolean); | |
return *this; | |
} | |
/** Push a number onto the stack. | |
* @param number the number to push | |
* @returns a reference to this lua::state | |
*/ | |
template<typename T> | |
state& push(T number) { | |
lua_pushnumber(L, number); | |
return *this; | |
} | |
state& push(void *ptr){ | |
lua_pushlightuserdata(L, ptr); | |
return *this; | |
} | |
/** Push a C-style string onto the stack. | |
* @param s the string to push | |
* @param length the length of the string | |
* @returns a reference to this lua::state | |
*/ | |
state& push(const char* s, size_t length) { | |
lua_pushlstring(L, s, length); | |
return *this; | |
} | |
/** Push a C-style string onto the stack. | |
* @param s the string to push | |
* @param length the length of the string | |
* @returns a reference to this lua::state | |
*/ | |
state& push(char* s, size_t length) { | |
lua_pushlstring(L, s, length); | |
return *this; | |
} | |
/** Push a C-style string onto the stack. | |
* @param s the string to push | |
* @note This must be a '0' terminated string. | |
* @returns a reference to this lua::state | |
*/ | |
state& push(const char* s) { | |
lua_pushstring(L, s); | |
return *this; | |
} | |
/** Push a C-style string onto the stack. | |
* @param s the string to push | |
* @note This must be a '0' terminated string. | |
* @returns a reference to this lua::state | |
*/ | |
state& push(char* s) { | |
lua_pushstring(L, s); | |
return *this; | |
} | |
/** Push a char as string onto the stack. | |
* @param s the char to push | |
* @note This must be a '0' terminated string. | |
* @returns a reference to this lua::state | |
*/ | |
state& push(char s) { | |
lua_pushlstring(L, &s, 1); | |
return *this; | |
} | |
/** Push an std::string onto the stack. | |
* @param s the string to push | |
* @returns a reference to this lua::state | |
*/ | |
state& push(const std::string& s) { | |
lua_pushlstring(L, s.c_str(), s.size()); | |
return *this; | |
} | |
/** Push an C function onto the stack. | |
* @param f the function to push | |
* @returns a reference to this lua::state | |
*/ | |
state& push(cfunction f) { | |
lua_pushcfunction(L, f); | |
return *this; | |
} | |
/** Create a new table on the stack. | |
* @returns a reference to this lua::state | |
*/ | |
state& push(table) { | |
lua_newtable(L); | |
return *this; | |
} | |
/** Get the value at index as the given numeric type. | |
* @param number where to store the value | |
* @param index the index to get | |
* @note This function does \em not pop the value from the stack. | |
* @todo Instead of throwing an exception here, we may just return an | |
* error code. | |
* @throws lua::bad_conversion if the value on the stack could not be | |
* converted to the indicated type | |
* @returns a reference to this lua::state | |
*/ | |
template<typename T> | |
state& to(T& number, int index = -1) { | |
if (lua_isnumber(L, index)) | |
number = static_cast<T>(lua_tonumber(L, index)); | |
else | |
throw bad_conversion("Cannot convert non 'number' value to number"); | |
return *this; | |
} | |
/** Get the value at index as the given type. | |
* @param default_value this value is returned if the conversion fails | |
* @param index the index to get | |
* @note This function does \em not pop the value from the stack. | |
* @returns the indicated value from the stack or the default value if | |
* the conversion fails | |
*/ | |
template<typename T> | |
T as(T default_value, int index = -1) { | |
if (lua_isnumber(L, index)) | |
return static_cast<T>(lua_tonumber(L, index)); | |
else | |
return default_value; | |
} | |
/** Get the value at index as the given type. | |
* @param index the index to get | |
* @note This function does \em not pop the value from the stack. | |
* @todo Instead of throwing an exception here, we may just return an | |
* error code. | |
* @throws lua::bad_conversion if the value on the stack could not be | |
* converted to the indicated type | |
* @returns the indicated value as the given type if possible | |
*/ | |
template<typename T> | |
T as(int index = -1) { | |
if (lua_isnumber(L, index)) | |
return static_cast<T>(lua_tonumber(L, index)); | |
else | |
throw bad_conversion("Cannot convert non 'number' value to number"); | |
} | |
/** Get the value at index as a string. | |
* @param string this value is returned if the conversion fails | |
* @param index the index to get | |
* @note This function does \em not pop the value from the stack. | |
* | |
* @note lua::state::as(std::string()) will convert the value at the | |
* indicated index to a string <em>on the stack</em>. This can | |
* confuse lua::state::next(); | |
* | |
* @returns the indicated value from the stack or the default value if the | |
* conversion fails | |
*/ | |
std::string as(const std::string& string, int index) { | |
if (lua_isstring(L, index)){ | |
size_t len; const char *str = lua_tolstring(L, index, &len); | |
return std::string(str, len); | |
} | |
else | |
return string; | |
} | |
/** Get the value at index as a string. | |
* @param string this value is returned if the conversion fails | |
* @param index the index to get | |
* @note This function does \em not pop the value from the stack. | |
* | |
* @returns the indicated value from the stack or the default value if the | |
* conversion fails | |
*/ | |
const char* as(const char* string, int index) { | |
if (lua_isstring(L, index)) | |
return lua_tostring(L, index); | |
else | |
return string; | |
} | |
types type(int index){ | |
int t = lua_type(L, index); | |
switch(t){ | |
case LUA_TNONE : return TNONE; | |
case LUA_TNIL : return TNIL; | |
case LUA_TBOOLEAN : return TBOOLEAN; | |
case LUA_TLIGHTUSERDATA : return TLIGHTUSERDATA; | |
case LUA_TNUMBER : return TNUMBER; | |
case LUA_TSTRING : return TSTRING; | |
case LUA_TTABLE : return TTABLE; | |
case LUA_TFUNCTION : return TFUNCTION; | |
case LUA_TUSERDATA : return TUSERDATA; | |
case LUA_TTHREAD : return TTHREAD; | |
} | |
throw lua::exception("lua_type return unknown type value!"); | |
return TNONE; | |
} | |
state& concat(int n){ | |
lua_concat(L, n); | |
return *this; | |
} | |
/** Check if the given index is of the given type. | |
* @param index the index to check | |
* @note this default version will check if the given value is a | |
* number | |
* @returns whether the value at the given index is a nil | |
*/ | |
template<typename T> | |
bool is(int index = -1) { | |
return 0 != lua_isnumber(L, index); | |
} | |
/** Check if the given index is exactly of the given type. | |
* @param index the index to check | |
* @param tid type id (lua::types) | |
* @returns whether the value at the given index is a tested type | |
*/ | |
bool is(types tid, int index = -1){ | |
return tid == this->type(index); | |
} | |
/** Check an argument of the current function. | |
* @param narg the argument number to check | |
* | |
* This function will throw a lua error if there is no argument at the | |
* given position. | |
* | |
* @note This function is meant to be called from with in a | |
* lua::cfunction. The error throw is internal to the lua intrepeter. | |
* When compiled as C++, a C++ exception is thrown, so the stack is | |
* properly unwound. This exception is not meant to be caught. | |
*/ | |
state& check(int narg) { | |
luaL_checkany(L, narg); | |
return *this; | |
} | |
#if 0/*lua_Integer != int*/ | |
/** Check an argument of the current function. | |
* @param i the int to hold the returned value | |
* @param narg the argument number to check | |
* | |
* This function checks if the given argument number is an int. | |
* | |
* @note This function is meant to be called from with in a | |
* lua::cfunction. The error throw is internal to the lua intrepeter. | |
* When compiled as C++, a C++ exception is thrown, so the stack is | |
* properly unwound. This exception is not meant to be caught. | |
*/ | |
state& check(int& i, int narg) { | |
i = luaL_checkint(L, narg); | |
return *this; | |
} | |
#endif | |
/** Check an argument of the current function. | |
* @param i the lua::integer (lua_Integer) to hold the returned value | |
* @param narg the argument number to check | |
* | |
* This function checks if the given argument number is an integer. | |
* | |
* @note This is different from lua::check(int(), ...). It returns a | |
* lua::integer (lua_Integer), which may not be an int. | |
* | |
* @note This function is meant to be called from with in a | |
* lua::cfunction. The error throw is internal to the lua intrepeter. | |
* When compiled as C++, a C++ exception is thrown, so the stack is | |
* properly unwound. This exception is not meant to be caught. | |
*/ | |
state& check(integer& i, int narg) { | |
i = luaL_checkinteger(L, narg); | |
return *this; | |
} | |
#if 1 /*lua_Integer != long*/ | |
/** Check an argument of the current function. | |
* @param l the long to hold the returned value | |
* @param narg the argument number to check | |
* | |
* This function checks if the given argument number is a long. | |
* | |
* @note This function is meant to be called from with in a | |
* lua::cfunction. The error throw is internal to the lua intrepeter. | |
* When compiled as C++, a C++ exception is thrown, so the stack is | |
* properly unwound. This exception is not meant to be caught. | |
*/ | |
state& check(long& l, int narg) { | |
l = luaL_checklong(L, narg); | |
return *this; | |
} | |
#endif | |
/** Check an argument of the current function. | |
* @param s the string to hold the returned value | |
* @param narg the argument number to check | |
* | |
* This function checks if the given argument number is a string. | |
* | |
* @note This function is meant to be called from with in a | |
* lua::cfunction. The error throw is internal to the lua intrepeter. | |
* When compiled as C++, a C++ exception is thrown, so the stack is | |
* properly unwound. This exception is not meant to be caught. | |
*/ | |
state& check(std::string& s, int narg) { | |
const char* c; | |
size_t l; | |
c = luaL_checklstring(L, narg, &l); | |
s.assign(c, l); | |
return *this; | |
} | |
/** Check an argument of the current function. | |
* @param n the lua::number (lua_Number) to hold the returned value | |
* @param narg the argument number to check | |
* | |
* This function checks if the given argument number is a lua::number | |
* (lua_Number, a double by default). | |
* | |
* @note This function is meant to be called from with in a | |
* lua::cfunction. The error throw is internal to the lua intrepeter. | |
* When compiled as C++, a C++ exception is thrown, so the stack is | |
* properly unwound. This exception is not meant to be caught. | |
*/ | |
state& check(number& n, int narg) { | |
n = luaL_checknumber(L, narg); | |
return *this; | |
} | |
/** Generate a Lua error. | |
* @param message the error message/value | |
* @note This function is used to raise errors from lua::cfunctions. | |
* @note This function never returns, instead it throws an exception | |
* caught by the intepreter. | |
*/ | |
template<typename msg_t> | |
void error(msg_t message) { | |
push(message); | |
lua_error(L); | |
} | |
/** Generate a Lua error. | |
* @param message the error message/value | |
* @note This function is used to raise errors from lua::cfunctions. | |
* @note This function never returns, instead it throws an exception | |
* caught by the intepreter. | |
*/ | |
template<> | |
void error(const std::string& message) { | |
push(message); | |
lua_error(L); | |
} | |
/** Generate a Lua error. | |
* @note This function is used to raise errors from lua::cfunctions. | |
* @note This function never returns, instead it throws an exception | |
* caught by the intepreter. | |
*/ | |
void error(){ | |
lua_error(L); | |
} | |
/** Call a lua function. | |
* @param nargs the number of args to pass to the function | |
* @param nresults the number of values to return from the function | |
* @param on_error A stack index where the error handling function is | |
* stored. | |
* @note The error handling function must be pushed in the stack | |
* before the function to be called and its arguments. | |
* @returns a reference to this lua::state | |
*/ | |
state& pcall(int nargs = 0, int nresults = 0, int on_error = 0) { | |
throw_error(lua_pcall(L, nargs, nresults, on_error)); | |
return *this; | |
} | |
/** Call a cfunction in protected mode. | |
* @param fn cfunction | |
*/ | |
state& pcall(cfunction fn, int nargs = 0, int nresults = 0, int on_error = 0){ | |
if(on_error < 0) on_error = on_error - 1; // we also push | |
return this->push(fn).insert(-(nargs + 1)).pcall(nargs,nresults,on_error); | |
} | |
/** Call a cfunction. | |
* @param fn cfunction | |
* @param ptr light userdata passed to function | |
* @returns a reference to this lua::state | |
*/ | |
state& cpcall(cfunction fn, void *ptr = NULL) { | |
#if LUA_VERSION_NUM >= 502 | |
lua_pushlightuserdata(L, ptr); | |
return this->pcall(fn, 1, 0, 0); | |
#else | |
throw_error(lua_cpcall(L, fn, ptr)); | |
#endif | |
return *this; | |
} | |
/** Call a lua function in unprotected mode. | |
* @param nargs the number of args to pass to the function | |
* @param nresults the number of values to return from the function | |
* stored. | |
* @note If there is an error in the call the program will terminate. | |
* @returns a reference to this lua::state | |
*/ | |
state& call(int nargs = 0, int nresults = 0) { | |
lua_call(L, nargs, nresults); | |
return *this; | |
} | |
/** Ensure the stack is at least the given size. | |
* @param size the size to use | |
* | |
* If the stack is smaller than the given size, it will grow to the | |
* specified size. | |
* | |
* @exception lua::exception Thrown if the operation fails. | |
* @returns a reference to this lua::state | |
*/ | |
state& checkstack(int size) { | |
if (!lua_checkstack(L, size)) | |
throw lua::exception("Error growing the stack"); | |
return *this; | |
} | |
/** Set a new index as the top of the stack. | |
* @param index the index to use as the new top | |
* @note If the prefious top was higher than the new one, top values | |
* are discarded. Otherwise this function pushs nils on to the stack | |
* to get the proper size. | |
* @returns a reference to this lua::state | |
*/ | |
state& settop(int index) { | |
lua_settop(L, index); | |
return *this; | |
} | |
/** Get the number of elements in the stack. | |
* @note This value is also the index of the top element. | |
* @returns the number of elements in the stack | |
*/ | |
int gettop() { | |
return lua_gettop(L); | |
} | |
/** Get the number of elements in the stack. | |
* @note This value is also the index of the top element. | |
* @returns the number of elements in the stack | |
*/ | |
int size() { | |
return lua_gettop(L); | |
} | |
/** Check if the stack is empty. | |
* @returns true if the stack is empty, false otherwise | |
*/ | |
bool empty() { | |
return !lua_gettop(L); | |
} | |
/** Move the top element to the given index. | |
* @param index the index to insert at | |
* @note All elements on top of the given index are shifted up to open | |
* space for this element. | |
* @returns a reference to this lua::state | |
*/ | |
state& insert(int index) { | |
lua_insert(L, index); | |
return *this; | |
} | |
/** Replace the given index with the top element. | |
* @param index the index to replae | |
* @returns a reference to this lua::state | |
*/ | |
state& replace(int index) { | |
lua_replace(L, index); | |
return *this; | |
} | |
/** Remove the given index from the stack. | |
* @param index the index to remove | |
* @note Elements are shifted down to fill in the empty spot. | |
* @returns a reference to this lua::state | |
*/ | |
state& remove(int index) { | |
lua_remove(L, index); | |
return *this; | |
} | |
/** Remove the given number of elemens from the stack. | |
* @param elements the number of elements to remove | |
* @returns a reference to this lua::state | |
*/ | |
state& pop(int elements = 1) { | |
lua_pop(L, elements); | |
return *this; | |
} | |
/** Push a copy of the element at the given index to the top of the | |
* stack. | |
* @param index the index of the element to copy | |
* @returns a reference to this lua::state | |
*/ | |
state& pushvalue(int index) { | |
lua_pushvalue(L, index); | |
return *this; | |
} | |
/** Create a new table on the stack. | |
* @returns a reference to this lua::state | |
*/ | |
state& newtable() { | |
lua_newtable(L); | |
return *this; | |
} | |
/** Get a value from a table on the stack. | |
* @param index the index the table is stored at | |
* | |
* This function gets a value from the table at the given index and | |
* pushes it onto the stack. | |
* | |
* @note You should have already pushed the key used to reference this | |
* value onto the stack before calling this function. | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& gettable(int index = -2) { | |
lua_gettable(L, index); | |
return *this; | |
} | |
/** Set a value in a table. | |
* @param index the index the table is stored at | |
* | |
* This function sets a value in a table stored at the given index. | |
* | |
* @note The key and value to be used should have already been pushed | |
* on the stack in that order. | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& settable(int index = -3) { | |
lua_settable(L, index); | |
return *this; | |
} | |
/** Get the next key value pair from a table on the stack. | |
* @param index the stack index the table is at | |
* | |
* This function pops a key from the stack and pushes the next key | |
* value pair to the stack. The key will be stored at index -2 and | |
* the value will be at index -1. The key is expected to be on the | |
* top of the stack. | |
* | |
* @note While traversing a table, do not call | |
* lua::state::to(std::string()) directly on a key, unless you know | |
* that the key is actually a string. lua::state::to(std::string()) | |
* changes the value at the given index; this confuses the next call | |
* to lua::state::next(). | |
* | |
* <strong>While Loop Example:</strong> | |
* @code | |
* while(L.next() != 0) { | |
* // do stuff | |
* L.pop(); | |
* } | |
* @endcode | |
* | |
* <strong>For Loop Example:</strong> | |
* @code | |
* for(L.push(lua::nil()); L.next(); L.pop()) { | |
* // do stuff | |
* } | |
* @endcode | |
* | |
* @returns true as long as there are remaining items in the table | |
*/ | |
bool next(int index = -2) { | |
return 0 != lua_next(L, index); | |
} | |
/** Load a global symbol onto the stack. | |
* @param name the name of the global to load | |
* | |
* This function loads a global symbol onto the stack from the lua | |
* state. | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& getglobal(const std::string& name) { | |
lua_getglobal(L, name.c_str()); | |
return *this; | |
} | |
/** Load a global symbol onto the stack. | |
* @param name the name of the global to load | |
* | |
* This function loads a global symbol onto the stack from the lua | |
* state. | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& getglobal(const char* name) { | |
lua_getglobal(L, name); | |
return *this; | |
} | |
/** Load a global registry onto the stack. | |
* @param name the name of the registry to load | |
* | |
* This function loads a registry symbol onto the stack from the lua | |
* state. | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& getregistry(const std::string& name) { | |
lua_getfield(L, LUA_REGISTRYINDEX, name.c_str()); | |
return *this; | |
} | |
/** Load a global registry onto the stack. | |
* @param name the name of the registry to load | |
* | |
* This function loads a registry symbol onto the stack from the lua | |
* state. | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& getregistry(const char* name) { | |
lua_getfield(L, LUA_REGISTRYINDEX, name); | |
return *this; | |
} | |
state& getregistry(int i) { | |
lua_rawgeti(L, LUA_REGISTRYINDEX, i); | |
return *this; | |
} | |
int ref(int t) { | |
return luaL_ref(L, t); | |
} | |
state& unref(int t, int r) { | |
luaL_unref(L, t, r); | |
return *this; | |
} | |
int refregistry() { | |
return this->ref(LUA_REGISTRYINDEX); | |
} | |
state& unrefregistry(int r) { | |
return this->unref(LUA_REGISTRYINDEX, r); | |
} | |
/** Set a global symbol. | |
* @param name the name of the global to set | |
* | |
* This function sets/creates a global symbol from the value above it | |
* on the stack. | |
* | |
* @note You should have pushed the value of the symbol onto the stack | |
* before calling this function. | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& setglobal(const std::string& name) { | |
lua_setglobal(L, name.c_str()); | |
return *this; | |
} | |
/** Set a global symbol. | |
* @param name the name of the global to set | |
* | |
* This function sets/creates a global symbol from the value above it | |
* on the stack. | |
* | |
* @note You should have pushed the value of the symbol onto the stack | |
* before calling this function. | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& setglobal(const char* name) { | |
lua_setglobal(L, name); | |
return *this; | |
} | |
/** Set a registry symbol. | |
* @param name the name of the registry to set | |
* | |
* This function sets/creates a registry symbol from the value above it | |
* on the stack. | |
* | |
* @note You should have pushed the value of the symbol onto the stack | |
* before calling this function. | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& setregistry(const std::string& name) { | |
lua_setfield(L, LUA_REGISTRYINDEX, name.c_str()); | |
return *this; | |
} | |
/** Set a registry symbol. | |
* @param name the name of the registry to set | |
* | |
* This function sets/creates a registry symbol from the value above it | |
* on the stack. | |
* | |
* @note You should have pushed the value of the symbol onto the stack | |
* before calling this function. | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& setregistry(const char* name) { | |
lua_setfield(L, LUA_REGISTRYINDEX, name); | |
return *this; | |
} | |
/** Load a file as a Lua chunk. | |
* @param filename the name of the file to load | |
* @param nresults the number of result | |
* @note After the data is loaded lua_pcall() is called. | |
* @returns a reference to this lua::state | |
*/ | |
state& runfile(const std::string& filename,int nargs = 0, int nresults = 0, int onerror = 0) { | |
return this->loadfile(filename).pcall(nargs, nresults, onerror); | |
} | |
state& runfile(const char* filename,int nargs = 0, int nresults = 0, int onerror = 0) { | |
return this->loadfile(filename).pcall(nargs, nresults, onerror); | |
} | |
state& loadfile(const std::string& filename) { | |
throw_error(luaL_loadfile(L, filename.c_str())); | |
return *this; | |
} | |
state& loadfile(const char* filename) { | |
throw_error(luaL_loadfile(L, filename)); | |
return *this; | |
} | |
/** Load a string as a Lua chunk. | |
* @param s the string to load | |
* @note After the data is loaded lua_pcall() is called. | |
* @returns a reference to this lua::state | |
*/ | |
state& run(const std::string& s,int nargs = 0, int nresults = 0, int onerror = 0) { | |
return this->load(s.c_str()).pcall(nargs, nresults, onerror); | |
} | |
state& run(const char *s,int nargs = 0, int nresults = 0, int onerror = 0) { | |
return this->load(s).pcall(nargs, nresults, onerror); | |
} | |
state& load(const std::string& s) { | |
throw_error(luaL_loadstring(L, s.c_str())); | |
return *this; | |
} | |
state& load(const char* s) { | |
throw_error(luaL_loadstring(L, s)); | |
return *this; | |
} | |
state& loadbuffer(const char* s, size_t sz, const char *chunkname = NULL) { | |
throw_error(luaL_loadbuffer(L, s, sz, chunkname)); | |
return *this; | |
} | |
/** Load a sequence of data as a Lua chunk. | |
* @param begin an iterator to the start of the sequence | |
* @param end an iterator to the end of the sequence (one past the | |
* end) | |
* | |
* This function takes a sequence of data and attempts to convert it | |
* into a Lua chunk. The type of data passed must be able to be | |
* converted into an 8-bit char. | |
* | |
* @note This function should automatically detect if the data is text | |
* or binary. | |
* @note After the data is loaded lua_pcall() is called. | |
* | |
* @returns a reference to this lua::state | |
*/ | |
template<typename iterator> | |
state& run(iterator begin, iterator end) { | |
// convert the data to characters | |
std::vector<char> chunk(begin, end); | |
// Here we use the address of the first element of our vector. | |
// This works because the data in std::vectors is contiguous. | |
return loadbuffer(&(*chunk.begin()), chunk.size(), NULL).pcall(); | |
} | |
/** Load lua chank using reader (raw load) | |
* @param rdr instans of luaxx::reader class | |
* | |
* @note this function do not execute loaded chunk | |
*/ | |
state& load(reader * rdr, const char *chunkname = NULL | |
#if LUA_VERSION_NUM >= 502 | |
,const char *mode = NULL | |
#endif | |
){ | |
throw_error( | |
lua_load(L, reader::lreader, reinterpret_cast<void*>(rdr), chunkname | |
#if LUA_VERSION_NUM >= 502 | |
,mode = NULL | |
#endif | |
) | |
); | |
return *this; | |
} | |
state& load(reader & rdr, const char *chunkname = NULL){ | |
return load(&rdr, chunkname); | |
} | |
/** Dump loaded chunk using writer (raw dump) | |
* @param rdr instans of luaxx::reader class | |
* | |
* @note this function do not execute loaded chunk | |
*/ | |
state& dump(writer * wrt){ | |
throw_error( | |
lua_dump(L, writer::lwriter, reinterpret_cast<void*>(wrt)) | |
); | |
return *this; | |
} | |
state& dump(writer & wrt){ | |
return dump(&wrt); | |
} | |
/** Get the length of a value on the stack. | |
* @param index the index the value is stored at | |
* @returns the length of the indicated value | |
*/ | |
size_t objlen(int index = -1) { | |
#if LUA_VERSION_NUM >= 502 | |
return lua_rawlen(L, index); | |
#else | |
return lua_objlen(L, index); | |
#endif | |
} | |
size_t rawlen(int index = -1){ | |
return this->objlen(index); | |
} | |
/** Get the length of a string on the stack. | |
* @param index the index the value is stored at | |
* @returns the length of the indicated value | |
*/ | |
size_t strlen(int index = -1) { | |
#if LUA_VERSION_NUM >= 502 | |
return this->rawlen(index); | |
#else | |
return lua_strlen(L, index); | |
#endif | |
} | |
/** Register loader for module | |
* @param name module name | |
* @param loader | |
* @param nup number of upvalues for loader | |
* | |
* @usage L.regloader("os", luaopen_os); | |
*/ | |
state& regloader (const char *name, lua::cfunction loader, int nup = 0) { | |
lua_pushcclosure(L, loader, nup); /* make loader */ | |
lua_getglobal(L, "package"); /* get '_G.package' */ | |
lua_getfield(L, -1, "preload"); /* get 'package.preload' */ | |
lua_remove(L, -2); /* pop 'package' */ | |
lua_insert(L,-2); /* swap loader and 'preload' */ | |
lua_setfield(L, -2, name); /* package.preload[name] = loader */ | |
lua_pop(L, 1); /* pop 'preload' */ | |
return *this; | |
} | |
state& regloader (const std::string &name, lua::cfunction loader, int nup = 0) { | |
return regloader(name.c_str(), loader, nup); | |
} | |
private: | |
lua_State* L; | |
bool managed; | |
// make lua::states non copyable | |
state(const state&); ///< Private copy constructor. | |
state& operator = (const state&); ///< Private asignment operator. | |
public: | |
inline int throw_error(int code); | |
public: | |
//*********************************************************************** | |
//* Extendet | |
//{********************************************************************** | |
/** Get a value from a table on the stack. | |
* @param index the index the table is stored at | |
* | |
* This function gets a value from the table at the given index and | |
* pushes it onto the stack. | |
* | |
* @note You should have already pushed the key used to reference this | |
* value onto the stack before calling this function. | |
* This function does a raw access (i.e., without metamethods) | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& rawget(int index = -2) { | |
lua_rawget(L, index); | |
return *this; | |
} | |
/** Set a value in a table. | |
* @param index the index the table is stored at | |
* | |
* This function sets a value in a table stored at the given index. | |
* | |
* @note The key and value to be used should have already been pushed | |
* on the stack in that order. | |
* This function does a raw access (i.e., without metamethods) | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& rawset(int index = -3) { | |
lua_rawset(L, index); | |
return *this; | |
} | |
/** Get a value from a table on the stack. | |
* @param index the index the table is stored at | |
* @param index the table | |
* | |
* This function gets a value from the table at the given index and | |
* pushes it onto the stack. | |
* | |
* @note You should have already pushed the key used to reference this | |
* value onto the stack before calling this function. | |
* This function does a raw access (i.e., without metamethods) | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& rawgeti(int index, int n) { | |
lua_rawgeti(L, index, n); | |
return *this; | |
} | |
state& rawgeti( int n) /*index = -1*/ { | |
return rawgeti(-1, n); | |
} | |
/** Set a value in a table. | |
* @param index the index the table is stored at | |
* @param index in the table | |
* | |
* This function sets a value in a table stored at the given index. | |
* | |
* @note The key and value to be used should have already been pushed | |
* on the stack in that order. | |
* This function does a raw access (i.e., without metamethods) | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& rawseti(int index, int n) { | |
lua_rawseti(L, index, n); | |
return *this; | |
} | |
state& rawseti(int n) /*index = -2*/ { | |
return rawseti(-2, n); | |
} | |
/** Get a value from a table on the stack. | |
* @param index the index the table is stored at | |
* @param index in the table | |
* | |
* This function gets a value from the table at the given index and | |
* pushes it onto the stack. | |
* | |
* @note Pushes onto the stack the value t[k], where t is the value | |
* at the given valid index index. As in Lua, this function may | |
* trigger a metamethod for the "index" event. | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& getfield(int index, const char *k) { | |
lua_getfield(L, index, k); | |
return *this; | |
} | |
state& getfield(const char *k, int index = -1 ) { | |
return getfield(index, k); | |
} | |
/** Set a value in a table. | |
* @param index the index the table is stored at | |
* @param index in the table | |
* | |
* Does the equivalent to t[k] = v, where t is the value at the given | |
* valid index index and v is the value at the top of the stack, | |
* This function pops the value from the stack. As in Lua, this function | |
* may trigger a metamethod for the "newindex" event. | |
* | |
* @note The value to be used should have already been pushed | |
* on the top stack in that order. | |
* | |
* @returns a reference to this lua::state | |
*/ | |
state& setfield(int index, const char *k) { | |
lua_setfield(L, index, k); | |
return *this; | |
} | |
state& setfield( const char *k, int index = -2) { | |
lua_setfield(L, index, k); | |
return *this; | |
} | |
state& newmetatable( const char *typeName ) { | |
luaL_newmetatable(L,typeName); | |
return *this; | |
} | |
state& newmetatable( const std::string& typeName ) { | |
return this->newmetatable(typeName.c_str()); | |
} | |
state& getmetatable( const char *typeName ) { | |
luaL_getmetatable(L,typeName); | |
return *this; | |
} | |
state& getmetatable( const std::string& typeName ) { | |
return this->getmetatable(typeName.c_str()); | |
} | |
state& setmetatable( int index = -2 ) { | |
lua_setmetatable(L,index); | |
return *this; | |
} | |
state& setmetatable( const char *typeName, int index = -1 ) { | |
this->getmetatable(typeName); | |
if(index < 0) index--; | |
else index++; | |
return this->setmetatable(index); | |
} | |
state& setmetatable( const std::string& typeName, int index = -1 ) { | |
return this->setmetatable(typeName.c_str(), index); | |
} | |
template< typename T > | |
state& newuserdata(){ | |
T* val = reinterpret_cast<T*>(lua_newuserdata(L,sizeof(T))); | |
if(!val) | |
throw bad_alloc("Can not create new userdata"); | |
return *this; | |
} | |
template< typename T > | |
state& newuserdata(typename T_REF<T>::const_reference value){ | |
T* val = reinterpret_cast<T*>(lua_newuserdata(L,sizeof(T))); | |
if(!val) | |
throw bad_alloc("Can not create new userdata"); | |
*val = value; | |
return *this; | |
} | |
//}********************************************************************** | |
}; | |
/** Get the value at index as a bool. | |
* @param boolean where to store the value | |
* @param index the index to get | |
* @note This function does \em not pop the value from the stack. | |
* @todo Instead of throwing an exception here, we may just return an | |
* error code. | |
* @throws lua::bad_conversion if the value on the stack could not be | |
* converted to the indicated type | |
* @returns a reference to this lua::state | |
*/ | |
template<> | |
inline | |
state& state::to(bool& boolean, int index) { | |
if (lua_isboolean(L, index)) | |
boolean = 0 != lua_toboolean(L, index); | |
else | |
throw bad_conversion("Cannot convert non 'boolean' value to bool"); | |
return *this; | |
} | |
/** Get the value at index as a string. | |
* @param string where to store the value | |
* @param index the index to get | |
* @note This function does \em not pop the value from the stack. | |
* @todo Instead of throwing an exception here, we may just return an | |
* error code. | |
* | |
* @note lua::state::to(std::string()) will convert the value at the | |
* indicated index to a string <em>on the stack</em>. This can | |
* confuse lua::state::next(); | |
* | |
* @throws lua::bad_conversion if the value on the stack could not be | |
* converted to the indicated type | |
* @returns a reference to this lua::state | |
*/ | |
template<> | |
inline | |
state& state::to(std::string& string, int index) { | |
if (lua_isstring(L, index)){ | |
size_t len; const char *str = lua_tolstring(L, index, &len); | |
string.replace(0, std::string::npos, str, len); | |
} | |
else | |
throw bad_conversion("Cannot convert value to string"); | |
return *this; | |
} | |
/** Get the value at index as a bool. | |
* @param default_value this value is returned if the conversion fails | |
* @param index the index to get | |
* @note This function does \em not pop the value from the stack. | |
* @returns the indicated value from the stack or the default value if the | |
* conversion fails | |
*/ | |
template<> | |
inline | |
bool state::as(bool default_value, int index) { | |
if (lua_isboolean(L, index)) | |
return 0 != lua_toboolean(L, index); | |
else | |
return 0 != default_value; | |
} | |
/** Get the value at index as a bool. | |
* @param index the index to get | |
* @note This function does \em not pop the value from the stack. | |
* @todo Instead of throwing an exception here, we may just return an | |
* error code. | |
* @throws lua::bad_conversion if the value on the stack could not be | |
* converted to the indicated type | |
* @returns the indicated value as a bool if possible | |
*/ | |
template<> | |
inline | |
bool state::as(int index) { | |
if (lua_isboolean(L, index)) | |
return 0 != lua_toboolean(L, index); | |
else | |
throw bad_conversion("Cannot convert non 'boolean' value to bool"); | |
} | |
/** Get the value at index as a string. | |
* @param index the index to get | |
* @note This function does \em not pop the value from the stack. | |
* | |
* @note lua::state::as(std::string()) will convert the value at the | |
* indicated index to a string <em>on the stack</em>. This can | |
* confuse lua::state::next(); | |
* | |
* @throws lua::bad_conversion if the value on the stack could not be | |
* converted to the indicated type | |
* @returns the indicated value as a string if possible | |
*/ | |
template<> | |
inline | |
std::string state::as(int index) { | |
if (lua_isstring(L, index)){ | |
size_t len; const char *str = lua_tolstring(L, index, &len); | |
return std::string(str, len); | |
} | |
else | |
throw bad_conversion("Cannot convert value to string"); | |
} | |
/** Check if the given index is a nil. | |
* @param index the index to check | |
* @returns whether the value at the given index is a nil | |
*/ | |
template<> | |
inline | |
bool state::is<nil>(int index) { | |
return lua_isnil(L, index); | |
} | |
/** Check if the given index is a lightuserdata. | |
* @param index the index to check | |
* @returns whether the value at the given index is a nil | |
*/ | |
template<> | |
inline | |
bool state::is<lightuserdata>(int index) { | |
return lua_islightuserdata(L, index); | |
} | |
/** Check if the given index is a boolean. | |
* @param index the index to check | |
* @returns whether the value at the given index is a boolean | |
*/ | |
template<> | |
inline | |
bool state::is<bool>(int index) { | |
return lua_isboolean(L, index); | |
} | |
/** Check if the given index is a string. | |
* @param index the index to check | |
* @returns whether the value at the given index is a number | |
*/ | |
template<> | |
inline | |
bool state::is<std::string>(int index) { | |
return 0 != lua_isstring(L, index); | |
} | |
/** Check if the given index is a table. | |
* @param index the index to check | |
* @returns whether the value at the given index is a table | |
*/ | |
template<> | |
inline | |
bool state::is<table>(int index) { | |
return lua_istable(L, index); | |
} | |
/** Check if the given index is a C function. | |
* @param index the index to check | |
* @returns whether the value at the given index is a function | |
*/ | |
template<> | |
inline | |
bool state::is<cfunction>(int index) { | |
return 0 != lua_iscfunction(L, index); | |
} | |
/** Check if the given index is a function. | |
* @param index the index to check | |
* @returns whether the value at the given index is a function | |
*/ | |
template<> | |
inline | |
bool state::is<function>(int index) { | |
return lua_isfunction(L, index); | |
} | |
/** Throws exceptions for error return codes. | |
* @param code the return code | |
* | |
* This function throws an exception based on the error it was passed. | |
* If it is passed a 0 it will not throw anything. | |
* | |
* @todo In the future this function may check an exception mask | |
* before throwing an error. | |
* | |
* @returns the code it was passed | |
*/ | |
inline int state::throw_error(int code) { | |
std::string error; | |
// below, we package lua errors into exceptions | |
switch (code) { | |
case 0: | |
break; | |
case LUA_ERRSYNTAX: | |
to(error).pop(); | |
throw syntax_error(error.c_str()); | |
break; | |
case LUA_ERRMEM: | |
to(error).pop(); | |
throw bad_alloc(error.c_str()); | |
break; | |
case LUA_ERRRUN: | |
to(error).pop(); | |
throw runtime_error(error.c_str()); | |
break; | |
case LUA_ERRFILE: | |
to(error).pop(); | |
throw file_error(error.c_str()); | |
break; | |
default: | |
to(error).pop(); | |
throw exception(error.c_str()); | |
} | |
return code; | |
} | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment