361 lines
11 KiB
C++
361 lines
11 KiB
C++
#ifndef LUNE_H
|
|
#define LUNE_H
|
|
|
|
|
|
#include "lua.h"
|
|
#include "lauxlib.h"
|
|
#include "lualib.h"
|
|
|
|
|
|
#include "luaxx.hpp"
|
|
#include <string>
|
|
|
|
namespace lua
|
|
{
|
|
class object
|
|
{
|
|
state &myst;
|
|
int myref;
|
|
public:
|
|
object(state &myst):myst(myst)
|
|
{
|
|
myref=luaL_ref(myst,LUA_REGISTRYINDEX);
|
|
}
|
|
~object()
|
|
{
|
|
luaL_unref(myst,LUA_REGISTRYINDEX,myref);
|
|
}
|
|
void Get()
|
|
{
|
|
lua_rawgeti(myst,LUA_REGISTRYINDEX,myref);
|
|
}
|
|
state &GetState(){return myst;};
|
|
};
|
|
|
|
class local_object
|
|
{
|
|
state myst;
|
|
int myref;
|
|
static object *mytbl;
|
|
public:
|
|
local_object(lua_State *L)
|
|
{
|
|
|
|
myst=state(L);
|
|
//LOG<<"Creating local object...\n";
|
|
//StackDump(L);
|
|
if(!mytbl)
|
|
{
|
|
//LOG<<" Metable...\n";
|
|
myst.newtable(); //2
|
|
if(myst.newmetatable("WEAKTABLE"))//3
|
|
{
|
|
//StackDump(L);
|
|
myst.push("kv"); //4
|
|
myst.setfield("__mode");//3
|
|
//LOG<<" Setting Metable...\n";
|
|
//StackDump(L);
|
|
}
|
|
//LOG<<" Attaching to holder...\n";
|
|
|
|
//myst.setfield("__metatable");//2
|
|
lua_setmetatable(myst,-1);
|
|
mytbl=new object(myst);
|
|
//StackDump(L);
|
|
//LOG<<" Done Metatable...\n";
|
|
}
|
|
//StackDump(L);
|
|
mytbl->Get();
|
|
//LOG<<" Got my table...\n";
|
|
//StackDump(L);
|
|
myst.insert(-2);
|
|
myref=luaL_ref(myst,-2);
|
|
//LOG<<"Before pop:";
|
|
//StackDump(L);
|
|
myst.pop(1);
|
|
GetTable();
|
|
//LOG<<"========Done...\n"<<"Ref="<<myref<<"\n";
|
|
//mytbl->Get();
|
|
//StackDump(L);
|
|
//LOG<<"===========================\n";
|
|
}
|
|
~local_object()
|
|
{
|
|
//LOG<<"Deleting local object...\n";
|
|
ReleaseTable();
|
|
}
|
|
void ReleaseTable()
|
|
{
|
|
mytbl->Get();
|
|
int pos=myst.gettop();
|
|
luaL_unref(myst,pos,myref);
|
|
myst.remove(pos);
|
|
}
|
|
state GetState(){return myst;}
|
|
void GetTable()
|
|
{
|
|
//LOG<<"Getting ref="<<myref<<"\n";
|
|
//StackDump(myst);
|
|
//LOG<<"Tbl preget\n";
|
|
mytbl->Get();
|
|
int pos=myst.gettop();
|
|
//StackDump(myst);
|
|
//LOG<<"Tbl get\n";
|
|
//int pos=myst.gettop();
|
|
lua_rawgeti(myst,pos,myref);
|
|
//StackDump(myst);
|
|
//LOG<<"Done\n";
|
|
myst.remove(pos);
|
|
}
|
|
protected:
|
|
|
|
};
|
|
};
|
|
|
|
template <typename T,bool GC=true>
|
|
class Lune
|
|
{
|
|
public:
|
|
typedef struct
|
|
{
|
|
T *pT;
|
|
int tableref;
|
|
} userdataType;
|
|
|
|
typedef int (T::*mfp)(lua_State *L);
|
|
typedef struct
|
|
{
|
|
const char *name;
|
|
mfp mfunc;
|
|
} RegType;
|
|
|
|
static void Register(lua_State *L)
|
|
{
|
|
lua_newtable(L);
|
|
int methods = lua_gettop(L);
|
|
|
|
luaL_newmetatable(L, T::className);
|
|
int metatable = lua_gettop(L);
|
|
|
|
// store method table in globals so that
|
|
// scripts can add functions written in Lua.
|
|
lua_pushvalue(L, methods);
|
|
lua_setglobal(L, T::className);
|
|
|
|
lua_pushliteral(L, "__metatable");
|
|
lua_pushvalue(L, methods);
|
|
lua_settable(L, metatable); // hide metatable from Lua getmetatable()
|
|
|
|
lua_pushliteral(L, "__index");
|
|
lua_pushcfunction(L, index_T);
|
|
lua_settable(L, metatable);
|
|
|
|
//lua_pushliteral(L, "__name");
|
|
//lua_pushstring(L, T::className);
|
|
//lua_settable(L, metatable);
|
|
|
|
lua_pushliteral(L, "__newindex");
|
|
lua_pushcfunction(L, newindex_T);
|
|
lua_settable(L, metatable);
|
|
|
|
lua_pushliteral(L, "__instances");
|
|
lua_newtable(L);
|
|
lua_settable(L, metatable);
|
|
if(GC)
|
|
{
|
|
lua_pushliteral(L, "__gc");
|
|
lua_pushcfunction(L, gc_T);
|
|
lua_settable(L, metatable);
|
|
}
|
|
|
|
|
|
lua_newtable(L); // metatable for method table
|
|
int mt = lua_gettop(L);
|
|
lua_pushliteral(L, "__call");
|
|
lua_pushcfunction(L, new_T);
|
|
lua_pushliteral(L, "new");
|
|
lua_pushvalue(L, -2); // dup new_T function
|
|
lua_settable(L, methods); // add new_T to method table
|
|
lua_settable(L, mt); // mt.__call = new_T
|
|
lua_setmetatable(L, methods);
|
|
//LOG<<"lune: registered class \""<<T::className<<"\"\n";
|
|
// fill method table with methods from class T
|
|
for (RegType *l = T::methods; l->name; l++)
|
|
{
|
|
/* edited by Snaily: shouldn't it be const RegType *l ... ? */
|
|
lua_pushstring(L, l->name);
|
|
lua_pushlightuserdata(L, (void*)l);
|
|
lua_pushcclosure(L, thunk, 1);
|
|
lua_settable(L, methods);
|
|
//LOG<<"lune: method \""<<l->name<<"\"\n";
|
|
}
|
|
|
|
lua_pop(L, 2); // drop metatable and method table
|
|
};
|
|
static void GetTable(lua_State *L,T *p)
|
|
{
|
|
GetTableEx(L,p->GetTableId());
|
|
}
|
|
static void GetTableEx(lua_State *L,int id)
|
|
{
|
|
lua::state s(L);
|
|
s.getmetatable(T::className);
|
|
s.getfield("__instances");
|
|
int ins=s.gettop();
|
|
lua_rawgeti(L, ins, id);
|
|
s.insert(-3);
|
|
s.pop(2);
|
|
}
|
|
static T *check(lua_State *L, int narg)
|
|
{
|
|
userdataType *ud =
|
|
static_cast<userdataType*>(luaL_checkudata(L, narg, T::className)); //TODO FIX THIs..
|
|
//(lua_touserdata(L, narg));//
|
|
if(!ud) luaL_error(L, "Bad argument %d: expected type %s", narg, T::className);
|
|
return ud->pT; // pointer to T object
|
|
}
|
|
protected:
|
|
private:
|
|
|
|
static int RegTable(lua_State *L)
|
|
{
|
|
// LOG<<"Regging....\n";
|
|
//lua::StackDump(L);
|
|
|
|
lua::state s(L);
|
|
int ssize=s.gettop();
|
|
//s.getglobal(T::className);
|
|
s.getmetatable(T::className);
|
|
s.getfield("__instances");
|
|
int ins=s.gettop();
|
|
s.newtable();
|
|
int id=luaL_ref(L,ins);
|
|
//LOG<<"After reg:\n";
|
|
//lua::StackDump(L);
|
|
s.settop(ssize);
|
|
return id;
|
|
}
|
|
static void UnregTable(lua_State *L,int id)
|
|
{
|
|
lua::state s(L);
|
|
s.getmetatable(T::className);
|
|
s.getfield("__instances");
|
|
int ins=s.gettop();
|
|
//LOG<<"Unreg table id:"<<id<<"stack dump:\n";
|
|
//lua::StackDump(L);
|
|
luaL_unref(L,ins,id);
|
|
}
|
|
static int index_T(lua_State *L) // calls with (table, key), return value
|
|
{
|
|
lua::state st(L);
|
|
std::string key=st.as<std::string>(-1);
|
|
T *p=check(L,1);
|
|
GetTable(L,p);
|
|
st.insert(-2);
|
|
//LOG<<"Index:\n";
|
|
//lua::StackDump(L);
|
|
lua_rawget(L,-2); //try getting from normal table
|
|
if(st.is<lua::nil>()) //failed
|
|
{
|
|
st.pop(2);
|
|
st.getglobal(T::className); //try class tables then
|
|
st.push(key);
|
|
st.gettable();
|
|
}
|
|
return 1;
|
|
}
|
|
static int newindex_T(lua_State *L)
|
|
{
|
|
//LOG<<"New index....\n";
|
|
//lua::StackDump(L);
|
|
|
|
lua::state st(L);
|
|
T *p=check(L,1);
|
|
GetTable(L,p);
|
|
//st.insert(-3);
|
|
//LOG<<"Before set:\n";
|
|
st.insert(-3);
|
|
//lua::StackDump(L);
|
|
lua_rawset(L,-3);
|
|
return 0;
|
|
}
|
|
static int thunk(lua_State *L)
|
|
{
|
|
//LOG<<"Size of stack:"<<lua_gettop(L)<<"\n";
|
|
//lua::StackDump(L);
|
|
if(lua_gettop(L)<1)
|
|
luaL_error(L,"Member function called without 'self'");
|
|
//LOG<<"Size of stack after:"<<lua_gettop(L)<<"\n";
|
|
// stack has userdata, followed by method args
|
|
T *obj = check(L, 1); // get 'self', or if you prefer, 'this'
|
|
//T *obj=static_cast<userdataType*>(lua_touserdata(L,1))->pT;
|
|
lua_remove(L, 1); // remove self so member function args start at index 1
|
|
// get member function from upvalue
|
|
RegType *l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1)));
|
|
return (obj->*(l->mfunc))(L); // call member function
|
|
}
|
|
static int gc_T(lua_State *L)
|
|
{
|
|
//lua_getfield(L,,"__ud");
|
|
//LOG<<"Garbage collecting.\n";
|
|
//lua::StackDump(L);
|
|
userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
|
|
T *obj = ud->pT;
|
|
|
|
delete obj; // call destructor for T objects
|
|
UnregTable(L,ud->tableref);
|
|
return 0;
|
|
}
|
|
static int new_T(lua_State *L)
|
|
{
|
|
//LOG<<"Pre build:"<<lua_gettop(L)<<"\n";
|
|
//lua::StackDump(L);
|
|
lua_remove(L, 1); // use classname:new(), instead of classname.new()
|
|
//lua_newtable(L);
|
|
int id=RegTable(L);
|
|
//LOG<<"Registred as:"<<id<<"\n";
|
|
//int ssize=lua_gettop(L);
|
|
T *obj = new T(L,id); // call constructor for T objects
|
|
lua_settop(L,0); //no need for parameters later.
|
|
//LOG<<"Post build:"<<lua_gettop(L)<<"\t";
|
|
//lua::StackDump(L);
|
|
//LOG<<"TSOP\n";
|
|
|
|
userdataType *ud =
|
|
static_cast<userdataType*>(lua_newuserdata(L, sizeof(userdataType)));
|
|
//lua::StackDump(L);
|
|
luaL_getmetatable(L, T::className); // lookup metatable in Lua registry
|
|
lua_setmetatable(L,-2);
|
|
//LOG<<"metatable set\n";
|
|
|
|
//lua::StackDump(L);
|
|
GetTable(L,obj);
|
|
lua_pushliteral(L,"__obj");
|
|
lua_pushvalue(L,-3);
|
|
lua_settable(L,-3);
|
|
lua_pop(L,1);
|
|
//LOG<<"Object referenced\n";
|
|
//lua::StackDump(L);
|
|
//T *p = new(ud) T(L); // call constructor for T objects
|
|
//lua::StackDump(L);
|
|
ud->pT = obj; // store pointer to object in userdata
|
|
ud->tableref=id;
|
|
//luaL_getmetatable(L, T::className); // lookup metatable in Lua registry
|
|
//lua_setmetatable(L, tableindex);
|
|
//lua::StackDump(L);
|
|
//LOG<<"Push done\n";
|
|
return 1; // userdata containing pointer to T object
|
|
}
|
|
Lune() {}; //non constructable...
|
|
};
|
|
#define method(class, name) {#name, &class::name}
|
|
#define DEF_LUNE(class) static const char className[];\
|
|
static Lune<class>::RegType methods[];
|
|
#define DEF_LUNE_NOGC(class) static const char className[];\
|
|
static Lune<class,false>::RegType methods[];
|
|
#define IMP_LUNE(class,lua_name) const char class::className[]=#lua_name;
|
|
#define LUNE_METHODS_START(class) Lune<class>::RegType class::methods[] = {
|
|
#define LUNE_METHODS_START_NOGC(class) Lune<class,false>::RegType class::methods[] = {
|
|
#define LUNE_METHODS_END() {0,0}}
|
|
#endif // LUNE_H
|