/* * Copyright (c) 2010-2012 OTClient * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef LUABINDER_H #define LUABINDER_H // this file is and must be included only from luainterface.h #include "luainterface.h" #include "luaexception.h" /// This namespace contains some dirty metaprogamming that uses a lot of C++0x features /// The purpose here is to create templates that can bind any function from C++ /// and expose in lua environment. This is done combining variadic templates, /// lambdas, tuples and some type traits features from the new C++0x standard to create /// templates that can detect functions's arguments and then generate lambdas. These lambdas /// pops arguments from lua stack, call the bound C++ function and then /// pushes the result to lua. namespace luabinder { /// Removes const references, transforming 'const T&' into 'T' template struct remove_const_ref { typedef typename std::remove_const::type>::type type; }; /// Pack arguments from lua stack into a tuple recursively template struct pack_values_into_tuple { template static void call(Tuple& tuple, LuaInterface* lua) { typedef typename std::tuple_element::type ValueType; std::get(tuple) = lua->polymorphicPop(); pack_values_into_tuple::call(tuple, lua); } }; template<> struct pack_values_into_tuple<0> { template static void call(Tuple& tuple, LuaInterface* lua) { } }; /// C++ function caller that can push results to lua template typename std::enable_if::value, int>::type call_fun_and_push_result(const F& f, LuaInterface* lua, const Args&... args) { Ret ret = f(args...); lua->polymorphicPush(ret); return 1; } /// C++ void function caller template typename std::enable_if::value, int>::type call_fun_and_push_result(const F& f, LuaInterface* lua, const Args&... args) { f(args...); return 0; } /// Expand arguments from tuple for later calling the C++ function template struct expand_fun_arguments { template static int call(const Tuple& tuple, const F& f, LuaInterface* lua, const Args&... args) { return expand_fun_arguments::call(tuple, f, lua, std::get(tuple), args...); } }; template struct expand_fun_arguments<0,Ret> { template static int call(const Tuple& tuple, const F& f, LuaInterface* lua, const Args&... args) { return call_fun_and_push_result(f, lua, args...); } }; /// Bind different types of functions generating a lambda template LuaCppFunction bind_fun_specializer(const F& f) { enum { N = std::tuple_size::value }; return [=](LuaInterface* lua) -> int { while(lua->stackSize() != N) { if(lua->stackSize() < N) g_lua.pushNil(); else g_lua.pop(); } Tuple tuple; pack_values_into_tuple::call(tuple, lua); return expand_fun_arguments::call(tuple, f, lua); }; } /// Bind a customized function inline LuaCppFunction bind_fun(const std::function& f) { return f; } /// Bind a std::function template LuaCppFunction bind_fun(const std::function& f) { typedef typename std::tuple::type...> Tuple; return bind_fun_specializer::type, decltype(f), Tuple>(f); } /// Specialization for lambdas template struct bind_lambda_fun; template struct bind_lambda_fun { static LuaCppFunction call(const Lambda& f) { typedef typename std::tuple::type...> Tuple; return bind_fun_specializer::type, decltype(f), Tuple>(f); } }; template typename std::enable_if::value, LuaCppFunction>::type bind_fun(const Lambda& f) { typedef decltype(&Lambda::operator()) F; return bind_lambda_fun::call(f); } /// Convert to C++ functions pointers to std::function then bind template LuaCppFunction bind_fun(Ret (*f)(Args...)) { return bind_fun(std::function(f)); } /// Create member function lambdas template std::function&, const Args&...)> make_mem_func(Ret (C::* f)(Args...)) { auto mf = std::mem_fn(f); return [=](const std::shared_ptr& obj, const Args&... args) mutable -> Ret { if(!obj) throw LuaException("failed to call a member function because the passed object is nil"); return mf(obj.get(), args...); }; } template std::function&, const Args&...)> make_mem_func(void (C::* f)(Args...)) { auto mf = std::mem_fn(f); return [=](const std::shared_ptr& obj, const Args&... args) mutable -> void { if(!obj) throw LuaException("failed to call a member function because the passed object is nil"); mf(obj.get(), args...); }; } /// Create member function lambdas for singleton classes template std::function make_mem_func_singleton(Ret (C::* f)(Args...), C* instance) { auto mf = std::mem_fn(f); return [=](Args... args) mutable -> Ret { return mf(instance, args...); }; } template std::function make_mem_func_singleton(void (C::* f)(Args...), C* instance) { auto mf = std::mem_fn(f); return [=](Args... args) mutable -> void { mf(instance, args...); }; } /// Bind member functions template LuaCppFunction bind_mem_fun(Ret (FC::* f)(Args...)) { typedef typename std::tuple, typename remove_const_ref::type...> Tuple; auto lambda = make_mem_func(f); return bind_fun_specializer::type, decltype(lambda), Tuple>(lambda); } /// Bind singleton member functions template LuaCppFunction bind_singleton_mem_fun(Ret (FC::*f)(Args...), C *instance) { typedef typename std::tuple::type...> Tuple; assert(instance); auto lambda = make_mem_func_singleton(f, static_cast(instance)); return bind_fun_specializer::type, decltype(lambda), Tuple>(lambda); } /// Bind customized member functions template LuaCppFunction bind_mem_fun(int (C::*f)(LuaInterface*)) { auto mf = std::mem_fn(f); return [=](LuaInterface* lua) -> int { auto obj = lua->castValue>(1); lua->remove(1); return mf(obj, lua); }; } } #endif