line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#pragma once |
2
|
|
|
|
|
|
|
#include "base.h" |
3
|
|
|
|
|
|
|
#include "../Ref.h" |
4
|
|
|
|
|
|
|
#include "../Stash.h" |
5
|
|
|
|
|
|
|
#include "../catch.h" |
6
|
|
|
|
|
|
|
#include "../Backref.h" |
7
|
|
|
|
|
|
|
#include |
8
|
|
|
|
|
|
|
#include |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
namespace xs { |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
using panda::refcnt_inc; |
13
|
|
|
|
|
|
|
using panda::refcnt_dec; |
14
|
|
|
|
|
|
|
using panda::refcnt_get; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
namespace typemap { namespace object { |
17
|
|
|
|
|
|
|
using svt_clear_t = int(*)(pTHX_ SV*, MAGIC*); |
18
|
|
|
|
|
|
|
using svt_copy_t = int(*)(pTHX_ SV*, MAGIC*, SV*, const char*, I32); |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
extern CV* fake_dtor; |
21
|
|
|
|
|
|
|
extern svt_copy_t backref_marker; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
void init (); |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
template struct TypemapMarker { |
26
|
0
|
|
|
|
|
|
static int func (pTHX_ SV*, MAGIC*) { return 0; } |
27
|
4884
|
100
|
|
|
|
|
PANDA_GLOBAL_MEMBER_PTR(TypemapMarker, svt_clear_t, get, &func); |
|
|
100
|
|
|
|
|
|
28
|
|
|
|
|
|
|
}; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
panda::string type_details(const std::type_info&); |
31
|
|
|
|
|
|
|
[[ noreturn ]] void _throw_incorrect_arg(SV* arg, const std::type_info& expected, panda::string_view package); |
32
|
|
|
|
|
|
|
[[ noreturn ]] void _throw_no_package (const std::type_info&); |
33
|
|
|
|
|
|
|
}} |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
template |
36
|
|
|
|
|
|
|
struct ObjectStorageIV { |
37
|
|
|
|
|
|
|
static const bool auto_disposable = false; |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
static inline void* get (SV* arg) { |
40
|
|
|
|
|
|
|
return SvIOK(arg) ? (void*)SvIVX(arg) : nullptr; |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
static inline void set (SV* arg, void* ptr) { |
44
|
|
|
|
|
|
|
SvIOK_on(arg); |
45
|
|
|
|
|
|
|
SvIVX(arg) = (IV)ptr; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
static Sv out (const TYPE& var, const Sv& proto) { |
49
|
|
|
|
|
|
|
return Typemap::create(var, proto); |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
}; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
template |
54
|
|
|
|
|
|
|
struct ObjectStorageMG_Impl { |
55
|
|
|
|
|
|
|
using PURE_TYPEMAP = std::remove_const_t>>; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
static const bool auto_disposable = true; |
58
|
|
|
|
|
|
|
|
59
|
1704
|
|
|
|
|
|
static inline void* get (SV* arg) { |
60
|
1704
|
|
|
|
|
|
MAGIC* mg = _get_magic(arg); |
61
|
1704
|
50
|
|
|
|
|
return mg ? mg->mg_ptr : NULL; |
|
|
50
|
|
|
|
|
|
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
|
64
|
703
|
|
|
|
|
|
static inline void set (SV* arg, void* ptr) { |
65
|
703
|
50
|
|
|
|
|
auto marker = xs::Sv::PayloadMarker::get(); |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
66
|
703
|
50
|
|
|
|
|
marker->svt_clear = typemap::object::TypemapMarker::get(); |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
67
|
703
|
|
|
|
|
|
marker->svt_free = _on_free; |
68
|
703
|
|
|
|
|
|
_set_br(marker, BACKREF()); |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
MAGIC* mg; |
71
|
703
|
50
|
|
|
|
|
Newx(mg, 1, MAGIC); |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
72
|
703
|
|
|
|
|
|
mg->mg_moremagic = SvMAGIC(arg); |
73
|
703
|
|
|
|
|
|
SvMAGIC_set(arg, mg); |
74
|
703
|
|
|
|
|
|
mg->mg_virtual = marker; |
75
|
703
|
|
|
|
|
|
mg->mg_type = PERL_MAGIC_ext; |
76
|
703
|
|
|
|
|
|
mg->mg_len = 0; |
77
|
703
|
|
|
|
|
|
mg->mg_obj = nullptr; |
78
|
703
|
|
|
|
|
|
mg->mg_ptr = (char*)ptr; |
79
|
703
|
|
|
|
|
|
mg->mg_private = 0; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
#ifdef USE_ITHREADS |
82
|
|
|
|
|
|
|
marker->svt_dup = _on_svdup; |
83
|
|
|
|
|
|
|
mg->mg_flags = MGf_DUP; |
84
|
|
|
|
|
|
|
#else |
85
|
703
|
|
|
|
|
|
mg->mg_flags = 0; |
86
|
|
|
|
|
|
|
#endif |
87
|
703
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
703
|
50
|
|
|
|
|
static Sv out (const TYPE& var, const Sv& proto) { return _out(var, proto, BACKREF()); } |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
private: |
92
|
1704
|
|
|
|
|
|
static inline MAGIC* _get_magic (SV* sv) { |
93
|
1704
|
|
|
|
|
|
auto marker = typemap::object::TypemapMarker::get(); |
94
|
|
|
|
|
|
|
MAGIC *mg; |
95
|
1704
|
50
|
|
|
|
|
for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) if (mg->mg_virtual && mg->mg_virtual->svt_clear == marker) return mg; |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
96
|
0
|
|
|
|
|
|
return NULL; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
703
|
|
|
|
|
|
static inline void _set_br (Sv::payload_marker_t*, std::false_type) {} |
100
|
|
|
|
|
|
|
static inline void _set_br (Sv::payload_marker_t* marker, std::true_type) { |
101
|
|
|
|
|
|
|
marker->svt_copy = typemap::object::backref_marker; |
102
|
|
|
|
|
|
|
marker->svt_local = _destroy_hook; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
1406
|
|
|
|
|
|
static inline Sv _out (const TYPE& var, const Sv& proto, std::false_type) { return Typemap::create(var, proto); } |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
static inline Sv _out (const TYPE& var, const Sv& proto, std::true_type) { |
108
|
|
|
|
|
|
|
auto br = Backref::get(var); |
109
|
|
|
|
|
|
|
if (!br) return Typemap::create(var, proto); |
110
|
|
|
|
|
|
|
if (br->svobj) { |
111
|
|
|
|
|
|
|
if (!std::is_const>::value) SvREADONLY_off(br->svobj); |
112
|
|
|
|
|
|
|
if (!br->zombie) return Ref::create(br->svobj); |
113
|
|
|
|
|
|
|
_from_zombie(Typemap::IType::template cast(var), br->svobj, _get_magic(br->svobj), br); |
114
|
|
|
|
|
|
|
return Sv::noinc(newRV_noinc(br->svobj)); |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
auto ret = Typemap::create(var, proto); |
117
|
|
|
|
|
|
|
br->svobj = SvRV(ret); |
118
|
|
|
|
|
|
|
return ret; |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
// this hook is invoked before perl frees SV and before DESTROY() method if SV is an object |
122
|
|
|
|
|
|
|
// if non-zero value is returned then the destruction of SV is completely aborted (and DESTROY() method is not called) |
123
|
|
|
|
|
|
|
static int _destroy_hook (pTHX_ SV* sv, MAGIC* mg) { return throw_guard(Sub(), [=]() -> int { |
124
|
|
|
|
|
|
|
TYPEMAP var = Typemap::IType::template in(mg->mg_ptr); |
125
|
|
|
|
|
|
|
auto br = Backref::get(var); |
126
|
|
|
|
|
|
|
if (!br) return 0; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
if (br->zombie) { |
129
|
|
|
|
|
|
|
// as no one has strong reference to our zombie SV backref, its destruction is only possible in 2 cases: |
130
|
|
|
|
|
|
|
// - decremented from C destructor of XSBackref class |
131
|
|
|
|
|
|
|
// - perl is cleaning his arena in destruction phase |
132
|
|
|
|
|
|
|
_restore_dtor(sv); |
133
|
|
|
|
|
|
|
_from_zombie(var, sv, mg, br); |
134
|
|
|
|
|
|
|
if (br->in_cdtor) Typemap::IType::retain(var); // avoid double deletion if refcnt policy of 'var' drops to 0 during deletion |
135
|
|
|
|
|
|
|
else assert(PL_in_clean_objs); |
136
|
|
|
|
|
|
|
return 0; |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
// if we are the only owner or in global destroy phase there is no sense of making zombie |
140
|
|
|
|
|
|
|
if (Typemap::IType::use_count(var) <= 1 || PL_in_clean_objs) { |
141
|
|
|
|
|
|
|
_restore_dtor(sv); |
142
|
|
|
|
|
|
|
br->svobj = NULL; |
143
|
|
|
|
|
|
|
return 0; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
// perl SV goes out of scope, but C object is still accessible -> save SV to zombie |
147
|
|
|
|
|
|
|
_to_zombie(var, sv, mg, br); |
148
|
|
|
|
|
|
|
return 1; |
149
|
|
|
|
|
|
|
});} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
struct ZombieMarker {}; |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
static inline MAGIC* _zombie_get_stash_magic (HV* stash) { |
154
|
|
|
|
|
|
|
auto marker = xs::Sv::PayloadMarker::get(); |
155
|
|
|
|
|
|
|
MAGIC *mg; |
156
|
|
|
|
|
|
|
for (mg = SvMAGIC(stash); mg; mg = mg->mg_moremagic) if (mg->mg_virtual == marker) return mg; |
157
|
|
|
|
|
|
|
return NULL; |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
#if PERL_VERSION >= 24 |
161
|
|
|
|
|
|
|
// prevent S_curse from calling dtor |
162
|
|
|
|
|
|
|
static inline void _ignore_dtor (SV* sv) { |
163
|
|
|
|
|
|
|
auto stash = SvSTASH(sv); |
164
|
|
|
|
|
|
|
auto meta = HvMROMETA(stash); |
165
|
|
|
|
|
|
|
if (meta->destroy == typemap::object::fake_dtor) return; |
166
|
|
|
|
|
|
|
auto stmg = _zombie_get_stash_magic(stash); |
167
|
|
|
|
|
|
|
if (!stmg) stmg = Sv(stash).payload_attach(Sv::Payload(), xs::Sv::PayloadMarker::get()); |
168
|
|
|
|
|
|
|
stmg->mg_obj = (SV*)meta->destroy; |
169
|
|
|
|
|
|
|
stmg->mg_ptr = (char*)(uint64_t)meta->destroy_gen; |
170
|
|
|
|
|
|
|
meta->destroy = typemap::object::fake_dtor; |
171
|
|
|
|
|
|
|
meta->destroy_gen = PL_sub_generation; // make cache valid |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
static inline void _restore_dtor (SV* sv) { |
175
|
|
|
|
|
|
|
auto stash = SvSTASH(sv); |
176
|
|
|
|
|
|
|
auto meta = HvMROMETA(stash); |
177
|
|
|
|
|
|
|
if (meta->destroy != typemap::object::fake_dtor) return; |
178
|
|
|
|
|
|
|
auto stmg = _zombie_get_stash_magic(stash); |
179
|
|
|
|
|
|
|
meta->destroy = (CV*)stmg->mg_obj; // restore dtor |
180
|
|
|
|
|
|
|
meta->destroy_gen = (uint64_t)stmg->mg_ptr; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
#else |
183
|
|
|
|
|
|
|
static inline void _ignore_dtor (SV*) {} |
184
|
|
|
|
|
|
|
static inline void _restore_dtor (SV*) {} |
185
|
|
|
|
|
|
|
#endif |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
static inline void _to_zombie (const TYPEMAP& var, SV* sv, MAGIC*, const Backref* br) { |
188
|
|
|
|
|
|
|
br->zombie = true; |
189
|
|
|
|
|
|
|
SvREFCNT(sv)++; |
190
|
|
|
|
|
|
|
Typemap::IType::release(var); |
191
|
|
|
|
|
|
|
_ignore_dtor(sv); |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
static inline void _from_zombie (const TYPEMAP& var, SV*, MAGIC*, const Backref* br) { |
195
|
|
|
|
|
|
|
br->zombie = false; |
196
|
|
|
|
|
|
|
Typemap::IType::retain(var); |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
2812
|
|
|
|
|
|
static int _on_free (pTHX_ SV* sv, MAGIC* mg) {return throw_guard(Sub(), [=]() -> int { |
200
|
|
|
|
|
|
|
using IType = typename Typemap::IType; |
201
|
703
|
|
|
|
|
|
TYPEMAP downgraded = IType::template in(mg->mg_ptr); |
202
|
703
|
50
|
|
|
|
|
TYPE var = Typemap::template cast(downgraded); |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
203
|
703
|
50
|
|
|
|
|
if (!var) throw "TYPEMAP PANIC: bad object in sv"; |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
204
|
703
|
50
|
|
|
|
|
Typemap::dispose(var, sv); |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
205
|
703
|
|
|
|
|
|
return 0; |
206
|
1406
|
50
|
|
|
|
|
});} |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
static int _on_svdup (pTHX_ MAGIC* mg, CLONE_PARAMS*) { return throw_guard(Sub(), [=]() -> int { |
209
|
|
|
|
|
|
|
using IType = typename Typemap::IType; |
210
|
|
|
|
|
|
|
TYPEMAP downgraded = IType::template in(mg->mg_ptr); |
211
|
|
|
|
|
|
|
TYPEMAP new_downgraded = Typemap::dup(downgraded); |
212
|
|
|
|
|
|
|
_on_svdup_br(downgraded, new_downgraded, BACKREF()); |
213
|
|
|
|
|
|
|
mg->mg_ptr = (char*)IType::out(new_downgraded); |
214
|
|
|
|
|
|
|
return 0; |
215
|
|
|
|
|
|
|
});} |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
static void _on_svdup_br (const TYPEMAP&, const TYPEMAP&, std::false_type) {} |
218
|
|
|
|
|
|
|
static void _on_svdup_br (const TYPEMAP& var, const TYPEMAP& new_var, std::true_type) { |
219
|
|
|
|
|
|
|
auto br = Backref::get(var); |
220
|
|
|
|
|
|
|
auto new_br = Backref::get(new_var); |
221
|
|
|
|
|
|
|
if (br && br->svobj && new_br) { |
222
|
|
|
|
|
|
|
new_br->svobj = MUTABLE_SV(ptr_table_fetch(PL_ptr_table, br->svobj)); |
223
|
|
|
|
|
|
|
assert(new_br->svobj); |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
}; |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
template using ObjectStorageMG = ObjectStorageMG_Impl; |
229
|
|
|
|
|
|
|
template using ObjectStorageMGBackref = ObjectStorageMG_Impl; |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
struct ObjectTypePtr { |
232
|
4884
|
|
|
|
|
|
template static inline T in (void* p) { return static_cast(p); } |
233
|
1406
|
|
|
|
|
|
template static inline const void* out (T* var) { return var; } |
234
|
1406
|
50
|
|
|
|
|
template static inline void destroy (T* var, SV*) { delete var; } |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
235
|
|
|
|
|
|
|
|
236
|
3882
|
50
|
|
|
|
|
template static inline TO cast (FROM* var) { return static_cast(const_cast*>(var)); } |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
237
|
2408
|
|
|
|
|
|
template static inline TO upgrade (FROM* var) { return panda::dyn_cast(var); } |
238
|
|
|
|
|
|
|
}; |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
struct ObjectTypeForeignPtr { |
241
|
|
|
|
|
|
|
template static inline T in (void* p) { return static_cast(p); } |
242
|
|
|
|
|
|
|
template static inline const void* out (T* var) { return var; } |
243
|
|
|
|
|
|
|
template static inline void destroy (T*, SV*) {} |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
template static inline TO cast (FROM* var) { return static_cast(const_cast*>(var)); } |
246
|
|
|
|
|
|
|
template static inline TO upgrade (FROM* var) { return panda::dyn_cast(var); } |
247
|
|
|
|
|
|
|
}; |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
struct ObjectTypeRefcntPtr { |
250
|
|
|
|
|
|
|
template static inline T in (void* p) { return static_cast(p); } |
251
|
|
|
|
|
|
|
template static inline const void* out (T* var) { refcnt_inc(var); return var; } |
252
|
|
|
|
|
|
|
template static inline void destroy (T* var, SV*) { refcnt_dec(var); } |
253
|
|
|
|
|
|
|
template static inline void retain (T* var) { refcnt_inc(var); } |
254
|
|
|
|
|
|
|
template static inline void release (T* var) { refcnt_dec(var); } |
255
|
|
|
|
|
|
|
template static inline uint32_t use_count (T* var) { return refcnt_get(var); } |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
template static inline TO cast (FROM* var) { return static_cast(const_cast*>(var)); } |
258
|
|
|
|
|
|
|
template static inline TO upgrade (FROM* var) { return panda::dyn_cast(var); } |
259
|
|
|
|
|
|
|
}; |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
struct ObjectTypeSharedPtr { |
262
|
|
|
|
|
|
|
template static inline T in (void* p) { return *(static_cast(p)); } |
263
|
|
|
|
|
|
|
template static inline const void* out (const std::shared_ptr& var) { return new std::shared_ptr(var); } |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
template static inline void retain (const std::shared_ptr& var) { |
266
|
|
|
|
|
|
|
char tmp[sizeof(var)]; |
267
|
|
|
|
|
|
|
new (tmp) std::shared_ptr(var); |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
template static inline void release (const std::shared_ptr& var) { |
271
|
|
|
|
|
|
|
std::shared_ptr tmp; |
272
|
|
|
|
|
|
|
memcpy(&tmp, &var, sizeof(tmp)); |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
template static inline uint32_t use_count (const std::shared_ptr& var) { return var.use_count() - 1; } |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
template static inline void destroy (const std::shared_ptr&, SV* arg) { |
278
|
|
|
|
|
|
|
using sp_t = std::shared_ptr; |
279
|
|
|
|
|
|
|
void* p = Typemap::IStorage::get(arg); |
280
|
|
|
|
|
|
|
delete static_cast(p); |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
template static inline TO cast (const std::shared_ptr& var) { |
284
|
|
|
|
|
|
|
return std::static_pointer_cast(std::const_pointer_cast>(var)); |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
template static inline TO upgrade (const std::shared_ptr& var) { |
288
|
|
|
|
|
|
|
return std::dynamic_pointer_cast(var); |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
}; |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
using StaticCast = std::true_type; |
293
|
|
|
|
|
|
|
using DynamicCast = std::false_type; |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
template class _IStorage, class CastType = StaticCast> |
296
|
|
|
|
|
|
|
struct TypemapObject : TypemapBase { |
297
|
|
|
|
|
|
|
using IType = _IType; |
298
|
|
|
|
|
|
|
using IStorage = _IStorage; |
299
|
|
|
|
|
|
|
using TypeNP = typename std::remove_pointer::type; |
300
|
|
|
|
|
|
|
|
301
|
1715
|
|
|
|
|
|
static TYPE in (SV* arg) { |
302
|
1715
|
100
|
|
|
|
|
if (!SvOBJECT(arg)) { |
|
|
100
|
|
|
|
|
|
303
|
1658
|
100
|
|
|
|
|
if (SvROK(arg)) { |
|
|
100
|
|
|
|
|
|
304
|
1647
|
|
|
|
|
|
arg = SvRV(arg); |
305
|
1647
|
50
|
|
|
|
|
if (!SvOBJECT(arg)) throw "arg is a reference to non-object"; |
|
|
50
|
|
|
|
|
|
306
|
|
|
|
|
|
|
} |
307
|
11
|
50
|
|
|
|
|
else if (!SvOK(arg)) return TYPE(); |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
308
|
0
|
|
|
|
|
|
else throw "arg is not a reference to object"; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
1704
|
50
|
|
|
|
|
auto ptr = IStorage::get(arg); |
|
|
50
|
|
|
|
|
|
312
|
1704
|
50
|
|
|
|
|
if (ptr) { |
|
|
50
|
|
|
|
|
|
313
|
1704
|
|
|
|
|
|
TYPEMAP downgraded = IType::template in(ptr); |
314
|
1704
|
50
|
|
|
|
|
TYPE ret = cast(downgraded); |
|
|
50
|
|
|
|
|
|
315
|
1704
|
50
|
|
|
|
|
if (ret) { |
|
|
50
|
|
|
|
|
|
316
|
1704
|
50
|
|
|
|
|
if (!std::is_const::value && SvREADONLY(arg)) throw "cannot modify read-only object"; |
|
|
50
|
|
|
|
|
|
317
|
1704
|
|
|
|
|
|
return ret; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
// it's definitely developer bug, there is no known way to determine real object tyep |
322
|
|
|
|
|
|
|
// from void*, see https://stackoverflow.com/questions/1718412/find-out-type-of-c-void-pointer |
323
|
0
|
|
|
|
|
|
auto package = Typemap::package(); |
324
|
0
|
|
|
|
|
|
auto package_view = panda::string_view{ package.data(), package.length() }; |
325
|
1715
|
|
|
|
|
|
typemap::object::_throw_incorrect_arg(arg, typeid (TYPE), package_view); |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
|
328
|
1406
|
|
|
|
|
|
static Sv out (const TYPE& var, const Sv& proto = Sv()) { return IStorage::out(var, proto); } |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
/* proto is a hint for TypemapObject's out/create to attach 'var' to |
331
|
|
|
|
|
|
|
* it might be: |
332
|
|
|
|
|
|
|
* 1) blessed object (or reference to it): in this case it is used as final object |
333
|
|
|
|
|
|
|
* typical usage - when calling next method |
334
|
|
|
|
|
|
|
* PROTO = stash.call_next(...); |
335
|
|
|
|
|
|
|
* 2) class name or stash to bless to: in this case reference to undef is created and blessed into this class |
336
|
|
|
|
|
|
|
* typical usage - in constructor, to bless to the class 'new' was called into, not to default class |
337
|
|
|
|
|
|
|
* PROTO = ST(0); |
338
|
|
|
|
|
|
|
* 3) other values (or reference to it): in this case it is blessed to typemap's default class and used |
339
|
|
|
|
|
|
|
* typical usage - in constructor or in overloaded typemap's create() method to specify object's base |
340
|
|
|
|
|
|
|
* PROTO = Array::create(); |
341
|
|
|
|
|
|
|
* 4) empty: in this case reference to undef is created and blessed to typemap's default class and used |
342
|
|
|
|
|
|
|
*/ |
343
|
703
|
|
|
|
|
|
static Sv create (const TYPE& var, const Sv& proto = Sv()) { |
344
|
703
|
50
|
|
|
|
|
if (!var) return &PL_sv_undef; |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
345
|
1406
|
|
|
|
|
|
Sv rv; |
346
|
|
|
|
|
|
|
SV* base; |
347
|
703
|
50
|
|
|
|
|
if (proto) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
348
|
0
|
0
|
|
|
|
|
if (SvROK(proto)) { // ref to object/base |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
349
|
0
|
0
|
|
|
|
|
rv = proto; |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
350
|
0
|
|
|
|
|
|
base = SvRV(proto); |
351
|
|
|
|
|
|
|
} |
352
|
0
|
0
|
|
|
|
|
else if (proto.type() <= SVt_PVMG) { // class name |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
353
|
0
|
0
|
|
|
|
|
if (SvOBJECT(proto)) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
354
|
0
|
|
|
|
|
|
base = proto; |
355
|
0
|
0
|
|
|
|
|
rv = Sv::noinc(newRV_noinc(base)); |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
356
|
|
|
|
|
|
|
} else { |
357
|
0
|
0
|
|
|
|
|
base = newSV_type(SVt_PVMG); |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
358
|
0
|
0
|
|
|
|
|
rv = Sv::noinc(newRV_noinc(base)); |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
359
|
0
|
0
|
|
|
|
|
sv_bless(rv, gv_stashsv(proto, GV_ADD)); |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
360
|
|
|
|
|
|
|
} |
361
|
0
|
|
|
|
|
|
goto ATTACH; // skip optional blessing |
362
|
|
|
|
|
|
|
} |
363
|
0
|
0
|
|
|
|
|
else if (proto.type() == SVt_PVHV && HvNAME(proto)) { // stash |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
364
|
0
|
0
|
|
|
|
|
base = newSV_type(SVt_PVMG); |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
365
|
0
|
0
|
|
|
|
|
rv = Sv::noinc(newRV_noinc(base)); |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
366
|
0
|
0
|
|
|
|
|
sv_bless(rv, proto.get()); |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
367
|
0
|
|
|
|
|
|
goto ATTACH; // skip optional blessing |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
else { // base given |
370
|
0
|
0
|
|
|
|
|
rv = Sv::noinc(newRV(proto)); |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
371
|
0
|
|
|
|
|
|
base = proto; |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
else { // nothing given, create ref to undef |
375
|
703
|
50
|
|
|
|
|
base = newSV_type(SVt_PVMG); |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
376
|
703
|
50
|
|
|
|
|
rv = Sv::noinc(newRV_noinc(base)); |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
377
|
703
|
|
|
|
|
|
goto BLESS; // definitely needs blessing |
378
|
|
|
|
|
|
|
} |
379
|
|
|
|
|
|
|
|
380
|
0
|
0
|
|
|
|
|
if (!SvOBJECT(base)) { // not blessed -> bless to default typemap's class |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
381
|
|
|
|
|
|
|
BLESS: |
382
|
703
|
100
|
|
|
|
|
static PERL_THREAD_LOCAL HV* stash = gv_stashpvn(Typemap::package().data(), Typemap::package().length(), GV_ADD); |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
383
|
703
|
50
|
|
|
|
|
sv_bless(rv, stash); // TODO: custom faster bless |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
384
|
|
|
|
|
|
|
} |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
ATTACH: |
387
|
703
|
50
|
|
|
|
|
IStorage::set(base, const_cast(IType::out(IType::template cast(var)))); |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
if (std::is_const::value) SvREADONLY_on(base); |
390
|
|
|
|
|
|
|
|
391
|
703
|
|
|
|
|
|
return rv; |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
static TYPEMAP dup (const TYPEMAP& obj) { return obj; } |
395
|
|
|
|
|
|
|
|
396
|
703
|
|
|
|
|
|
static void dispose (const TYPE& var, SV* arg) { |
397
|
703
|
|
|
|
|
|
IType::destroy(var, arg); |
398
|
703
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
static void destroy (const TYPE& var, SV* arg) { |
401
|
|
|
|
|
|
|
if (!std::is_same::value) return; |
402
|
|
|
|
|
|
|
if (!IStorage::auto_disposable) dispose(var, arg); |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
|
405
|
2442
|
50
|
|
|
|
|
template static inline TO cast (FROM v) { return _cast(v, CastType()); } |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
static panda::string_view package () { typemap::object::_throw_no_package(typeid(TYPE)); return ""; } |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
static Stash default_stash () { |
410
|
|
|
|
|
|
|
static PERL_THREAD_LOCAL Stash stash = gv_stashpvn(Typemap::package().data(), Typemap::package().length(), GV_ADD); |
411
|
|
|
|
|
|
|
return stash; |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
private: |
415
|
2408
|
|
|
|
|
|
template static inline TO _cast (FROM v, DynamicCast) { return IType::template upgrade(v); } |
416
|
2476
|
|
|
|
|
|
template static inline TO _cast (FROM v, StaticCast) { return IType::template cast(v); } |
417
|
|
|
|
|
|
|
}; |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
} |