#pragma once
#include <xs/Array.h>
#include <xs/Simple.h>
#include <initializer_list>
namespace xs {
using xs::my_perl;
struct Sub : Sv {
enum class Want { Void = G_VOID, Scalar = G_SCALAR, Array = G_ARRAY };
static Sub create (panda::string_view code); // create sub by evaling perl code
static Sub create (XSUBADDR_t); // create anon XSub
static Sub noinc (SV* val) { return Sub(val, NONE); }
static Sub noinc (CV* val) { return Sub(val, NONE); }
static Sub clone_anon_xsub (const Sub&);
Sub (std::nullptr_t = nullptr) {}
Sub (SV* sv, bool policy = INCREMENT) : Sv(sv, policy) { _validate(); }
Sub (CV* sv, bool policy = INCREMENT) : Sv(sv, policy) {}
explicit
Sub (panda::string_view subname, I32 flags = 0) {
*this = get_cvn_flags(subname.data(), subname.length(), flags);
}
Sub (const Sub& oth) : Sv(oth) {}
Sub (Sub&& oth) : Sv(std::move(oth)) {}
Sub (const Sv& oth) : Sv(oth) { _validate(); }
Sub (Sv&& oth) : Sv(std::move(oth)) { _validate(); }
Sub (const Simple&) = delete;
Sub (const Array&) = delete;
Sub (const Hash&) = delete;
Sub (const Glob&) = delete;
Sub (const Io&) = delete;
Sub& operator= (SV* val) { Sv::operator=(val); _validate(); return *this; }
Sub& operator= (CV* val) { Sv::operator=(val); return *this; }
Sub& operator= (const Sub& oth) { Sv::operator=(oth); return *this; }
Sub& operator= (Sub&& oth) { Sv::operator=(std::move(oth)); return *this; }
Sub& operator= (const Sv& oth) { return operator=(oth.get()); }
Sub& operator= (Sv&& oth) { Sv::operator=(std::move(oth)); _validate(); return *this; }
Sub& operator= (const Simple&) = delete;
Sub& operator= (const Array&) = delete;
Sub& operator= (const Hash&) = delete;
Sub& operator= (const Glob&) = delete;
Sub& operator= (const Io&) = delete;
void set (SV* val) { Sv::operator=(val); }
operator AV* () const = delete;
operator HV* () const = delete;
operator CV* () const { return (CV*)sv; }
operator GV* () const = delete;
operator IO* () const = delete;
CV* operator-> () const { return (CV*)sv; }
template <typename T = SV> panda::enable_if_one_of_t<T,SV,CV>* get () const { return (T*)sv; }
Stash stash () const;
Glob glob () const;
panda::string_view name () const {
GV* gv = CvGV((CV*)sv);
return panda::string_view(GvNAME(gv), GvNAMELEN(gv));
}
bool named () const { return CvNAMED((CV*)sv); }
Sub SUPER () const {
GV* mygv = CvGV((CV*)sv);
GV* supergv = gv_fetchmeth_pvn(GvSTASH(mygv), GvNAME(mygv), GvNAMELEN(mygv), 0, GV_SUPER);
return Sub(supergv ? GvCV(supergv) : nullptr);
}
Sub SUPER_strict () const {
Sub ret = SUPER();
if (!ret) _throw_super();
return ret;
}
static Want want () { return (Want)GIMME_V; }
static int want_count ();
private:
struct CallArgs {
SV* self;
SV*const* list;
const Scalar* scalars;
size_t items;
};
template <class Enable, class...Ctx> struct CallContext;
public:
template <class...Ctx> using call_t = decltype(CallContext<void, Ctx...>::call((CV*)nullptr, CallArgs()));
template <class...Ctx, class...Args>
call_t<Ctx...> call (Args&&...va) const {
auto args = _get_args(va...);
return CallContext<void, Ctx...>::call((CV*)sv, args);
}
template <class...Args>
Scalar operator() (Args&&...args) const { return call<Scalar>(std::forward<Args>(args)...); }
private:
void _validate () {
if (!sv) return;
if (SvTYPE(sv) == SVt_PVCV) return;
if (SvROK(sv)) { // reference to code?
SV* val = SvRV(sv);
if (SvTYPE(val) == SVt_PVCV) {
Sv::operator=(val);
return;
}
}
if (is_undef()) return reset();
reset();
throw std::invalid_argument("SV is not a Sub or Sub reference");
}
void _throw_super () const;
template <class...Args>
struct VCallArgs : CallArgs {
SV* list[sizeof...(Args)];
VCallArgs (Args&&...args) : CallArgs{nullptr, list, nullptr, sizeof...(Args)}, list{std::forward<Args>(args)...} {
for (auto sv : list)
if (sv && SvTYPE(sv) > SVt_PVMG && SvTYPE(sv) != SVt_PVGV)
throw std::invalid_argument("one of arguments for sub.call() is not a scalar value");
}
};
template <class...T> struct type_pack {};
static CallArgs _get_args (SV*const* args = nullptr, size_t items = 0) { return {nullptr, args, nullptr, items}; }
static CallArgs _get_args (SV* arg0, SV*const* args, size_t items) { return { arg0, args, nullptr, items}; }
static CallArgs _get_args (const Scalar* args, size_t items) { return {nullptr, nullptr, args, items}; }
static CallArgs _get_args (SV* arg0, const Scalar* args, size_t items) { return { arg0, nullptr, args, items}; }
static CallArgs _get_args (const std::initializer_list<Scalar>& l) { return {nullptr, nullptr, l.begin(), l.size()}; }
static CallArgs _get_args (SV* arg0, const std::initializer_list<Scalar>& l) { return { arg0, nullptr, l.begin(), l.size()}; }
template <class...Args, typename = type_pack<decltype(Scalar(std::declval<Args>()))...>>
static VCallArgs<Args...> _get_args (Args&&...args) { return {std::forward<Args>(args)...}; }
static size_t _call (CV*, I32 flags, const CallArgs&, SV** ret, size_t maxret, AV** avr);
};
template <class...Types>
struct Sub::CallContext<void, std::tuple<Types...>> {
using type = std::tuple<Types...>;
static constexpr size_t N = sizeof...(Types);
static type call (CV* cv, const CallArgs& args) {
SV* ret[N] = {nullptr};
_call(cv, G_ARRAY, args, ret, N, nullptr);
return _make_tuple<type>(ret, std::make_index_sequence<N>());
}
template <class T, size_t...Inds>
static T _make_tuple (SV** svs, std::integer_sequence<size_t, Inds...>) {
return T(typename std::tuple_element<Inds, T>::type(svs[Inds], Sv::NONE)...);
}
};
template <class Enable, class...Ret>
struct Sub::CallContext : Sub::CallContext<void, std::tuple<Ret...>> {};
template <class T>
struct Sub::CallContext<enable_if_sv_t<T,void>, T> {
static T call (CV* cv, const CallArgs& args) {
SV* ret = NULL;
_call(cv, G_SCALAR, args, &ret, 1, nullptr);
return T(ret, Sv::NONE);
}
};
template <>
struct Sub::CallContext<void> : Sub::CallContext<void, Scalar> {};
template <>
struct Sub::CallContext<void, List> {
static List call (CV* cv, const CallArgs& args) {
AV* av = NULL;
_call(cv, G_ARRAY, args, nullptr, 0, &av);
return List(av, Sv::NONE);
}
};
template <>
struct Sub::CallContext<void, void> {
static void call (CV* cv, const CallArgs& args) { _call(cv, G_VOID, args, nullptr, 0, nullptr); }
};
template <class T, size_t N>
struct Sub::CallContext<enable_if_sv_t<T,void>, std::array<T,N>> {
using type = std::array<T,N>;
static type call (CV* cv, const CallArgs& args) {
SV* svret[N];
auto nret = _call(cv, G_ARRAY, args, svret, N, nullptr);
type ret;
for (size_t i = 0; i < nret; ++i) ret[i] = T(svret[i], Sv::NONE);
return ret;
}
};
template <>
struct Sub::CallContext<void, panda::string> {
static panda::string call (CV* cv, const CallArgs& args) { return CallContext<void, Simple>::call(cv, args).as_string<panda::string>(); }
};
template <class T>
struct Sub::CallContext<panda::enable_if_arithmetic_t<T,void>, T> {
static T call (CV* cv, const CallArgs& args) { return CallContext<void, Simple>::call(cv, args); }
};
}