Skip to content

Instantly share code, notes, and snippets.

@moteus
Created February 20, 2014 13:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save moteus/9113307 to your computer and use it in GitHub Desktop.
Save moteus/9113307 to your computer and use it in GitHub Desktop.
/* 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