From b8a2bc64f6fed0cc829b9908e192b3acb9bb62e5 Mon Sep 17 00:00:00 2001 From: Daniel Wallin Date: Thu, 5 Feb 2009 15:10:11 +0100 Subject: [PATCH] New recursive overload resolver. --- luabind/detail/call.hpp | 158 ++++++++++++++++++---- luabind/make_function.hpp | 61 ++++++--- luabind/tag_function.hpp | 5 +- src/function.cpp | 336 +++++++++++++++++++--------------------------- 4 files changed, 311 insertions(+), 249 deletions(-) rewrite src/function.cpp (72%) diff --git a/luabind/detail/call.hpp b/luabind/detail/call.hpp index beeab7d..4d0f133 100644 --- a/luabind/detail/call.hpp +++ b/luabind/detail/call.hpp @@ -26,33 +26,75 @@ namespace luabind { namespace detail { +struct invoke_context; + +struct LUABIND_API function_object +{ + function_object(lua_CFunction entry) + : entry(entry) + , next(0) + {} + + virtual ~function_object() + {} + + virtual int call( + lua_State* L, invoke_context& ctx) const = 0; + virtual void format_signature(lua_State* L, char const* function) const = 0; + + lua_CFunction entry; + std::string name; + function_object* next; +}; + +struct invoke_context +{ + invoke_context() + : best_score(std::numeric_limits::max()) + , candidate_index(0) + {} + + operator bool() const + { + return candidate_index == 1; + } + + void format_error(lua_State* L, function_object const* overloads) const; + + int best_score; + function_object const* candidates[10]; + int candidate_index; +}; + template int invoke0( - lua_State* L, F const& f, Signature - , Policies const& policies, IsVoid, mpl::true_) + lua_State* L, function_object const& self, invoke_context& ctx + , F const& f, Signature, Policies const& policies, IsVoid, mpl::true_) { return invoke_member( - L, f, Signature(), policies + L, self, ctx, f, Signature(), policies , mpl::long_::value - 1>(), IsVoid() ); } template int invoke0( - lua_State* L, F const& f, Signature - , Policies const& policies, IsVoid, mpl::false_) + lua_State* L, function_object const& self, invoke_context& ctx, + F const& f, Signature, Policies const& policies, IsVoid, mpl::false_) { return invoke_normal( - L, f, Signature(), policies + L, self, ctx, f, Signature(), policies , mpl::long_::value - 1>(), IsVoid() ); } template -int invoke(lua_State* L, F const& f, Signature, Policies const& policies) +int invoke( + lua_State* L, function_object const& self, invoke_context& ctx + , F const& f, Signature, Policies const& policies) { return invoke0( - L, f, Signature(), policies + L, self, ctx, f, Signature(), policies , boost::is_void::type>() , boost::is_member_function_pointer() ); @@ -75,6 +117,20 @@ int maybe_yield(lua_State* L, int results, Policies*) L, results, mpl::bool_::value>()); } +inline int sum_scores(int const* first, int const* last) +{ + int result = 0; + + for (; first != last; ++first) + { + if (*first < 0) + return *first; + result += *first; + } + + return result; +} + # define LUABIND_INVOKE_NEXT_ITER(n) \ typename mpl::next< \ BOOST_PP_IF( \ @@ -99,6 +155,12 @@ int maybe_yield(lua_State* L, int results, Policies*) BOOST_PP_CAT(p,n), BOOST_PP_CAT(a,n), lua_to_cpp>::type BOOST_PP_CAT(c,n); \ int const BOOST_PP_CAT(index,n) = LUABIND_INVOKE_NEXT_INDEX(n); +# define LUABIND_INVOKE_COMPUTE_ARITY(n) + (BOOST_PP_CAT(p,n)::has_arg ? 1 : 0) + +# define LUABIND_INVOKE_COMPUTE_SCORE(n) \ + , BOOST_PP_CAT(c,n).match( \ + L, LUABIND_DECORATE_TYPE(BOOST_PP_CAT(a,n)), BOOST_PP_CAT(index,n)) + # define LUABIND_INVOKE_ARG(z, n, base) \ BOOST_PP_CAT(c,base(n)).apply( \ L, LUABIND_DECORATE_TYPE(BOOST_PP_CAT(a,base(n))), BOOST_PP_CAT(index,base(n))) @@ -147,7 +209,8 @@ invoke_member invoke_normal # endif ( - lua_State* L, F const& f, Signature, Policies const&, mpl::long_ + lua_State* L, function_object const& self, invoke_context& ctx + , F const& f, Signature, Policies const&, mpl::long_ # ifdef LUABIND_INVOKE_VOID , mpl::true_ # else @@ -169,27 +232,71 @@ invoke_normal # include BOOST_PP_LOCAL_ITERATE() # endif + int const arity = 0 +# if N > 0 +# define BOOST_PP_LOCAL_MACRO(n) LUABIND_INVOKE_COMPUTE_ARITY(n) +# define BOOST_PP_LOCAL_LIMITS (0,N-1) +# include BOOST_PP_LOCAL_ITERATE() +# endif + ; + int const arguments = lua_gettop(L); + int score = -1; + + if (arity == arguments) + { + int const scores[] = { + 0 +# if N > 0 +# define BOOST_PP_LOCAL_MACRO(n) LUABIND_INVOKE_COMPUTE_SCORE(n) +# define BOOST_PP_LOCAL_LIMITS (0,N-1) +# include BOOST_PP_LOCAL_ITERATE() +# endif + }; + + score = sum_scores(scores + 1, scores + 1 + N); + } + + if (score >= 0 && score < ctx.best_score) + { + ctx.best_score = score; + ctx.candidates[0] = &self; + ctx.candidate_index = 1; + } + else if (score == ctx.best_score) + { + ctx.candidates[ctx.candidate_index++] = &self; + } + + int results = 0; + + if (self.next) + { + results = self.next->call(L, ctx); + } + + if (score == ctx.best_score && ctx.candidate_index == 1) + { # ifndef LUABIND_INVOKE_VOID - result_converter.apply( - L, + result_converter.apply( + L, # endif # ifdef LUABIND_INVOKE_MEMBER - (c0.apply(L, LUABIND_DECORATE_TYPE(a0), index0).*f)( - BOOST_PP_ENUM(BOOST_PP_DEC(N), LUABIND_INVOKE_ARG, BOOST_PP_INC) - ) + (c0.apply(L, LUABIND_DECORATE_TYPE(a0), index0).*f)( + BOOST_PP_ENUM(BOOST_PP_DEC(N), LUABIND_INVOKE_ARG, BOOST_PP_INC) + ) # else # define LUABIND_INVOKE_IDENTITY(x) x - f( - BOOST_PP_ENUM(N, LUABIND_INVOKE_ARG, LUABIND_INVOKE_IDENTITY) - ) + f( + BOOST_PP_ENUM(N, LUABIND_INVOKE_ARG, LUABIND_INVOKE_IDENTITY) + ) # undef LUABIND_INVOKE_IDENTITY # endif # ifndef LUABIND_INVOKE_VOID - ) + ) # endif - ; + ; # if N > 0 # define BOOST_PP_LOCAL_MACRO(n) LUABIND_INVOKE_CONVERTER_POSTCALL(n) @@ -197,15 +304,16 @@ invoke_normal # include BOOST_PP_LOCAL_ITERATE() # endif - int const results = lua_gettop(L) - arguments; + results = maybe_yield(L, lua_gettop(L) - arguments, (Policies*)0); - int const indices[] = { - arguments + results BOOST_PP_ENUM_TRAILING_PARAMS(N, index) - }; + int const indices[] = { + arguments + results BOOST_PP_ENUM_TRAILING_PARAMS(N, index) + }; - policy_list_postcall::apply(L, indices); + policy_list_postcall::apply(L, indices); + } - return maybe_yield(L, results, (Policies*)0); + return results; } # undef N diff --git a/luabind/make_function.hpp b/luabind/make_function.hpp index d43c5b4..f7dbd44 100644 --- a/luabind/make_function.hpp +++ b/luabind/make_function.hpp @@ -17,37 +17,54 @@ namespace luabind { namespace detail { - struct function_base - { - virtual ~function_base() - {} - - virtual int call(lua_State*) const = 0; - virtual int compute_score(lua_State*) const = 0; - virtual void format_signature(lua_State*, char const*) const = 0; - }; + LUABIND_API void handle_exception_aux(lua_State* L); template - struct function_impl : function_base + struct function_object_impl : function_object { - function_impl(F f, Policies policies) - : f(f) + function_object_impl(F f, Policies const& policies) + : function_object(&entry_point) + , f(f) , policies(policies) {} - int call(lua_State* L) const + int call(lua_State* L, invoke_context& ctx) const { - return invoke(L, f, Signature(), policies); + return invoke(L, *this, ctx, f, Signature(), policies); } - int compute_score(lua_State* L) const + void format_signature(lua_State* L, char const* function) const { - return detail::compute_score(L, Signature(), policies); + detail::format_signature(L, function, Signature()); } - void format_signature(lua_State* L, char const* function) const + static int entry_point(lua_State* L) { - detail::format_signature(L, function, Signature()); + function_object_impl const* impl = + *(function_object_impl const**)lua_touserdata(L, lua_upvalueindex(1)); + + invoke_context ctx; + + int results; + + try + { + results = invoke( + L, *impl, ctx, impl->f, Signature(), impl->policies); + } + catch (...) + { + handle_exception_aux(L); + lua_error(L); + } + + if (!ctx) + { + ctx.format_error(L, impl); + lua_error(L); + } + + return results; } F f; @@ -55,7 +72,8 @@ namespace detail }; LUABIND_API object make_function_aux( - lua_State*, int, function_base* + lua_State*, int + , function_object* impl ); LUABIND_API void add_overload(object const&, char const*, object const&); @@ -68,8 +86,9 @@ object make_function(lua_State* L, F f, Signature, Policies) return detail::make_function_aux( L , detail::compute_arity(Signature(), Policies()) - , new detail::function_impl( - f, Policies()) + , new detail::function_object_impl( + f, Policies() + ) ); } diff --git a/luabind/tag_function.hpp b/luabind/tag_function.hpp index 4788d42..51fe97b 100644 --- a/luabind/tag_function.hpp +++ b/luabind/tag_function.hpp @@ -40,10 +40,11 @@ namespace detail template int invoke( - lua_State* L, tagged_function const& tagged + lua_State* L, function_object const& self, invoke_context& ctx + , tagged_function const& tagged , Signature, Policies const& policies) { - return invoke(L, tagged.f, Signature(), policies); + return invoke(L, self, ctx, tagged.f, Signature(), policies); } template diff --git a/src/function.cpp b/src/function.cpp dissimilarity index 72% index 3666217..8608a4b 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -1,201 +1,135 @@ -// Copyright Daniel Wallin 2008. Use, modification and distribution is -// subject to 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) - -#define LUABIND_BUILDING - -#include -#include - -#include - -namespace luabind { namespace detail { - -namespace -{ - - struct function - { - function( - int arity, function_base* impl - ) - : m_arity(arity) - , m_impl(impl) - , m_next(0) - {} - - int call(lua_State*) const; - - std::string m_name; - int m_arity; - std::auto_ptr m_impl; - function const* m_next; - object m_keep_alive; - }; - - int const max_candidates = 10; - - int function::call(lua_State* L) const - { - function const* candidates[max_candidates]; - int candidate_idx = 0; - - function const* best = 0; - int best_score = std::numeric_limits::max(); - - int args = lua_gettop(L); - - char const* function_name = m_name.empty() ? "" : m_name.c_str(); - - for (function const* f = this; f != 0; f = f->m_next) - { - if (args != f->m_arity) - continue; - - int score = f->m_impl->compute_score(L); - - if (score < 0) - continue; - - if (score < best_score) - { - best = f; - best_score = score; - candidate_idx = 0; - } - else if (score == best_score) - { - best = 0; - } - - assert(candidate_idx < max_candidates); - candidates[candidate_idx++] = f; - } - - if (!best) - { - if (best_score != std::numeric_limits::max()) - { - // Ambiguous - lua_pushstring(L, "Ambiguous, candidates:\n"); - for (int i = 0; i < candidate_idx; ++i) - { - if (i != 0) - lua_pushstring(L, "\n"); - candidates[i]->m_impl->format_signature(L, function_name); - } - lua_concat(L, candidate_idx * 2); - } - else - { - // No overload - lua_pushstring(L, "No matching overload found, candidates:\n"); - int count = 0; - for (function const* f = this; f != 0; f = f->m_next) - { - if (count != 0) - lua_pushstring(L, "\n"); - f->m_impl->format_signature(L, function_name); - ++count; - } - lua_concat(L, count * 2); - } - - return -2; - } - - return best->m_impl->call(L); - } - - int function_dispatcher(lua_State* L) - { - function const* f = - (function const*)lua_touserdata(L, lua_upvalueindex(1)); - - int results = -2; - { - boost::optional result = handle_exceptions( - L, boost::bind(&function::call, f, L)); - - if (result) - results = *result; - } - - if (results == -2) - lua_error(L); - - return results; - } - - int function_destroy(lua_State* L) - { - function* fn = (function*)lua_touserdata(L, 1); - fn->~function(); - return 0; - } - - void push_function_metatable(lua_State* L) - { - lua_pushstring(L, "luabind.function"); - lua_rawget(L, LUA_REGISTRYINDEX); - - if (lua_istable(L, -1)) - return; - - lua_pop(L, 1); - - lua_newtable(L); - - lua_pushstring(L, "__gc"); - lua_pushcclosure(L, &function_destroy, 0); - lua_rawset(L, -3); - - lua_pushstring(L, "luabind.function"); - lua_pushvalue(L, -2); - lua_rawset(L, LUA_REGISTRYINDEX); - } - -} // namespace unnamed - -LUABIND_API bool is_luabind_function(lua_State* L, int index) -{ - return lua_tocfunction(L, index) == &function_dispatcher; -} - -LUABIND_API void add_overload( - object const& context, char const* name, object const& fn) -{ - function* f = touserdata(getupvalue(fn, 1)); - f->m_name = name; - - if (object overloads = context[name]) - { - if (tocfunction(overloads) == &function_dispatcher - && tocfunction(fn) == &function_dispatcher) - { - f->m_next = touserdata(getupvalue(overloads, 1)); - f->m_keep_alive = overloads; - f->m_name = name; - } - } - - context[name] = fn; -} - -LUABIND_API object make_function_aux( - lua_State* L, int arity, function_base* impl -) -{ - void* storage = lua_newuserdata(L, sizeof(function)); - push_function_metatable(L); - new (storage) function(arity, impl); - lua_setmetatable(L, -2); - - lua_pushcclosure(L, &function_dispatcher, 1); - stack_pop pop(L, 1); - - return object(from_stack(L, -1)); -} - -}} // namespace luabind::detail - +// Copyright Daniel Wallin 2008. Use, modification and distribution is +// subject to 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) + +#define LUABIND_BUILDING + +#include + +namespace luabind { namespace detail { + +namespace +{ + + int function_destroy(lua_State* L) + { + function_object* fn = *(function_object**)lua_touserdata(L, 1); + delete fn; + return 0; + } + + void push_function_metatable(lua_State* L) + { + lua_pushstring(L, "luabind.function"); + lua_rawget(L, LUA_REGISTRYINDEX); + + if (lua_istable(L, -1)) + return; + + lua_pop(L, 1); + + lua_newtable(L); + + lua_pushstring(L, "__gc"); + lua_pushcclosure(L, &function_destroy, 0); + lua_rawset(L, -3); + + lua_pushstring(L, "luabind.function"); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); + } + + // A pointer to this is used as a tag value to identify functions exported + // by luabind. + int function_tag = 0; + +} // namespace unnamed + +LUABIND_API bool is_luabind_function(lua_State* L, int index) +{ + if (!lua_getupvalue(L, index, 2)) + return false; + bool result = lua_touserdata(L, -1) == &function_tag; + lua_pop(L, 1); + return result; +} + +namespace +{ + + inline bool is_luabind_function(object const& obj) + { + obj.push(obj.interpreter()); + bool result = detail::is_luabind_function(obj.interpreter(), -1); + lua_pop(obj.interpreter(), 1); + return result; + } + +} // namespace unnamed + +LUABIND_API void add_overload( + object const& context, char const* name, object const& fn) +{ + function_object* f = *touserdata(getupvalue(fn, 1)); + f->name = name; + + if (object overloads = context[name]) + { + if (is_luabind_function(overloads) && is_luabind_function(fn)) + { + f->next = *touserdata(getupvalue(overloads, 1)); + } + } + + context[name] = fn; +} + +LUABIND_API object make_function_aux(lua_State* L, int arity, function_object* impl) +{ + void* storage = lua_newuserdata(L, sizeof(function_object*)); + push_function_metatable(L); + *(function_object**)storage = impl; + lua_setmetatable(L, -2); + + lua_pushlightuserdata(L, &function_tag); + lua_pushcclosure(L, impl->entry, 2); + stack_pop pop(L, 1); + + return object(from_stack(L, -1)); +} + +void invoke_context::format_error( + lua_State* L, function_object const* overloads) const +{ + char const* function_name = + overloads->name.empty() ? "" : overloads->name.c_str(); + + if (candidate_index == 0) + { + lua_pushstring(L, "No matching overload found, candidates:\n"); + int count = 0; + for (function_object const* f = overloads; f != 0; f = f->next) + { + if (count != 0) + lua_pushstring(L, "\n"); + f->format_signature(L, function_name); + ++count; + } + lua_concat(L, count * 2); + } + else + { + // Ambiguous + lua_pushstring(L, "Ambiguous, candidates:\n"); + for (int i = 0; i < candidate_index; ++i) + { + if (i != 0) + lua_pushstring(L, "\n"); + candidates[i]->format_signature(L, function_name); + } + lua_concat(L, candidate_index * 2); + } +} + +}} // namespace luabind::detail + -- 2.11.4.GIT