#pragma once #include "stack_push.h" #include "stack_read.h" #include #include #include namespace perlbind { namespace detail { // handles xsub call stack from perl, inherits stack::pusher to push return values class xsub_stack : public stack::pusher { public: xsub_stack() = delete; xsub_stack(PerlInterpreter* my_perl, CV* cv) : stack::pusher(my_perl) { GV* gv = CvGV(cv); m_sub_name = GvNAME(gv); m_pkg_name = HvNAME(GvSTASH(gv)); dXSARGS; this->sp = sp; this->ax = ax; this->mark = mark; this->items = items; } ~xsub_stack() { XSRETURN(m_pushed); } int size() const { return items; } std::string name() const { return std::string(pkg_name()) + "::" + sub_name(); } const char* pkg_name() const { return m_pkg_name; } const char* sub_name() const { return m_sub_name; } template void push_return(T&& value) { XSprePUSH; push(std::forward(value)); } // returns true if all perl stack arguments are compatible with expected native arg types template bool check_types(Tuple&& types) { static constexpr int count = std::tuple_size::value; if (items != count) return false; else if (count == 0) return true; using make_sequence = std::make_index_sequence; return check_stack(std::forward(types), make_sequence()); } // returns tuple of converted perl stack arguments, throws on an incompatible type template auto convert_stack(Tuple&& types) { using make_sequence = std::make_index_sequence::value>; return get_stack(std::forward(types), make_sequence()); } std::string types() { std::string args; for (int i = 0; i < items; ++i) { args += get_type_name(ST(i)); if (i < (items - 1)) args += ", "; } return args.empty() ? "void" : args; } protected: int ax = 0; int items = 0; SV** mark = nullptr; const char* m_pkg_name = nullptr; const char* m_sub_name = nullptr; std::string get_type_name(SV* item) { switch (SvTYPE(item)) { case SVt_NULL: return ""; case SVt_NV: return "double"; case SVt_PV: return "string"; case SVt_PVAV: return "array"; case SVt_PVHV: return "hash"; case SVt_IV: if (sv_isobject(item)) return std::string(sv_reftype(SvRV(item), true)) + "*"; else if (SvROK(item)) return "ref"; else return "int"; default: return sv_reftype(item, true); } } private: template bool check_index(T t, size_t index) { return stack::read_as::check(my_perl, static_cast(index), ax, items); } // return true if perl stack matches all expected argument types in tuple template bool check_stack(Tuple&& t, std::index_sequence) { // lists compatibility of each expected arg type (no short-circuit) std::initializer_list res = { check_index(std::get(std::forward(t)), I)... }; return std::all_of(res.begin(), res.end(), [](bool same) { return same; }); } template T get_stack_index(T t, size_t index) { return stack::read_as::get(my_perl, static_cast(index), ax, items); } template auto get_stack(Tuple&& t, std::index_sequence) { return Tuple{ get_stack_index(std::get(std::forward(t)), I)... }; } }; } // namespace detail } // namespace perlbind