From 23473852bfd8c23198e107113113cc2b7af5949c Mon Sep 17 00:00:00 2001 From: Aki Tuomi Date: Fri, 7 Jul 2017 18:36:26 +0300 Subject: [PATCH] ext/luawrapper: Add toString and eq to LuaContext https://github.com/ahupowerdns/luawrapper/pull/35 --- ext/luawrapper/include/LuaContext.hpp | 110 ++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/ext/luawrapper/include/LuaContext.hpp b/ext/luawrapper/include/LuaContext.hpp index d0ffe6f6b..abe432523 100644 --- a/ext/luawrapper/include/LuaContext.hpp +++ b/ext/luawrapper/include/LuaContext.hpp @@ -47,6 +47,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include #include @@ -64,6 +65,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # define ATTR_UNUSED #endif +#define LUACONTEXT_GLOBAL_EQ "e5ddced079fc405aa4937b386ca387d2" +#define EQ_FUNCTION_NAME "__eq" +#define TOSTRING_FUNCTION_NAME "__tostring" + /** * Defines a Lua context * A Lua context is used to interpret Lua code. Since everything in Lua is a variable (including functions), @@ -100,8 +105,32 @@ public: // opening default library if required to do so if (openDefaultLibs) luaL_openlibs(mState); + + writeGlobalEq(); } + void writeGlobalEq() { + const auto eqFunction = [](lua_State* lua) -> int { + try { + lua_pushstring(lua, "__eq"); + lua_gettable(lua, -2); + /* if not found, return false */ + if (lua_isnil(lua, -1)) { + lua_pop(lua, -2); + lua_pushboolean(lua, false); + return 1; + } + lua_insert(lua, lua_gettop(lua)-2); + return callRaw(lua, PushedObject{lua, 3}, 1).release(); + } catch(...) { + Pusher::push(lua, std::current_exception()).release(); + luaError(lua); + } + }; + lua_pushcfunction(mState, eqFunction); + lua_setglobal(mState, LUACONTEXT_GLOBAL_EQ); + }; + /** * Move constructor */ @@ -394,6 +423,56 @@ public: registerFunctionImpl(functionName, std::move(fn), tag{}, tag{}); } + /** + * Wrappers for registering "__eq" function in case we want to change this to something else some day + */ + + template + auto registerEqFunction(TPointerToMemberFunction pointer) + -> typename std::enable_if::value>::type + { + registerFunctionImpl(EQ_FUNCTION_NAME, std::mem_fn(pointer), tag{}); + } + + template + void registerEqFunction(TType fn) + { + static_assert(std::is_member_function_pointer::value, "registerFunction must take a member function pointer type as template parameter"); + registerFunctionImpl(EQ_FUNCTION_NAME, std::move(fn), tag{}); + } + + template + void registerEqFunction(TType fn) + { + static_assert(std::is_function::value, "registerFunction must take a function type as template parameter"); + registerFunctionImpl(EQ_FUNCTION_NAME, std::move(fn), tag{}, tag{}); + } + + /** + * Wrappers for registering "__tostring" function in case we want to change this to something else some day + */ + + template + auto registerToStringFunction(TPointerToMemberFunction pointer) + -> typename std::enable_if::value>::type + { + registerFunctionImpl(TOSTRING_FUNCTION_NAME, std::mem_fn(pointer), tag{}); + } + + template + void registerToStringFunction(TType fn) + { + static_assert(std::is_member_function_pointer::value, "registerFunction must take a member function pointer type as template parameter"); + registerFunctionImpl(TOSTRING_FUNCTION_NAME, std::move(fn), tag{}); + } + + template + void registerToStringFunction(TType fn) + { + static_assert(std::is_function::value, "registerFunction must take a function type as template parameter"); + registerFunctionImpl(TOSTRING_FUNCTION_NAME, std::move(fn), tag{}, tag{}); + } + /** * Inverse operation of registerFunction * @tparam TType Type whose function belongs to @@ -1496,6 +1575,28 @@ private: } }; + const auto toStringFunction = [](lua_State* lua) -> int { + try { + assert(lua_gettop(lua) == 1); + assert(lua_isuserdata(lua, 1)); + lua_pushstring(lua, "__tostring"); + lua_gettable(lua, 1); + if (lua_isnil(lua, -1)) + { + const void *ptr = lua_topointer(lua, -2); + lua_pop(lua, 1); + lua_pushstring(lua, (boost::format("userdata 0x%08x") % reinterpret_cast(ptr)).str().c_str()); + return 1; + } + lua_pushvalue(lua, 1); + return callRaw(lua, PushedObject{lua, 2}, 1).release(); + } catch (...) { + Pusher::push(lua, std::current_exception()).release(); + luaError(lua); + } + }; + + // writing structure for this type into the registry checkTypeRegistration(state, &typeid(TType)); @@ -1535,6 +1636,15 @@ private: lua_pushcfunction(state, newIndexFunction); lua_settable(state, -3); + lua_pushstring(state, "__tostring"); + lua_pushcfunction(state, toStringFunction); + lua_settable(state, -3); + + lua_pushstring(state, "__eq"); + lua_getglobal(state, LUACONTEXT_GLOBAL_EQ); + lua_settable(state, -3); + + // at this point, the stack contains the object at offset -2 and the metatable at offset -1 // lua_setmetatable will bind the two together and pop the metatable // our custom type remains on the stack (and that's what we want since this is a push function) -- 2.40.0