File Coverage

src/xs/Sub.h
Criterion Covered Total %
statement 3 3 100.0
branch 1 2 50.0
condition n/a
subroutine n/a
pod n/a
total 4 5 80.0


line stmt bran cond sub pod time code
1             #pragma once
2             #include
3             #include
4             #include
5              
6             namespace xs {
7              
8             using xs::my_perl;
9              
10             struct Sub : Sv {
11             static Sub noinc (SV* val) { return Sub(val, NONE); }
12             static Sub noinc (CV* val) { return Sub(val, NONE); }
13              
14             Sub (std::nullptr_t = nullptr) {}
15             Sub (SV* sv, bool policy = INCREMENT) : Sv(sv, policy) { _validate(); }
16             Sub (CV* sv, bool policy = INCREMENT) : Sv(sv, policy) {}
17              
18             explicit
19             Sub (panda::string_view subname, I32 flags = 0) {
20             *this = get_cvn_flags(subname.data(), subname.length(), flags);
21             }
22              
23             Sub (const Sub& oth) : Sv(oth) {}
24             Sub (Sub&& oth) : Sv(std::move(oth)) {}
25             Sub (const Sv& oth) : Sv(oth) { _validate(); }
26             Sub (Sv&& oth) : Sv(std::move(oth)) { _validate(); }
27              
28             Sub (const Simple&) = delete;
29             Sub (const Array&) = delete;
30             Sub (const Hash&) = delete;
31             Sub (const Glob&) = delete;
32             Sub (const Io&) = delete;
33              
34             Sub& operator= (SV* val) { Sv::operator=(val); _validate(); return *this; }
35             Sub& operator= (CV* val) { Sv::operator=(val); return *this; }
36             Sub& operator= (const Sub& oth) { Sv::operator=(oth); return *this; }
37             Sub& operator= (Sub&& oth) { Sv::operator=(std::move(oth)); return *this; }
38             Sub& operator= (const Sv& oth) { return operator=(oth.get()); }
39             Sub& operator= (Sv&& oth) { Sv::operator=(std::move(oth)); _validate(); return *this; }
40             Sub& operator= (const Simple&) = delete;
41             Sub& operator= (const Array&) = delete;
42             Sub& operator= (const Hash&) = delete;
43             Sub& operator= (const Glob&) = delete;
44             Sub& operator= (const Io&) = delete;
45              
46             void set (SV* val) { Sv::operator=(val); }
47              
48             operator AV* () const = delete;
49             operator HV* () const = delete;
50             operator CV* () const { return (CV*)sv; }
51             operator GV* () const = delete;
52             operator IO* () const = delete;
53              
54             CV* operator-> () const { return (CV*)sv; }
55              
56             template panda::enable_if_one_of_t* get () const { return (T*)sv; }
57              
58             Stash stash () const;
59             Glob glob () const;
60              
61 6           panda::string_view name () const {
62 6 50         GV* gv = CvGV((CV*)sv);
63 6           return panda::string_view(GvNAME(gv), GvNAMELEN(gv));
64             }
65              
66             bool named () const { return CvNAMED((CV*)sv); }
67              
68             Sub SUPER () const {
69             GV* mygv = CvGV((CV*)sv);
70             GV* supergv = gv_fetchmeth_pvn(GvSTASH(mygv), GvNAME(mygv), GvNAMELEN(mygv), 0, GV_SUPER);
71             return Sub(supergv ? GvCV(supergv) : nullptr);
72             }
73              
74             Sub SUPER_strict () const {
75             Sub ret = SUPER();
76             if (!ret) _throw_super();
77             return ret;
78             }
79              
80             private:
81             struct CallArgs {
82             SV* self;
83             SV*const* list;
84             const Scalar* scalars;
85             size_t items;
86             };
87              
88             template struct CallContext;
89              
90             public:
91             template using call_t = decltype(CallContext::call((CV*)nullptr, CallArgs()));
92              
93             template
94             call_t call (Args&&...va) const {
95             auto args = _get_args(va...);
96             return CallContext::call((CV*)sv, args);
97             }
98              
99             template
100             Scalar operator() (Args&&...args) const { return call(std::forward(args)...); }
101              
102             private:
103             void _validate () {
104             if (!sv) return;
105             if (SvTYPE(sv) == SVt_PVCV) return;
106             if (SvROK(sv)) { // reference to code?
107             SV* val = SvRV(sv);
108             if (SvTYPE(val) == SVt_PVCV) {
109             Sv::operator=(val);
110             return;
111             }
112             }
113             if (is_undef()) return reset();
114             reset();
115             throw std::invalid_argument("SV is not a Sub or Sub reference");
116             }
117              
118             void _throw_super () const;
119              
120             template
121             struct VCallArgs : CallArgs {
122             SV* list[sizeof...(Args)];
123             VCallArgs (Args&&...args) : CallArgs{nullptr, list, nullptr, sizeof...(Args)}, list{std::forward(args)...} {
124             for (auto sv : list)
125             if (sv && SvTYPE(sv) > SVt_PVMG && SvTYPE(sv) != SVt_PVGV)
126             throw std::invalid_argument("one of arguments for sub.call() is not a scalar value");
127             }
128             };
129              
130             template struct type_pack {};
131              
132             static CallArgs _get_args (SV*const* args = nullptr, size_t items = 0) { return {nullptr, args, nullptr, items}; }
133             static CallArgs _get_args (SV* arg0, SV*const* args, size_t items) { return { arg0, args, nullptr, items}; }
134             static CallArgs _get_args (const Scalar* args, size_t items) { return {nullptr, nullptr, args, items}; }
135             static CallArgs _get_args (SV* arg0, const Scalar* args, size_t items) { return { arg0, nullptr, args, items}; }
136             static CallArgs _get_args (const std::initializer_list& l) { return {nullptr, nullptr, l.begin(), l.size()}; }
137             static CallArgs _get_args (SV* arg0, const std::initializer_list& l) { return { arg0, nullptr, l.begin(), l.size()}; }
138              
139             template ()))...>>
140             static VCallArgs _get_args (Args&&...args) { return {std::forward(args)...}; }
141              
142             static size_t _call (CV*, I32 flags, const CallArgs&, SV** ret, size_t maxret, AV** avr);
143             };
144              
145             template
146             struct Sub::CallContext> {
147             using type = std::tuple;
148             static constexpr size_t N = sizeof...(Types);
149              
150             static type call (CV* cv, const CallArgs& args) {
151             SV* ret[N] = {nullptr};
152             _call(cv, G_ARRAY, args, ret, N, nullptr);
153             return _make_tuple(ret, std::make_index_sequence());
154             }
155              
156             template
157             static T _make_tuple (SV** svs, std::integer_sequence) {
158             return T(typename std::tuple_element::type(svs[Inds], Sv::NONE)...);
159             }
160             };
161              
162             template
163             struct Sub::CallContext : Sub::CallContext> {};
164              
165             template
166             struct Sub::CallContext, T> {
167             static T call (CV* cv, const CallArgs& args) {
168             SV* ret = NULL;
169             _call(cv, G_SCALAR, args, &ret, 1, nullptr);
170             return T(ret, Sv::NONE);
171             }
172             };
173              
174             template <>
175             struct Sub::CallContext : Sub::CallContext {};
176              
177             template <>
178             struct Sub::CallContext {
179             static List call (CV* cv, const CallArgs& args) {
180             AV* av = NULL;
181             _call(cv, G_ARRAY, args, nullptr, 0, &av);
182             return List(av, Sv::NONE);
183             }
184             };
185              
186             template <>
187             struct Sub::CallContext {
188             static void call (CV* cv, const CallArgs& args) { _call(cv, G_VOID, args, nullptr, 0, nullptr); }
189             };
190              
191             template
192             struct Sub::CallContext, std::array> {
193             using type = std::array;
194             static type call (CV* cv, const CallArgs& args) {
195             SV* svret[N];
196             auto nret = _call(cv, G_ARRAY, args, svret, N, nullptr);
197             type ret;
198             for (size_t i = 0; i < nret; ++i) ret[i] = T(svret[i], Sv::NONE);
199             return ret;
200             }
201             };
202              
203             template <>
204             struct Sub::CallContext {
205             static panda::string call (CV* cv, const CallArgs& args) { return CallContext::call(cv, args).as_string(); }
206             };
207              
208             template
209             struct Sub::CallContext, T> {
210             static T call (CV* cv, const CallArgs& args) { return CallContext::call(cv, args); }
211             };
212              
213             }