#pragma once namespace perlbind { namespace detail { // traits for function and class method exports template struct base_traits { using return_t = Ret; using sig_t = util::type_name; using stack_tuple = std::conditional_t::value, std::tuple, std::tuple>; static constexpr int arity = sizeof...(Args); static constexpr int stack_arity = sizeof...(Args) + (std::is_void::value ? 0 : 1); static constexpr int vararg_count = count_of::value + count_of::value; static constexpr bool is_vararg = vararg_count > 0; static constexpr bool is_vararg_last = is_last::value || is_last::value; static_assert(!is_vararg || (vararg_count == 1 && is_vararg_last), "A function may only accept a single array or hash and it must be " "be the last parameter. Prefer using reference parameters instead."); }; template ::value> struct function_traits : public function_traits {}; template struct function_traits : base_traits { using type = Ret(*)(Args...); }; template struct function_traits : base_traits { using type = Ret(Class::*)(Args...); }; template struct function_traits : base_traits { using type = Ret(Class::*)(Args...) const; }; template struct function_traits : base_traits { using type = Ret(*)(Args...); }; // represents a bound native function struct function_base { virtual ~function_base() = default; virtual std::string get_signature() const = 0; virtual bool is_compatible(xsub_stack&) const = 0; virtual void call(xsub_stack&) const = 0; static const MGVTBL mgvtbl; }; template struct function : public function_base, function_traits { using target_t = typename function::type; using return_t = typename function::return_t; function() = delete; function(PerlInterpreter* interp, T func) : my_perl(interp), m_func(func) {} std::string get_signature() const override { return util::type_name::str(); }; bool is_compatible(xsub_stack& stack) const override { return function::is_vararg || stack.check_types(typename function::stack_tuple{}); } void call(xsub_stack& stack) const override { if (!function::is_vararg && stack.size() != function::stack_arity) { using sig = typename function::sig_t; int count = std::is_member_function_pointer::value ? stack.size() - 1 : stack.size(); SV* err = newSVpvf("'%s(%s)' called with %d argument(s), expected %d\n argument(s): (%s)\n", stack.name().c_str(), sig::str().c_str(), count, function::arity, stack.types().c_str()); err = sv_2mortal(err); throw std::runtime_error(SvPV_nolen(err)); } call_impl(stack, std::is_void()); } private: void call_impl(xsub_stack& stack, std::false_type) const { return_t result = apply(m_func, stack.convert_stack(typename function::stack_tuple{})); stack.push_return(std::move(result)); } void call_impl(xsub_stack& stack, std::true_type) const { apply(m_func, stack.convert_stack(typename function::stack_tuple{})); } // c++14 call function template with tuple arg unpacking (c++17 can use std::apply()) template auto call_func(F func, Tuple&& t, std::index_sequence) const { return func(std::get(std::forward(t))...); } template auto call_member(F method, Tuple&& t, std::index_sequence) const { return (std::get<0>(t)->*method)(std::get(std::forward(t))...); } template ::value, bool> = true> auto apply(F func, Tuple&& t) const { using make_sequence = std::make_index_sequence::value>; return call_func(func, std::forward(t), make_sequence{}); } template ::value, bool> = true> auto apply(F func, Tuple&& t) const { using make_sequence = std::make_index_sequence::value - 1>; return call_member(func, std::forward(t), make_sequence{}); } PerlInterpreter* my_perl = nullptr; T m_func; }; } // namespace detail } // namespace perlbind