Files
integration/luprex/core/cpp/luastack.hpp

520 lines
17 KiB
C++
Raw Normal View History

2021-01-02 13:31:18 -05:00
/////////////////////////////////////////////////////////
//
//
2021-01-12 15:49:05 -05:00
// LUASTACK
2021-01-02 13:31:18 -05:00
//
2021-01-17 16:23:10 -05:00
// The standard lua C API asks you to work with a stack machine. You're supposed
// to manually push and pop values on the lua stack. I find this difficult, I
// find it hard to remember what stack position contains what value.
//
// To make it easier, I've created this module, "LuaStack." This module
// creates the illusion that you're working with local variables that contain
// lua values.
2021-01-02 13:31:18 -05:00
//
// 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.
//
//
2021-01-12 15:49:05 -05:00
// LUADEFINE
2021-01-02 13:31:18 -05:00
//
2021-01-12 15:49:05 -05:00
// LuaDefine is a macro that defines a C function which is
// exposed to lua. It creates a global registry of functions
// created with LuaDefine. You use it like so:
2021-01-02 13:31:18 -05:00
//
// 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.
//
//
/////////////////////////////////////////////////////////
2020-11-27 13:21:07 -05:00
#ifndef LUASTACK_HPP
#define LUASTACK_HPP
extern "C" {
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "luajit.h"
}
#include <string>
2020-12-05 18:57:53 -05:00
#include <vector>
2020-11-27 13:21:07 -05:00
class LuaSlot {
protected:
int index_;
private:
inline operator int() const {
return index_;
}
public:
LuaSlot() {
index_ = 0;
}
int index() const {
return index_;
}
friend class LuaStack;
};
class LuaArg : public LuaSlot {};
class LuaRet : public LuaSlot {};
class LuaVar : public LuaSlot {};
2020-12-05 18:57:53 -05:00
class LuaSpecial : public LuaSlot {
public:
LuaSpecial(int n) {
index_ = n;
}
};
extern LuaSpecial LuaRegistry;
extern LuaSpecial LuaGlobals;
2020-11-27 13:21:07 -05:00
class LuaUpvalue : public LuaSlot {
public:
LuaUpvalue(int n) {
index_ = lua_upvalueindex(n);
}
};
2020-12-05 18:57:53 -05:00
class LuaNilMarker {};
extern LuaNilMarker LuaNil;
2021-01-02 13:31:18 -05:00
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); }
using LuaDeleterFn = void (*)(void *);
using LuaTypeTag = lua_CFunction;
template<typename T>
int LuaTypeTagValue(lua_State *L) { return 0; }
2020-11-27 13:21:07 -05:00
class LuaStack {
private:
int narg_;
int ngap_;
int nvar_;
int nret_;
int argpos_;
int gappos_;
int varpos_;
int retpos_;
int rettop_;
int finaltop_;
lua_State *L_;
template<int NARG, int NVAR, int NRET, class... SS>
void count_slots(LuaArg &v, SS & ... stackslots)
{
count_slots<NARG+1, NRET, NVAR>(stackslots...);
}
template<int NARG, int NVAR, int NRET, class... SS>
void count_slots(LuaVar &v, SS & ... stackslots)
{
count_slots<NARG, NVAR+1, NRET>(stackslots...);
}
template<int NARG, int NVAR, int NRET, class... SS>
void count_slots(LuaRet &v, SS & ... stackslots)
{
count_slots<NARG, NVAR, NRET+1>(stackslots...);
}
template<int NARG, int NVAR, int NRET>
void count_slots() {
count_slots_finalize(NARG, NVAR, NRET);
}
void count_slots_finalize(int narg, int nvar, int nret);
template<class... SS>
void assign_slots(int argp, int varp, int retp, LuaArg &v, SS & ... stackslots) {
v.index_ = argp;
assign_slots(argp + 1, varp, retp, stackslots...);
}
template<class... SS>
void assign_slots(int argp, int varp, int retp, LuaVar &v, SS & ... stackslots) {
v.index_ = varp;
assign_slots(argp, varp+1, retp, stackslots...);
}
template<class... SS>
void assign_slots(int argp, int varp, int retp, LuaRet &v, SS & ... stackslots) {
v.index_ = retp;
assign_slots(argp, varp, retp+1, stackslots...);
}
void assign_slots(int argp, int varp, int retp) {}
void clear_frame();
2020-12-05 18:57:53 -05:00
private:
// Push any value on the stack, by type.
2021-01-02 13:31:18 -05:00
void push_any_value(LuaNewTableMarker s) const { lua_newtable(L_); }
2020-12-05 18:57:53 -05:00
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); }
2021-01-02 13:31:18 -05:00
void push_any_value(float s) const { lua_pushnumber(L_, s); }
2020-12-05 18:57:53 -05:00
void push_any_value(double s) const { lua_pushnumber(L_, s); }
2021-01-02 13:31:18 -05:00
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(lua_CFunction s) const { lua_pushcfunction(L_, s); }
2020-12-05 18:57:53 -05:00
void push_any_value(bool b) const { lua_pushboolean(L_, b ? 1:0); }
2021-01-02 13:31:18 -05:00
// 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); }
2020-12-05 18:57:53 -05:00
// 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() {
}
2021-01-02 13:31:18 -05:00
// 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);
2020-12-05 18:57:53 -05:00
}
2021-01-02 13:31:18 -05:00
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);
2020-12-05 18:57:53 -05:00
}
template<int NRET, typename... T>
2021-01-02 13:31:18 -05:00
void call_cfunction(int otop, std::string &s, T... args) {
call_cfunction<NRET+1>(otop, args...);
pop_any_value(s);
2020-12-05 18:57:53 -05:00
}
template<int NRET, typename... T>
2021-01-02 13:31:18 -05:00
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...);
2020-12-05 18:57:53 -05:00
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;
2021-01-02 13:31:18 -05:00
template<typename T>
static void delete_pointer(void *p) { delete (T*)p; }
static void do_not_delete(void *p) { }
2020-11-27 13:21:07 -05:00
public:
2021-01-02 13:31:18 -05:00
template<class... SS>
LuaStack(lua_State *L, SS & ... stackslots) {
L_ = L;
count_slots<0, 0, 0>(stackslots...);
if (lua_gettop(L) < narg_) {
luaL_error(L, "not enough arguments on stack");
}
assign_slots(argpos_, varpos_, retpos_, stackslots...);
clear_frame();
2020-12-05 18:57:53 -05:00
}
2020-11-27 13:21:07 -05:00
2021-01-02 13:31:18 -05:00
~LuaStack() {};
int result();
public:
2020-12-05 18:57:53 -05:00
int type(LuaSlot s) const { return lua_type(L_, s); }
2021-01-02 13:31:18 -05:00
void checktype(LuaSlot s, int type) const { luaL_checktype(L_, s, type); }
2020-11-27 13:21:07 -05:00
2021-01-02 13:31:18 -05:00
bool istable(LuaSlot s) const { return lua_type(L_, s) == LUA_TTABLE; }
2020-11-27 14:24:37 -05:00
bool isstring(LuaSlot s) const { return lua_type(L_, s) == LUA_TSTRING; }
2021-01-02 13:31:18 -05:00
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; }
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); }
2021-02-25 14:09:16 -05:00
2020-11-27 14:24:37 -05:00
void clearmetatable(LuaSlot tab) const;
void setmetatable(LuaSlot tab, LuaSlot mt) const;
2021-01-02 13:31:18 -05:00
void getmetatable(LuaSlot mt, LuaSlot tab) const;
2020-11-27 14:24:37 -05:00
void checknometa(LuaSlot index) const;
void newtable(LuaSlot target) const;
2021-02-17 13:38:22 -05:00
lua_State *newthread(LuaSlot target) const;
2021-02-09 17:15:54 -05:00
2021-02-10 16:22:24 -05:00
void makesubtable(LuaSlot sub, LuaSlot tab, const char *name) const;
2021-02-09 17:15:54 -05:00
2020-11-27 14:24:37 -05:00
int next(LuaSlot tab, LuaSlot key, LuaSlot value) const;
2020-11-27 13:21:07 -05:00
2021-02-10 16:22:24 -05:00
void makeclass(LuaSlot tab, LuaSlot name) const;
void makeclass(LuaSlot tab, const char *name) const {
push_any_value(name);
LuaSpecial classname(lua_gettop(L_));
makeclass(tab, classname);
lua_pop(L_, 1);
}
bool equal(LuaSlot v1, LuaSlot v2) const {
2021-01-02 13:31:18 -05:00
return lua_equal(L_, v1, v2);
}
2021-02-10 16:22:24 -05:00
bool equal(LuaSlot v1, const char *name) const {
push_any_value(name);
bool result = lua_equal(L_, v1, -1);
lua_pop(L_, 1);
return result;
}
2021-01-02 13:31:18 -05:00
template<typename T1, typename T2>
void set(T1 &target, T2 value) const {
push_any_value(value);
pop_any_value(target);
2021-02-07 17:26:48 -05:00
}
template<typename RT, typename KT>
void gettable(RT &target, LuaSlot tab, KT key) const {
push_any_value(key);
lua_gettable(L_, tab);
pop_any_value(target);
2021-01-02 13:31:18 -05:00
}
template<typename RT, typename KT>
void rawget(RT &target, LuaSlot tab, KT key) const {
2020-12-05 18:57:53 -05:00
push_any_value(key);
2020-11-27 13:21:07 -05:00
lua_rawget(L_, tab);
2021-01-02 13:31:18 -05:00
pop_any_value(target);
2020-11-27 13:21:07 -05:00
}
2020-12-05 18:57:53 -05:00
template<typename KT, typename VT>
void rawset(LuaSlot tab, KT key, VT value) const {
push_any_value(key);
push_any_value(value);
2020-11-27 13:21:07 -05:00
lua_rawset(L_, tab);
}
2021-02-10 16:47:45 -05:00
// template<typename VT>
// void rawset(LuaSlot tab, const char *field, VT value) const {
// push_any_value(value);
// lua_rawset(L_, tab, field);
// }
// template<typename RT>
// void rawget(RT &target, LuaSlot tab, const char *field) const {
// lua_rawget(L_, tab, field);
// pop_any_value(target);
// }
2020-11-27 13:21:07 -05:00
2020-12-05 18:57:53 -05:00
// 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>
2021-01-02 13:31:18 -05:00
void call(T&... args) {
call_cfunction<0>(lua_gettop(L_), args...);
2020-12-05 18:57:53 -05:00
}
};
2020-12-05 18:57:53 -05:00
class LuaFunctionReg {
private:
2021-01-02 13:31:18 -05:00
const char *mode_;
2020-12-05 18:57:53 -05:00
const char *name_;
lua_CFunction func_;
LuaFunctionReg *next_;
static LuaFunctionReg *LuaFunctionRegistry;
2020-11-27 13:21:07 -05:00
2020-12-05 18:57:53 -05:00
public:
using List = std::vector<const LuaFunctionReg *>;
2021-01-02 13:31:18 -05:00
LuaFunctionReg(const char *mode, const char *n, lua_CFunction f);
2020-12-05 18:57:53 -05:00
static List all();
2021-01-02 13:31:18 -05:00
const char *get_mode() const { return mode_; }
2020-12-05 18:57:53 -05:00
const char *get_name() const { return name_; }
lua_CFunction get_func() const { return func_; }
2020-11-27 13:21:07 -05:00
};
2021-01-02 13:31:18 -05:00
#define LuaDefine(name, mode) \
2020-12-05 18:57:53 -05:00
int name(lua_State *L); \
LuaFunctionReg reg_##name(mode, #name, name); \
int name(lua_State *L)
2021-01-12 15:49:05 -05:00
#define LuaStringify(x) #x
#define LuaAssert(L, x) if (!(x)) { luaL_error((L), "Assert failed: %s (file %s line %d)", LuaStringify(x), __FILE__, __LINE__); }
2020-11-27 13:21:07 -05:00
#endif // LUASTACK_HPP