dungeon/libs/kaguya-1.3.2/include/kaguya/metatable.hpp
Adrian Hedqvist ec131d8bda Too much stuff
* Implemented lua using kaguya, only for config files for now
* Moved color struct to its own header
* statically link glew instead of including the source in the project
* Other stuff that I don't remember
2017-10-18 12:25:25 +02:00

696 lines
25 KiB
C++

// Copyright satoren
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#include <string>
#include <vector>
#include <map>
#include <typeinfo>
#include "kaguya/config.hpp"
#include "kaguya/push_any.hpp"
#include "kaguya/native_function.hpp"
#include "kaguya/lua_ref_function.hpp"
#define KAGUYA_PROPERTY_PREFIX "_prop_"
namespace kaguya {
#define KAGUYA_PP_STRUCT_TDEF_REP(N) KAGUYA_PP_CAT(class A, N) = void
#define KAGUYA_PP_STRUCT_TEMPLATE_DEF_REPEAT(N) \
KAGUYA_PP_REPEAT_ARG(N, KAGUYA_PP_STRUCT_TDEF_REP)
template <KAGUYA_PP_STRUCT_TEMPLATE_DEF_REPEAT(KAGUYA_CLASS_MAX_BASE_CLASSES)>
struct MultipleBase {};
#undef KAGUYA_PP_STRUCT_TDEF_REP
#undef KAGUYA_PP_STRUCT_TEMPLATE_DEF_REPEAT
}
namespace kaguya {
struct LuaCodeChunk {
LuaCodeChunk(const std::string &src, const std::string &name = "")
: code_(src), chunk_name_(name) {}
LuaCodeChunk(const char *src, const char *name = "")
: code_(src), chunk_name_(name ? name : "") {}
std::string code_;
std::string chunk_name_;
};
/// @ingroup lua_type_traits
/// @brief lua_type_traits for LuaCodeChunk
template <> struct lua_type_traits<LuaCodeChunk> {
static int push(lua_State *state, const LuaCodeChunk &ref) {
int status = luaL_loadbuffer(
state, ref.code_.c_str(), ref.code_.size(),
ref.chunk_name_.empty() ? ref.code_.c_str() : ref.chunk_name_.c_str());
if (!except::checkErrorAndThrow(status, state)) {
return 0;
}
return 1;
}
};
struct LuaCodeChunkExecute : LuaCodeChunk {
LuaCodeChunkExecute(const std::string &src, const std::string &name = "")
: LuaCodeChunk(src, name) {}
LuaCodeChunkExecute(const char *src, const char *name = "")
: LuaCodeChunk(src, name) {}
};
typedef LuaCodeChunkExecute LuaCodeChunkResult;
/// @ingroup lua_type_traits
/// @brief lua_type_traits for LuaCodeChunkResult
template <> struct lua_type_traits<LuaCodeChunkExecute> {
static int push(lua_State *state, const LuaCodeChunkExecute &ref) {
int status = luaL_loadbuffer(
state, ref.code_.c_str(), ref.code_.size(),
ref.chunk_name_.empty() ? ref.code_.c_str() : ref.chunk_name_.c_str());
if (!except::checkErrorAndThrow(status, state)) {
return 0;
}
status = lua_pcall_wrap(state, 0, 1);
if (!except::checkErrorAndThrow(status, state)) {
return 0;
}
return 1;
}
};
namespace Metatable {
typedef std::map<std::string, AnyDataPusher> PropMapType;
typedef std::map<std::string, AnyDataPusher> MemberMapType;
inline bool is_property_key(const char *keyname) {
return keyname &&
strncmp(keyname, KAGUYA_PROPERTY_PREFIX,
sizeof(KAGUYA_PROPERTY_PREFIX) - 1) != 0;
}
inline int property_index_function(lua_State *L) {
// Lua
// local arg = {...};local metatable = arg[1];
// return function(table, index)
// if string.find(index,KAGUYA_PROPERTY_PREFIX)~=0 then
// local propfun = metatable[KAGUYA_PROPERTY_PREFIX ..index];
// if propfun then return propfun(table) end
// end
// return metatable[index]
// end
static const int table = 1;
static const int key = 2;
static const int metatable = lua_upvalueindex(1);
const char *strkey = lua_tostring(L, key);
if (lua_type(L, 1) == LUA_TUSERDATA && is_property_key(strkey)) {
int type = lua_getfield_rtype(
L, metatable, (KAGUYA_PROPERTY_PREFIX + std::string(strkey)).c_str());
if (type == LUA_TFUNCTION) {
lua_pushvalue(L, table);
lua_call(L, 1, 1);
return 1;
}
}
lua_pushvalue(L, key);
lua_gettable(L, metatable);
return 1;
}
inline int property_newindex_function(lua_State *L) {
// Lua
// local arg = {...};local metatable = arg[1];
// return function(table, index, value)
// if type(table) == 'userdata' then
// if string.find(index,KAGUYA_PROPERTY_PREFIX)~=0 then
// local propfun = metatable[KAGUYA_PROPERTY_PREFIX..index];
// if propfun then return propfun(table,value) end
// end
// end
// rawset(table,index,value)
// end
static const int table = 1;
static const int key = 2;
static const int value = 3;
static const int metatable = lua_upvalueindex(1);
const char *strkey = lua_tostring(L, 2);
if (lua_type(L, 1) == LUA_TUSERDATA && is_property_key(strkey)) {
int type = lua_getfield_rtype(
L, metatable, (KAGUYA_PROPERTY_PREFIX + std::string(strkey)).c_str());
if (type == LUA_TFUNCTION) {
lua_pushvalue(L, table);
lua_pushvalue(L, value);
lua_call(L, 2, 0);
return 0;
}
}
lua_pushvalue(L, key);
lua_pushvalue(L, value);
lua_rawset(L, table);
return 0;
}
inline int multiple_base_index_function(lua_State *L) {
// Lua
// local arg = {...};local metabases = arg[1];
// return function(t, k)
// for i = 1,#metabases do
// local v = metabases[i][k]
// if v then
// t[k] = v
// return v end
// end
// end
static const int table = 1;
static const int key = 2;
static const int metabases = lua_upvalueindex(1);
lua_pushnil(L);
while (lua_next(L, metabases) != 0) {
if (lua_type(L, -1) == LUA_TTABLE) {
lua_pushvalue(L, key);
int type = lua_gettable_rtype(L, -2);
if (type != LUA_TNIL) {
lua_pushvalue(L, key);
lua_pushvalue(L, -2);
lua_settable(L, table);
return 1;
}
}
lua_settop(L, 3); // pop value
}
return 0;
}
inline int call_constructor_function(lua_State *L) {
// function(t,...) return t.new(...) end
lua_getfield(L, 1, "new");
lua_replace(L, 1);
lua_call(L, lua_gettop(L) - 1, LUA_MULTRET);
return lua_gettop(L);
}
inline void get_call_constructor_metatable(lua_State *L) {
static int key = 0;
int ttype = lua_rawgetp_rtype(L, LUA_REGISTRYINDEX, &key);
if (ttype != LUA_TTABLE) {
lua_pop(L, 1);
lua_createtable(L, 0, 1);
lua_pushstring(L, "__call");
lua_pushcfunction(L, &call_constructor_function);
lua_rawset(L, -3);
lua_pushvalue(L, -1);
lua_rawsetp(L, LUA_REGISTRYINDEX, &key);
}
}
inline void setMembers(lua_State *state, int metatable_index,
const MemberMapType &member_map,
const PropMapType &property_map) {
for (MemberMapType::const_iterator it = member_map.begin();
it != member_map.end(); ++it) {
util::one_push(state, it->first);
util::one_push(state, it->second);
lua_rawset(state, metatable_index);
}
for (PropMapType::const_iterator it = property_map.begin();
it != property_map.end(); ++it) {
util::one_push(state, KAGUYA_PROPERTY_PREFIX + it->first);
util::one_push(state, it->second);
lua_rawset(state, metatable_index);
}
}
inline void setPropertyIndexMetamethod(lua_State *state, int metatable_index) {
lua_pushstring(state, "__index");
lua_pushvalue(state, metatable_index);
lua_pushcclosure(state, &property_index_function, 1);
lua_rawset(state, metatable_index);
}
inline void setPropertyNewIndexMetamethod(lua_State *state,
int metatable_index) {
lua_pushstring(state, "__newindex");
lua_pushvalue(state, metatable_index);
lua_pushcclosure(state, &property_newindex_function, 1);
lua_rawset(state, metatable_index);
}
inline void setMultipleBase(lua_State *state, int metatable_index,
int metabase_array_index) {
lua_createtable(state, 0, 1);
int newmetaindex = lua_gettop(state);
lua_pushstring(state, "__index");
lua_pushvalue(state, metabase_array_index); // bind metabase_array to
// multiple_base_index_function
lua_pushcclosure(state, &multiple_base_index_function, 1);
lua_rawset(state,
newmetaindex); // newmeta["__index"] = multiple_base_index_function
lua_setmetatable(state, metatable_index); // metatable.setMetatable(newmeta);
}
}
/// class binding interface.
template <typename class_type, typename base_class_type = void>
class UserdataMetatable {
public:
UserdataMetatable() {
addStaticFunction("__gc", &class_userdata::destructor<ObjectWrapperBase>);
KAGUYA_STATIC_ASSERT(is_registerable<class_type>::value ||
!traits::is_std_vector<class_type>::value,
"std::vector is binding to lua-table by default.If "
"you wants register for std::vector yourself,"
"please define KAGUYA_NO_STD_VECTOR_TO_TABLE");
KAGUYA_STATIC_ASSERT(is_registerable<class_type>::value ||
!traits::is_std_map<class_type>::value,
"std::map is binding to lua-table by default.If you "
"wants register for std::map yourself,"
"please define KAGUYA_NO_STD_MAP_TO_TABLE");
// can not register push specialized class
KAGUYA_STATIC_ASSERT(is_registerable<class_type>::value,
"Can not register specialized of type conversion "
"class. e.g. std::tuple");
}
LuaTable createMatatable(lua_State *state) const {
util::ScopedSavedStack save(state);
if (!class_userdata::newmetatable<class_type>(state)) {
except::OtherError(state, typeid(class_type *).name() +
std::string(" is already registered"));
return LuaTable();
}
int metatable_index = lua_gettop(state);
Metatable::setMembers(state, metatable_index, member_map_, property_map_);
if (!traits::is_same<base_class_type, void>::value ||
!property_map_.empty()) // if base class has property and derived class
// hasnt property. need property access
// metamethod
{
if (member_map_.count("__index") == 0) {
Metatable::setPropertyIndexMetamethod(state, metatable_index);
}
if (member_map_.count("__newindex") == 0) {
Metatable::setPropertyNewIndexMetamethod(state, metatable_index);
}
} else {
if (member_map_.count("__index") == 0) {
lua_pushstring(state, "__index");
lua_pushvalue(state, metatable_index);
lua_rawset(state, metatable_index);
}
}
set_base_metatable(state, metatable_index,
types::typetag<base_class_type>());
if (lua_getmetatable(state, metatable_index)) // get base_metatable
{
lua_pushstring(state, "__call");
lua_pushcfunction(state, &Metatable::call_constructor_function);
lua_rawset(state, -3); // base_metatable["__call"] =
// Metatable::call_constructor_function
} else {
Metatable::get_call_constructor_metatable(state);
lua_setmetatable(state, metatable_index);
}
return LuaStackRef(state, metatable_index);
}
#if KAGUYA_USE_CPP11
template <typename... ArgTypes> UserdataMetatable &setConstructors() {
addOverloadedFunctions(
"new", typename ConstructorFunction<class_type, ArgTypes>::type()...);
return *this;
}
#else
#define KAGUYA_SET_CON_TYPE_DEF(N) \
typename ConstructorFunction<class_type, KAGUYA_PP_CAT(A, N)>::type()
#define KAGUYA_SET_CON_FN_DEF(N) \
template <KAGUYA_PP_TEMPLATE_DEF_REPEAT(N)> \
inline UserdataMetatable &setConstructors() { \
addOverloadedFunctions("new", \
KAGUYA_PP_REPEAT_ARG(N, KAGUYA_SET_CON_TYPE_DEF)); \
return *this; \
}
KAGUYA_PP_REPEAT_DEF(KAGUYA_FUNCTION_MAX_OVERLOADS, KAGUYA_SET_CON_FN_DEF)
#undef KAGUYA_SET_CON_FN_DEF
#undef KAGUYA_SET_CON_TYPE_DEF
#endif
/// @brief add member property with getter function.(experimental)
/// @param name function name for lua
/// @param mem bind member data
template <typename Ret>
UserdataMetatable &addProperty(const char *name, Ret class_type::*mem) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered.");
return *this;
}
property_map_[name] = AnyDataPusher(kaguya::function(mem));
return *this;
}
/// @brief add member property with getter function.(experimental)
/// @param name function name for lua
/// @param getter getter function
template <typename GetType>
UserdataMetatable &addProperty(const char *name,
GetType (class_type::*getter)() const) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered.");
return *this;
}
property_map_[name] = AnyDataPusher(kaguya::function(getter));
return *this;
}
/// @brief add member property with setter, getter functions.(experimental)
/// @param name function name for lua
/// @param getter getter function
/// @param setter setter function
template <typename GetType>
UserdataMetatable &addProperty(const char *name,
GetType (*getter)(const class_type *)) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered.");
return *this;
}
property_map_[name] = AnyDataPusher(function(getter));
return *this;
}
/// @brief add member property with setter, getter functions.(experimental)
/// @param name function name for lua
/// @param getter getter function
/// @param setter setter function
template <typename GetType>
UserdataMetatable &addProperty(const char *name,
GetType (*getter)(const class_type &)) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered.");
return *this;
}
property_map_[name] = AnyDataPusher(function(getter));
return *this;
}
/// @brief add member property with setter, getter functions.(experimental)
/// @param name function name for lua
/// @param getter getter function
/// @param setter setter function
template <typename GetType, typename SetType>
UserdataMetatable &addProperty(const char *name,
GetType (class_type::*getter)() const,
void (class_type::*setter)(SetType)) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered.");
return *this;
}
property_map_[name] = AnyDataPusher(overload(getter, setter));
return *this;
}
/// @brief add member property with external setter, getter
/// functions.(experimental)
/// @param name function name for lua
/// @param getter getter function
/// @param setter setter function
template <typename GetType, typename SetType>
UserdataMetatable &addProperty(const char *name,
GetType (*getter)(const class_type *),
void (*setter)(class_type *, SetType)) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered.");
return *this;
}
property_map_[name] = AnyDataPusher(overload(getter, setter));
return *this;
}
/// @brief add member property with external setter, getter
/// functions.(experimental)
/// @param name function name for lua
/// @param getter getter function
/// @param setter setter function
template <typename GetType, typename SetType>
UserdataMetatable &addProperty(const char *name,
GetType (*getter)(const class_type &),
void (*setter)(class_type &, SetType)) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered.");
return *this;
}
property_map_[name] = AnyDataPusher(overload(getter, setter));
return *this;
}
/// @brief add member property with getter function.(experimental)
/// @param name function name for lua
/// @param getter getter function
template <typename GetterType>
UserdataMetatable &addPropertyAny(const char *name, GetterType getter) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered.");
return *this;
}
property_map_[name] = AnyDataPusher(function(getter));
return *this;
}
/// @brief add member property with setter, getter functions.(experimental)
/// @param name function name for lua
/// @param getter getter function
/// @param setter setter function
template <typename GetterType, typename SetterType>
UserdataMetatable &addPropertyAny(const char *name, GetterType getter,
SetterType setter) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered.");
return *this;
}
property_map_[name] = AnyDataPusher(overload(getter, setter));
return *this;
}
/// @brief add non member function
/// @param name function name for lua
/// @param f function
template <typename Fun>
UserdataMetatable &addStaticFunction(const char *name, Fun f) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered.");
return *this;
}
member_map_[name] = AnyDataPusher(kaguya::function(f));
return *this;
}
#if KAGUYA_USE_CPP11
/// @brief assign overloaded from functions.
/// @param name name for lua
/// @param f functions
template <typename... Funcs>
UserdataMetatable &addOverloadedFunctions(const char *name, Funcs... f) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered.");
return *this;
}
member_map_[name] = AnyDataPusher(overload(f...));
return *this;
}
/// @brief assign data by argument value.
/// @param name name for lua
/// @param d data
template <typename Data>
UserdataMetatable &addStaticField(const char *name, Data &&d) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered.");
return *this;
}
member_map_[name] = AnyDataPusher(std::forward<Data>(d));
return *this;
}
#else
#define KAGUYA_ADD_OVERLOAD_FUNCTION_DEF(N) \
template <KAGUYA_PP_TEMPLATE_DEF_REPEAT(N)> \
inline UserdataMetatable &addOverloadedFunctions( \
const char *name, KAGUYA_PP_ARG_CR_DEF_REPEAT(N)) { \
if (has_key(name)) { \
throw KaguyaException(std::string(name) + " is already registered."); \
return *this; \
} \
member_map_[name] = \
AnyDataPusher(kaguya::overload(KAGUYA_PP_ARG_REPEAT(N))); \
return *this; \
}
KAGUYA_PP_REPEAT_DEF(KAGUYA_FUNCTION_MAX_OVERLOADS,
KAGUYA_ADD_OVERLOAD_FUNCTION_DEF)
#undef KAGUYA_ADD_OVERLOAD_FUNCTION_DEF
/// @brief assign data by argument value.
/// @param name name for lua
/// @param d data
template <typename Data>
UserdataMetatable &addStaticField(const char *name, const Data &d) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered.");
return *this;
}
member_map_[name] = AnyDataPusher(d);
return *this;
}
#endif
#if defined(_MSC_VER) && _MSC_VER <= 1800
// can not write Ret class_type::* f on MSC++2013
template <typename Fun>
UserdataMetatable &addFunction(const char *name, Fun f) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered. To "
"overload a function, use "
"addOverloadedFunctions");
return *this;
}
member_map_[name] = AnyDataPusher(kaguya::function(f));
return *this;
}
#else
/// @brief assign function
/// @param name name for lua
/// @param f pointer to member function.
template <typename Ret>
UserdataMetatable &addFunction(const char *name, Ret class_type::*f) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered. To "
"overload a function, use "
"addOverloadedFunctions");
return *this;
}
member_map_[name] = AnyDataPusher(kaguya::function(f));
return *this;
}
#endif
/// @brief assign function
/// @param name name for lua
/// @param f member function object.
UserdataMetatable &addFunction(const char *name, PolymorphicMemberInvoker f) {
if (has_key(name)) {
throw KaguyaException(std::string(name) + " is already registered. To "
"overload a function, use "
"addOverloadedFunctions");
return *this;
}
member_map_[name] = AnyDataPusher(kaguya::function(f));
return *this;
}
private:
void set_base_metatable(lua_State *, int, types::typetag<void>) const {}
template <class Base>
void set_base_metatable(lua_State *state, int metatable_index,
types::typetag<Base>) const {
class_userdata::get_metatable<Base>(state);
lua_setmetatable(state,
metatable_index); // metatable.setMetatable(newmeta);
PointerConverter &pconverter = PointerConverter::get(state);
pconverter.add_type_conversion<Base, class_type>();
}
#if KAGUYA_USE_CPP11
template <typename Base>
void metatables(lua_State *state, int metabase_array_index,
PointerConverter &pvtreg,
types::typetag<MultipleBase<Base> >) const {
class_userdata::get_metatable<Base>(state);
lua_rawseti(state, metabase_array_index,
lua_rawlen(state, metabase_array_index) + 1);
pvtreg.add_type_conversion<Base, class_type>();
}
template <typename Base, typename... Remain>
void metatables(lua_State *state, int metabase_array_index,
PointerConverter &pvtreg,
types::typetag<MultipleBase<Base, Remain...> >) const {
class_userdata::get_metatable<Base>(state);
lua_rawseti(state, metabase_array_index,
lua_rawlen(state, metabase_array_index) + 1);
pvtreg.add_type_conversion<Base, class_type>();
metatables(state, metabase_array_index, pvtreg,
types::typetag<MultipleBase<Remain...> >());
}
template <typename... Bases>
void
set_base_metatable(lua_State *state, int metatable_index,
types::typetag<MultipleBase<Bases...> > metatypes) const {
PointerConverter &pconverter = PointerConverter::get(state);
lua_createtable(state, sizeof...(Bases), 0);
int metabase_array_index = lua_gettop(state);
metatables(state, metabase_array_index, pconverter, metatypes);
Metatable::setMultipleBase(state, metatable_index, metabase_array_index);
}
#else
#define KAGUYA_GET_BASE_METATABLE(N) \
class_userdata::get_metatable<KAGUYA_PP_CAT(A, N)>(state); \
lua_rawseti(state, metabase_array_index, \
lua_rawlen(state, metabase_array_index) + 1); \
pconverter.add_type_conversion<KAGUYA_PP_CAT(A, N), class_type>();
#define KAGUYA_MULTIPLE_INHERITANCE_SETBASE_DEF(N) \
template <KAGUYA_PP_TEMPLATE_DEF_REPEAT(N)> \
void set_base_metatable( \
lua_State *state, int metatable_index, \
types::typetag<MultipleBase<KAGUYA_PP_TEMPLATE_ARG_REPEAT(N)> >) const { \
PointerConverter &pconverter = PointerConverter::get(state); \
lua_createtable(state, N, 0); \
int metabase_array_index = lua_gettop(state); \
KAGUYA_PP_REPEAT(N, KAGUYA_GET_BASE_METATABLE); \
Metatable::setMultipleBase(state, metatable_index, metabase_array_index); \
}
KAGUYA_PP_REPEAT_DEF(KAGUYA_CLASS_MAX_BASE_CLASSES,
KAGUYA_MULTIPLE_INHERITANCE_SETBASE_DEF)
#undef KAGUYA_MULTIPLE_INHERITANCE_SETBASE_DEF
#undef KAGUYA_GET_BASE_METATABLE
#endif
bool has_key(const std::string &key) {
if (member_map_.count(key) > 0) {
return true;
}
if (property_map_.count(key) > 0) {
return true;
}
std::string propkey = KAGUYA_PROPERTY_PREFIX + key;
if (member_map_.count(propkey) > 0) //_prop_keyname is reserved for property
{
return true;
}
return false;
}
Metatable::PropMapType property_map_;
Metatable::MemberMapType member_map_;
};
/// @ingroup lua_type_traits
/// @brief lua_type_traits for UserdataMetatable
template <typename T, typename Base>
struct lua_type_traits<UserdataMetatable<T, Base> > {
typedef const UserdataMetatable<T, Base> &push_type;
static int push(lua_State *l, push_type ref) {
return ref.createMatatable(l).push(l);
}
};
}