God knows what's modified
This commit is contained in:
@@ -1,3 +1,155 @@
|
||||
/////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
// LuaStack
|
||||
//
|
||||
// Class LuaStack lets you create "lua local variables." These are
|
||||
// variables that seem to store lua values. Class LuaStack also provides
|
||||
// accessors like "rawget" that reference lua local variables instead of
|
||||
// using the lua stack.
|
||||
//
|
||||
// Of course, this is all using the lua stack under the covers. Lua
|
||||
// local variables are actually just lua stack addresses. But that's
|
||||
// all kept fairly well hidden. When you use Lua local variables, and
|
||||
// the accessors inside class LuaStack, it appears that you're
|
||||
// manipulating data using local variables instead of using a stack.
|
||||
// For people like me, that's easier to think about.
|
||||
//
|
||||
// Here's an example.
|
||||
//
|
||||
// let's say you have a function that takes two arguments
|
||||
// ARG1 and ARG2, has a single return value RET1, and needs two local
|
||||
// variables LOC1 and LOC2. We would declare it like this:
|
||||
//
|
||||
// int myfunc(lua_State *L) {
|
||||
//
|
||||
// LuaArg arg1, arg2; // Declare local variables to hold the arguments.
|
||||
// LuaRet ret1; // Declare local variables to hold the return values.
|
||||
// LuaVar loc1, loc2, loc3; // Declare local variables for other purposes.
|
||||
//
|
||||
// // Assign every local var a stack index.
|
||||
// LuaStack LS(L, arg1, arg2, ret1, loc1, loc2, loc3);
|
||||
//
|
||||
// // manipulate the data in the lua local variables...
|
||||
// LS.rawget(loc1, arg1, arg2);
|
||||
// ... etc ...
|
||||
// }
|
||||
//
|
||||
// Class LuaArg, LuaRet, and LuaVar are all lua local variables.
|
||||
// The luastack constructor assigns each one of them a position on
|
||||
// the lua stack. It also makes sure that the arguments are in
|
||||
// the LuaArg variables, and it makes sure that the LuaRet values
|
||||
// are the only thing left on the stack at return time.
|
||||
//
|
||||
// Class LuaStack provides a complete catalog of accessors
|
||||
// like 'rawget' - roughly speaking, it provides equivalents to
|
||||
// every major accessor in the lua API. However, the accessors
|
||||
// provided by LuaStack take input and output from lua locals, not
|
||||
// from the stack. For example, consider this:
|
||||
//
|
||||
// LS.rawget(value, tab, key);
|
||||
//
|
||||
// In the above, value, tab, and key should be lua local variables.
|
||||
// This does a rawget on 'table', with the specified 'key', and
|
||||
// stores the result in 'value'. Nothing is added to or removed
|
||||
// from the lua stack. In general, none of the accessors in class
|
||||
// LuaStack add anything to the stack, or pop anything from the
|
||||
// stack.
|
||||
//
|
||||
// Class LuaStack can also do automatic type conversions. For
|
||||
// example, suppose you do this:
|
||||
//
|
||||
// LS.rawget(value, tab, key);
|
||||
//
|
||||
// Nominally, you would expect value, tab, and key to be lua local
|
||||
// variables. But if you pass a std::string for key, then LuaStack will
|
||||
// automatically convert it. In general, class LuaStack can
|
||||
// convert lua_Integer, lua_Number, std::string, bool, and LuaNil.
|
||||
//
|
||||
// On output, LuaStack can convert lua_Integers, lua_Numbers, and
|
||||
// std::strings. In this case, strict type checking is done. If
|
||||
// there is a type mismatch, a lua error is thrown.
|
||||
//
|
||||
// You can use the operator 'set' to assign a value to a lua local
|
||||
// variable:
|
||||
//
|
||||
// LS.set(val1, val2);
|
||||
//
|
||||
// This is actually a copy operation that copies from one lua local
|
||||
// variable to another. But using type conversions, it can also be
|
||||
// used to assign arbitrary values to lua local variables, or to
|
||||
// get values from lua local variables.
|
||||
//
|
||||
// Passing LuaNewTable as an input will cause a new table to be
|
||||
// created before calling the specified operation.
|
||||
//
|
||||
//
|
||||
/////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
// LuaStack type checking
|
||||
//
|
||||
// LuaStack contains accessors for type checking. These include:
|
||||
//
|
||||
// bool LuaStack::isnumber(LuaSlot s)
|
||||
// bool LuaStack::isinteger(LuaSlot s)
|
||||
// bool LuaStack::isstring(LuaSlot s)
|
||||
// etc...
|
||||
//
|
||||
// And it also contains operations that throw errors:
|
||||
//
|
||||
// void LuaStack::checknumber(LuaSlot s)
|
||||
// void LuaStack::checkinteger(LuaSlot s)
|
||||
// void LuaStack::checkstring(LuaSlot s)
|
||||
// etc...
|
||||
//
|
||||
// These are different from the lua builtins in that they are strict.
|
||||
// For example, 'isnumber' only returns true if the value in the
|
||||
// lua local variable is already a number. No conversions are done.
|
||||
//
|
||||
// These functions do checking and also conversion at the same time:
|
||||
//
|
||||
// lua_Integer LuaStack::ckinteger(LuaSlot s)
|
||||
// lua_Number LuaStack::cknumber(LuaSlot s)
|
||||
// std::string LuaStack::ckstring(LuaSlot s)
|
||||
// lua_State *LuaStack::ckthread(LuaSlot s)
|
||||
//
|
||||
// Like the other operations, they are strict.
|
||||
//
|
||||
//
|
||||
/////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
// LuaDefine
|
||||
//
|
||||
// LuaDefine is a macro that helps you define lua functions, but
|
||||
// it also puts the function into a global function registry.
|
||||
// You use it like so:
|
||||
//
|
||||
// LuaDefine(function_name, "modebits") {
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// This macroexpands into a function definition and a function
|
||||
// registration. The function definition looks like this:
|
||||
//
|
||||
// int function_name(lua_State *L) {
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// The macro expansion generates this function definition, but it
|
||||
// also generates a "registration object" whose constructor puts
|
||||
// this function into a global registry of lua-callable C functions.
|
||||
// This global registry is later used to inject these C functions
|
||||
// into the lua intepreter. The mode is a string that contain
|
||||
// the following characters:
|
||||
//
|
||||
// c - create a class, and put a function into it.
|
||||
// f - create a global function not inside a class.
|
||||
// a - autoexec - run this function automatically on model creation.
|
||||
//
|
||||
//
|
||||
/////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef LUASTACK_HPP
|
||||
@@ -13,17 +165,6 @@ extern "C" {
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// LuaSlot
|
||||
//
|
||||
// An LuaSlot contains a lua stack index. It is initialized by the
|
||||
// LuaStack and contains the same index until the LuaStack
|
||||
// is destroyed. You can convert an LuaSlot into a lua stack index
|
||||
// by simply coercing it to 'int'.
|
||||
//
|
||||
// There are three variants of LuaSlot that you can use: LuaArg (function
|
||||
// argument), LuaRet (function return value), and LuaVar (function local
|
||||
// variable).
|
||||
//
|
||||
class LuaSlot {
|
||||
protected:
|
||||
int index_;
|
||||
@@ -67,6 +208,20 @@ class LuaUpvalue : public LuaSlot {
|
||||
class LuaNilMarker {};
|
||||
extern LuaNilMarker LuaNil;
|
||||
|
||||
class LuaNewTableMarker {};
|
||||
extern LuaNewTableMarker LuaNewTable;
|
||||
|
||||
class LuaDiscardMarker {};
|
||||
extern LuaDiscardMarker LuaDiscard;
|
||||
|
||||
struct LuaAcceptNilNumber { lua_Number v; };
|
||||
struct LuaAcceptNilInteger { lua_Integer v; };
|
||||
struct LuaAcceptNilString { std::string v; };
|
||||
|
||||
inline LuaAcceptNilNumber &LuaAcceptNil(lua_Number &x) { return *(LuaAcceptNilNumber *)(&x); }
|
||||
inline LuaAcceptNilInteger &LuaAcceptNil(lua_Integer &x) { return *(LuaAcceptNilInteger *)(&x); }
|
||||
inline LuaAcceptNilString &LuaAcceptNil(std::string &x) { return *(LuaAcceptNilString *)(&x); }
|
||||
|
||||
class LuaStack {
|
||||
private:
|
||||
int narg_;
|
||||
@@ -130,6 +285,75 @@ private:
|
||||
|
||||
void clear_frame();
|
||||
|
||||
private:
|
||||
// Push any value on the stack, by type.
|
||||
void push_any_value(LuaNewTableMarker s) const { lua_newtable(L_); }
|
||||
void push_any_value(LuaNilMarker s) const { lua_pushnil(L_); }
|
||||
void push_any_value(LuaSlot s) const { lua_pushvalue(L_, s); }
|
||||
void push_any_value(const std::string &s) const { lua_pushlstring(L_, s.c_str(), s.size()); }
|
||||
void push_any_value(const char *s) const { lua_pushstring(L_, s); }
|
||||
void push_any_value(float s) const { lua_pushnumber(L_, s); }
|
||||
void push_any_value(double s) const { lua_pushnumber(L_, s); }
|
||||
void push_any_value(int s) const { lua_pushinteger(L_, s); }
|
||||
void push_any_value(lua_Integer s) const { lua_pushinteger(L_, s); }
|
||||
void push_any_value(bool b) const { lua_pushboolean(L_, b ? 1:0); }
|
||||
|
||||
// Pop any value off the stack, by type.
|
||||
void pop_any_value(LuaSlot &s) const { lua_replace(L_, s); }
|
||||
void pop_any_value(lua_Integer &s) const { s = luaL_ckinteger(L_, -1); lua_pop(L_, 1); }
|
||||
void pop_any_value(lua_Number &s) const { s = luaL_cknumber(L_, -1); lua_pop(L_, 1); }
|
||||
void pop_any_value(std::string &s) const;
|
||||
void pop_any_value(LuaAcceptNilNumber &s) const;
|
||||
void pop_any_value(LuaAcceptNilInteger &s) const;
|
||||
void pop_any_value(LuaAcceptNilString &s) const;
|
||||
void pop_any_value(LuaDiscardMarker &s) const { lua_pop(L_, 1); }
|
||||
|
||||
// Push multiple values on the stack, in order, by type.
|
||||
template<typename T0, typename... T>
|
||||
void push_any_values(T0 arg0, T... args) {
|
||||
push_any_value(arg0);
|
||||
push_any_values(args...);
|
||||
}
|
||||
void push_any_values() {
|
||||
}
|
||||
|
||||
// Call the CFunction, pushing and popping arguments appropriately.
|
||||
template<int NRET, typename... T>
|
||||
void call_cfunction(int otop, LuaSlot s, T... args) {
|
||||
call_cfunction<NRET+1>(otop, args...);
|
||||
pop_any_value(s);
|
||||
}
|
||||
template<int NRET, typename... T>
|
||||
void call_cfunction(int otop, lua_Integer &s, T... args) {
|
||||
call_cfunction<NRET+1>(otop, args...);
|
||||
pop_any_value(s);
|
||||
}
|
||||
template<int NRET, typename... T>
|
||||
void call_cfunction(int otop, lua_Number &s, T... args) {
|
||||
call_cfunction<NRET+1>(otop, args...);
|
||||
pop_any_value(s);
|
||||
}
|
||||
template<int NRET, typename... T>
|
||||
void call_cfunction(int otop, std::string &s, T... args) {
|
||||
call_cfunction<NRET+1>(otop, args...);
|
||||
pop_any_value(s);
|
||||
}
|
||||
template<int NRET, typename... T>
|
||||
void call_cfunction(int otop, LuaDiscardMarker &s, T... args) {
|
||||
call_cfunction<NRET+1>(otop, args...);
|
||||
pop_any_value(s);
|
||||
}
|
||||
template<int NRET, typename... T>
|
||||
void call_cfunction(int otop, lua_CFunction fn, T... args) {
|
||||
push_any_values(args...);
|
||||
int nret = fn(L_);
|
||||
check_nret(NRET, otop, nret);
|
||||
}
|
||||
|
||||
// Check number of return values: xpected number of return values,
|
||||
// original stack top, and number of declared return values.
|
||||
void check_nret(int xnret, int otop, int nret) const;
|
||||
|
||||
public:
|
||||
template<class... SS>
|
||||
LuaStack(lua_State *L, SS & ... stackslots) {
|
||||
@@ -145,100 +369,57 @@ public:
|
||||
~LuaStack() {};
|
||||
|
||||
int result();
|
||||
|
||||
private:
|
||||
// Push any value on the stack, by type.
|
||||
void push_any_value(LuaNilMarker s) const { lua_pushnil(L_); }
|
||||
void push_any_value(LuaSlot s) const { lua_pushvalue(L_, s); }
|
||||
void push_any_value(const std::string &s) const { lua_pushlstring(L_, s.c_str(), s.size()); }
|
||||
void push_any_value(const char *s) const { lua_pushstring(L_, s); }
|
||||
void push_any_value(double s) const { lua_pushnumber(L_, s); }
|
||||
void push_any_value(int s) const { lua_pushnumber(L_, s); }
|
||||
void push_any_value(bool b) const { lua_pushboolean(L_, b ? 1:0); }
|
||||
|
||||
// Push multiple values on the stack, in order, by type.
|
||||
template<typename T0, typename... T>
|
||||
void push_any_values(T0 arg0, T... args) {
|
||||
push_any_value(arg0);
|
||||
push_any_values(args...);
|
||||
}
|
||||
void push_any_values() {
|
||||
}
|
||||
|
||||
private:
|
||||
// Push everything after the CFunction. Used in the implementation
|
||||
// of the 'call' template function.
|
||||
template<typename... T>
|
||||
void push_after_cfunction(LuaSlot s, T... args) {
|
||||
push_after_cfunction(args...);
|
||||
}
|
||||
template<typename... T>
|
||||
void push_after_cfunction(lua_CFunction f, T... args) {
|
||||
push_any_values(args...);
|
||||
}
|
||||
|
||||
// Call the CFunction, verify that the number of return values is
|
||||
// correct, and pop everything before the CFunction. Used in the
|
||||
// implementation of the 'call' template function.
|
||||
template<int NRET, typename... T>
|
||||
void call_cfunction_and_pop(int otop, LuaSlot s, T... args) {
|
||||
call_cfunction_and_pop<NRET+1>(otop, args...);
|
||||
lua_replace(L_, s);
|
||||
}
|
||||
template<int NRET, typename... T>
|
||||
void call_cfunction_and_pop(int otop, lua_CFunction fn, T... args) {
|
||||
int nret = fn(L_);
|
||||
check_nret(NRET, otop, nret);
|
||||
}
|
||||
|
||||
// Check number of return values: xpected number of return values,
|
||||
// original stack top, and number of declared return values.
|
||||
void check_nret(int xnret, int otop, int nret) const;
|
||||
public:
|
||||
template<typename T>
|
||||
void set(LuaSlot target, T value) const {
|
||||
push_any_value(value);
|
||||
lua_replace(L_, target);
|
||||
}
|
||||
|
||||
int type(LuaSlot s) const { return lua_type(L_, s); }
|
||||
void checktype(LuaSlot s, int type) const { luaL_checktype(L_, s, type); }
|
||||
|
||||
bool isboolean(LuaSlot s) const { return lua_isboolean(L_, s); }
|
||||
bool iscfunction(LuaSlot s) const { return lua_iscfunction(L_, s); }
|
||||
bool isfunction(LuaSlot s) const { return lua_isfunction(L_, s); }
|
||||
bool islightuserdata(LuaSlot s) const { return lua_islightuserdata(L_, s); }
|
||||
bool isnil(LuaSlot s) const { return lua_isnil(L_, s); }
|
||||
bool isnumber(LuaSlot s) const { return lua_type(L_, s) == LUA_TNUMBER; }
|
||||
bool istable(LuaSlot s) const { return lua_type(L_, s) == LUA_TTABLE; }
|
||||
bool isstring(LuaSlot s) const { return lua_type(L_, s) == LUA_TSTRING; }
|
||||
bool istable(LuaSlot s) const { return lua_istable(L_, s); }
|
||||
bool isthread(LuaSlot s) const { return lua_isthread(L_, s); }
|
||||
bool isuserdata(LuaSlot s) const { return lua_isuserdata(L_, s); }
|
||||
bool isnumber(LuaSlot s) const { return lua_type(L_, s) == LUA_TNUMBER; }
|
||||
bool isthread(LuaSlot s) const { return lua_type(L_, s) == LUA_TTHREAD; }
|
||||
bool isfunction(LuaSlot s) const { return lua_type(L_, s) == LUA_TFUNCTION; }
|
||||
bool isboolean(LuaSlot s) const { return lua_type(L_, s) == LUA_TBOOLEAN; }
|
||||
bool isnil(LuaSlot s) const { return lua_type(L_, s) == LUA_TNIL; }
|
||||
|
||||
bool toboolean(LuaSlot s) const { return lua_toboolean(L_, s); }
|
||||
int tointeger(LuaSlot s) const { return lua_tointeger(L_, s); }
|
||||
double tonumber(LuaSlot s) const { return lua_tonumber(L_, s); }
|
||||
std::string tostring(LuaSlot s) const;
|
||||
lua_State *tothread(LuaSlot s) const { return lua_tothread(L_, s); }
|
||||
|
||||
int checkint(LuaSlot s) const { return luaL_checkint(L_, s); }
|
||||
long checklong(LuaSlot s) const { return luaL_checklong(L_, s); }
|
||||
double checknumber(LuaSlot s) const { return luaL_checknumber(L_, s); }
|
||||
std::string checkstring(LuaSlot s) const;
|
||||
void checktype(LuaSlot s, int t) const { return luaL_checktype(L_, s, t); }
|
||||
void checktable(LuaSlot index) const { checktype(index, LUA_TTABLE); }
|
||||
void checkstring(LuaSlot index) const { checktype(index, LUA_TSTRING); }
|
||||
void checknumber(LuaSlot index) const { checktype(index, LUA_TNUMBER); }
|
||||
void checkthread(LuaSlot index) const { checktype(index, LUA_TTHREAD); }
|
||||
void checkfunction(LuaSlot index) const { checktype(index, LUA_TFUNCTION); }
|
||||
void checkboolean(LuaSlot index) const { checktype(index, LUA_TBOOLEAN); }
|
||||
void checknil(LuaSlot index) const { checktype(index, LUA_TNIL); }
|
||||
|
||||
lua_Integer ckinteger(LuaSlot s) const { return luaL_ckinteger(L_, s); }
|
||||
double cknumber(LuaSlot s) const { return luaL_cknumber(L_, s); }
|
||||
std::string ckstring(LuaSlot s) const;
|
||||
lua_State *ckthread(LuaSlot s) const { return luaL_ckthread(L_, s); }
|
||||
|
||||
void clearmetatable(LuaSlot tab) const;
|
||||
void setmetatable(LuaSlot tab, LuaSlot mt) const;
|
||||
void getmetatable(LuaSlot mt, LuaSlot tab) const;
|
||||
void checknometa(LuaSlot index) const;
|
||||
|
||||
void newtable(LuaSlot target) const;
|
||||
|
||||
int next(LuaSlot tab, LuaSlot key, LuaSlot value) const;
|
||||
bool isemptytable(LuaSlot s) const;
|
||||
|
||||
template<typename KT>
|
||||
void rawget(LuaSlot target, LuaSlot tab, KT key) const {
|
||||
bool equal(LuaSlot v1, LuaSlot v2) {
|
||||
return lua_equal(L_, v1, v2);
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
void set(T1 &target, T2 value) const {
|
||||
push_any_value(value);
|
||||
pop_any_value(target);
|
||||
}
|
||||
|
||||
template<typename RT, typename KT>
|
||||
void rawget(RT &target, LuaSlot tab, KT key) const {
|
||||
push_any_value(key);
|
||||
lua_rawget(L_, tab);
|
||||
lua_replace(L_, target);
|
||||
pop_any_value(target);
|
||||
}
|
||||
|
||||
template<typename KT, typename VT>
|
||||
@@ -254,25 +435,24 @@ public:
|
||||
lua_setfield(L_, tab, field);
|
||||
}
|
||||
|
||||
void getfield(LuaSlot target, LuaSlot tab, const char *field) const {
|
||||
template<typename RT>
|
||||
void getfield(RT &target, LuaSlot tab, const char *field) const {
|
||||
lua_getfield(L_, tab, field);
|
||||
lua_replace(L_, target);
|
||||
pop_any_value(target);
|
||||
}
|
||||
|
||||
// Call invokes any C function. It pushes the arguments on the stack,
|
||||
// calls the cfunction, verifies that the number of return values is as
|
||||
// expected, and pops the return values into LuaVars.
|
||||
template<typename... T>
|
||||
void call(T... args) {
|
||||
int otop = lua_gettop(L_);
|
||||
push_after_cfunction(args...);
|
||||
call_cfunction_and_pop<0>(otop, args...);
|
||||
void call(T&... args) {
|
||||
call_cfunction<0>(lua_gettop(L_), args...);
|
||||
}
|
||||
};
|
||||
|
||||
class LuaFunctionReg {
|
||||
private:
|
||||
int mode_;
|
||||
const char *mode_;
|
||||
const char *name_;
|
||||
lua_CFunction func_;
|
||||
LuaFunctionReg *next_;
|
||||
@@ -282,20 +462,15 @@ private:
|
||||
public:
|
||||
using List = std::vector<const LuaFunctionReg *>;
|
||||
|
||||
LuaFunctionReg(int m, const char *n, lua_CFunction f);
|
||||
LuaFunctionReg(const char *mode, const char *n, lua_CFunction f);
|
||||
static List all();
|
||||
|
||||
int get_mode() const { return mode_; }
|
||||
const char *get_mode() const { return mode_; }
|
||||
const char *get_name() const { return name_; }
|
||||
lua_CFunction get_func() const { return func_; }
|
||||
};
|
||||
|
||||
#define LuaDefineHidden(name) LuaDefineCore(name, 0)
|
||||
#define LuaDefineGlobalFunction(name) LuaDefineCore(name, 1)
|
||||
#define LuaDefineGlobalMethod(name) LuaDefineCore(name, 2)
|
||||
#define LuaDefineClassMethod(name) LuaDefineCore(name, 3)
|
||||
|
||||
#define LuaDefineCore(name, mode) \
|
||||
#define LuaDefine(name, mode) \
|
||||
int name(lua_State *L); \
|
||||
LuaFunctionReg reg_##name(mode, #name, name); \
|
||||
int name(lua_State *L)
|
||||
|
||||
Reference in New Issue
Block a user