rcx 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.clang-format +28 -0
- data/.rspec +3 -0
- data/.yarnrc.yml +1 -0
- data/CHANGELOG.md +4 -0
- data/Doxyfile +2863 -0
- data/LICENSE.txt +23 -0
- data/README.md +20 -0
- data/Rakefile +39 -0
- data/examples/class.cpp +20 -0
- data/include/rcx/internal/rcx.hpp +1258 -0
- data/include/rcx/internal/rcx_impl.hpp +1365 -0
- data/include/rcx/rcx.hpp +6 -0
- data/lib/rcx/mkmf.rb +54 -0
- data/lib/rcx/version.rb +3 -0
- data/lib/rcx.rb +2 -0
- data/package.json +6 -0
- data/yarn.lock +21 -0
- metadata +74 -0
@@ -0,0 +1,1365 @@
|
|
1
|
+
// SPDX-License-Identifier: BSL-1.0
|
2
|
+
// SPDX-FileCopyrightText: Copyright 2024-2025 Kasumi Hanazuki <kasumi@rollingapple.net>
|
3
|
+
|
4
|
+
#include <concepts>
|
5
|
+
#include <memory>
|
6
|
+
#include <ranges>
|
7
|
+
#include <stdexcept>
|
8
|
+
#include <string_view>
|
9
|
+
#include <tuple>
|
10
|
+
#include <type_traits>
|
11
|
+
#include <typeinfo>
|
12
|
+
#include <utility>
|
13
|
+
|
14
|
+
#include <ffi.h>
|
15
|
+
#include <rcx/internal/rcx.hpp>
|
16
|
+
|
17
|
+
#if HAVE_CXXABI_H
|
18
|
+
#include <cxxabi.h>
|
19
|
+
#endif
|
20
|
+
|
21
|
+
namespace std {
|
22
|
+
template <std::derived_from<rcx::Value> T>
|
23
|
+
template <typename ParseContext>
|
24
|
+
constexpr ParseContext::iterator formatter<T, char>::parse(ParseContext &ctx) {
|
25
|
+
auto it = ctx.begin();
|
26
|
+
if(it == ctx.end()) {
|
27
|
+
return it;
|
28
|
+
}
|
29
|
+
if(*it == '#') {
|
30
|
+
inspect = true;
|
31
|
+
++it;
|
32
|
+
}
|
33
|
+
if(it == ctx.end() || *it != '}') {
|
34
|
+
throw std::format_error("Invalid format args for std::formatter<ValueBase>.");
|
35
|
+
}
|
36
|
+
return it;
|
37
|
+
}
|
38
|
+
|
39
|
+
template <std::derived_from<rcx::Value> T>
|
40
|
+
template <typename FormatContext>
|
41
|
+
FormatContext::iterator formatter<T, char>::format(T value, FormatContext &ctx) const {
|
42
|
+
return std::format_to(ctx.out(), "{}",
|
43
|
+
static_cast<std::string_view>(inspect ? value.inspect() : value.to_string()));
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
namespace rcx {
|
48
|
+
namespace detail {
|
49
|
+
#if HAVE_ABI___CXA_DEMANGLE
|
50
|
+
static bool constexpr have_abi_cxa_demangle = true;
|
51
|
+
#else
|
52
|
+
static bool constexpr have_abi_cxa_demangle = false;
|
53
|
+
#endif
|
54
|
+
#if HAVE_ABI___CXA_CURRENT_EXCEPTION_TYPE
|
55
|
+
static bool constexpr have_abi_cxa_current_exception_type = true;
|
56
|
+
#else
|
57
|
+
static bool constexpr have_abi_cxa_current_exception_type = false;
|
58
|
+
#endif
|
59
|
+
|
60
|
+
auto cxx_protect(std::invocable<> auto const &functor) noexcept
|
61
|
+
-> std::invoke_result_t<decltype(functor)>;
|
62
|
+
|
63
|
+
inline NativeRbFunc *RCX_Nonnull alloc_callback(std::function<RbFunc> f) {
|
64
|
+
static std::array argtypes = {
|
65
|
+
&ffi_type_sint, // int argc
|
66
|
+
&ffi_type_pointer, // VALUE *argv
|
67
|
+
&ffi_type_pointer, // VALUE self (has the same size as void *)
|
68
|
+
};
|
69
|
+
static ffi_cif cif = [] {
|
70
|
+
ffi_cif cif;
|
71
|
+
if(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, argtypes.size(), &ffi_type_pointer,
|
72
|
+
argtypes.data()) != FFI_OK) {
|
73
|
+
throw std::runtime_error("ffi_prep_cif failed");
|
74
|
+
}
|
75
|
+
return cif;
|
76
|
+
}();
|
77
|
+
static auto const trampoline = [](ffi_cif *RCX_Nonnull, void *RCX_Nonnull ret,
|
78
|
+
void *RCX_Nonnull args[], void *RCX_Nonnull function) {
|
79
|
+
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
80
|
+
auto argc = *reinterpret_cast<int *>(args[0]);
|
81
|
+
auto argv = *reinterpret_cast<Value **>(args[1]);
|
82
|
+
auto self = *reinterpret_cast<Value *>(args[2]);
|
83
|
+
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
84
|
+
*reinterpret_cast<Value *>(ret) = cxx_protect([&] {
|
85
|
+
return (*reinterpret_cast<decltype(f) *>(function))(std::span<Value>(argv, argc), self);
|
86
|
+
});
|
87
|
+
};
|
88
|
+
|
89
|
+
void *callback = nullptr;
|
90
|
+
auto closure = static_cast<ffi_closure *>(ffi_closure_alloc(sizeof(ffi_closure), &callback));
|
91
|
+
|
92
|
+
if(!closure) {
|
93
|
+
throw std::runtime_error{"ffi_closure_alloc failed"};
|
94
|
+
}
|
95
|
+
|
96
|
+
if(ffi_prep_closure_loc(closure, &cif, trampoline,
|
97
|
+
new decltype(f)(std::move(f)), // let it leak
|
98
|
+
callback) != FFI_OK) {
|
99
|
+
throw std::runtime_error{"ffi_prep_closure_loc failed"};
|
100
|
+
}
|
101
|
+
|
102
|
+
return reinterpret_cast<NativeRbFunc *>(callback);
|
103
|
+
}
|
104
|
+
|
105
|
+
template <concepts::ArgSpec... ArgSpec> struct Parser {
|
106
|
+
std::span<Value> args;
|
107
|
+
Value self;
|
108
|
+
|
109
|
+
auto parse(Ruby &ruby, std::invocable<typename ArgSpec::ResultType...> auto &&func)
|
110
|
+
-> std::invoke_result_t<decltype(func), typename ArgSpec::ResultType...> {
|
111
|
+
// Experssions in an initializer list is evaluated from left to right, in contrast to
|
112
|
+
// function arguments.
|
113
|
+
return std::apply(std::forward<decltype(func)>(func),
|
114
|
+
std::tuple<typename ArgSpec::ResultType...>{ArgSpec::parse(ruby, self, args)...});
|
115
|
+
}
|
116
|
+
};
|
117
|
+
|
118
|
+
template <typename... ArgSpec> struct method_callback {
|
119
|
+
template <typename F> static NativeRbFunc *RCX_Nonnull alloc(F &&function) {
|
120
|
+
return alloc_callback([function](std::span<Value> args, Value self) -> Value {
|
121
|
+
Parser<ArgSpec...> parser{args, self};
|
122
|
+
using Result = decltype(parser.parse(detail::unsafe_ruby(), std::move(function)));
|
123
|
+
if constexpr(std::is_void_v<Result>) {
|
124
|
+
parser.parse(detail::unsafe_ruby(), std::move(function));
|
125
|
+
return {};
|
126
|
+
} else {
|
127
|
+
return into_Value<Result>(parser.parse(detail::unsafe_ruby(), function));
|
128
|
+
}
|
129
|
+
});
|
130
|
+
}
|
131
|
+
};
|
132
|
+
|
133
|
+
inline void check_jump_tag(int state) {
|
134
|
+
enum {
|
135
|
+
RUBY_TAG_NONE = 0,
|
136
|
+
RUBY_TAG_RAISE = 6,
|
137
|
+
};
|
138
|
+
|
139
|
+
switch(state) {
|
140
|
+
case RUBY_TAG_NONE:
|
141
|
+
return;
|
142
|
+
case RUBY_TAG_RAISE: {
|
143
|
+
Exception const err = detail::unsafe_coerce<Exception>(rb_errinfo());
|
144
|
+
rb_set_errinfo(RUBY_Qnil);
|
145
|
+
throw err;
|
146
|
+
}
|
147
|
+
default:
|
148
|
+
throw Jump(state);
|
149
|
+
}
|
150
|
+
}
|
151
|
+
|
152
|
+
template <std::invocable<> F>
|
153
|
+
inline auto protect(F functor) -> auto
|
154
|
+
requires(noexcept(functor()))
|
155
|
+
{
|
156
|
+
int state = 0;
|
157
|
+
|
158
|
+
using Result = std::invoke_result_t<F>;
|
159
|
+
if constexpr(std::is_void_v<Result>) {
|
160
|
+
::rb_protect(
|
161
|
+
[](VALUE callback) {
|
162
|
+
(*reinterpret_cast<F *>(callback))();
|
163
|
+
return RUBY_Qnil;
|
164
|
+
},
|
165
|
+
reinterpret_cast<VALUE>(&functor), &state);
|
166
|
+
check_jump_tag(state);
|
167
|
+
return;
|
168
|
+
} else {
|
169
|
+
std::optional<Result> result;
|
170
|
+
auto callback = [&functor, &result]() { *result = functor(); };
|
171
|
+
using Callback = decltype(callback);
|
172
|
+
|
173
|
+
::rb_protect(
|
174
|
+
[](VALUE callback) {
|
175
|
+
(*reinterpret_cast<Callback *>(callback))();
|
176
|
+
return RUBY_Qnil;
|
177
|
+
},
|
178
|
+
reinterpret_cast<VALUE>(&callback), &state);
|
179
|
+
check_jump_tag(state);
|
180
|
+
return std::move(*result);
|
181
|
+
}
|
182
|
+
}
|
183
|
+
|
184
|
+
template <typename A, typename R>
|
185
|
+
requires(std::is_integral_v<A> && sizeof(A) == sizeof(VALUE) && std::is_integral_v<R> &&
|
186
|
+
sizeof(R) == sizeof(VALUE))
|
187
|
+
inline R protect(R (*RCX_Nonnull func)(A) noexcept, A arg) {
|
188
|
+
int state = 0;
|
189
|
+
|
190
|
+
auto result =
|
191
|
+
reinterpret_cast<R>(::rb_protect(reinterpret_cast<VALUE (*RCX_Nonnull)(VALUE)>(func),
|
192
|
+
reinterpret_cast<VALUE>(arg), &state));
|
193
|
+
check_jump_tag(state);
|
194
|
+
return result;
|
195
|
+
}
|
196
|
+
|
197
|
+
/**
|
198
|
+
* Assumes the (C) function doesn't throw C++ exceptions.
|
199
|
+
*/
|
200
|
+
template <typename R, typename... A>
|
201
|
+
constexpr auto assume_noexcept(R (*RCX_Nonnull f)(A...)) noexcept
|
202
|
+
-> R (*RCX_Nonnull)(A...) noexcept {
|
203
|
+
return reinterpret_cast<R (*RCX_Nonnull)(A...) noexcept>(f);
|
204
|
+
}
|
205
|
+
template <typename R, typename... A>
|
206
|
+
constexpr auto assume_noexcept(R (*RCX_Nonnull f)(A..., ...)) noexcept
|
207
|
+
-> R (*RCX_Nonnull)(A..., ...) noexcept {
|
208
|
+
return reinterpret_cast<R (*RCX_Nonnull)(A..., ...) noexcept>(f);
|
209
|
+
}
|
210
|
+
|
211
|
+
/**
|
212
|
+
* Converts anything into ID.
|
213
|
+
*
|
214
|
+
* Do not store the return value. Dynamic IDs may be garbage-collected.
|
215
|
+
*/
|
216
|
+
template <concepts::Identifier I> inline decltype(auto) into_ID(I &&id) noexcept {
|
217
|
+
if constexpr(requires {
|
218
|
+
{ id.as_ID() } noexcept -> std::same_as<ID>;
|
219
|
+
}) {
|
220
|
+
return id.as_ID();
|
221
|
+
} else {
|
222
|
+
return Symbol(std::forward<decltype(id)>(id)).as_ID();
|
223
|
+
}
|
224
|
+
}
|
225
|
+
|
226
|
+
// Calculate the default type of the self parameter for the methods of ClassT<T>.
|
227
|
+
template <concepts::ConvertibleFromValue T>
|
228
|
+
using self_type =
|
229
|
+
std::conditional_t<std::derived_from<T, typed_data::WrappedStructBase>, T &, T>;
|
230
|
+
template <concepts::ConvertibleFromValue T>
|
231
|
+
using self_type_const =
|
232
|
+
std::conditional_t<std::derived_from<T, typed_data::WrappedStructBase>, T const &, T const>;
|
233
|
+
};
|
234
|
+
|
235
|
+
namespace arg {
|
236
|
+
template <concepts::ConvertibleFromValue T>
|
237
|
+
inline typename Self<T>::ResultType Self<T>::parse(Ruby &, Value self, std::span<Value> &) {
|
238
|
+
return from_Value<T>(self);
|
239
|
+
}
|
240
|
+
|
241
|
+
template <concepts::ConvertibleFromValue T, detail::cxstring name>
|
242
|
+
inline typename Arg<T, name>::ResultType Arg<T, name>::parse(
|
243
|
+
Ruby &, Value, std::span<Value> &args) {
|
244
|
+
if(args.empty()) {
|
245
|
+
throw Exception::format(builtin::ArgumentError, "Missing required argument: {}",
|
246
|
+
static_cast<std::string_view>(name));
|
247
|
+
}
|
248
|
+
auto arg = from_Value<T>(args.front());
|
249
|
+
args = std::ranges::drop_view(args, 1);
|
250
|
+
return arg;
|
251
|
+
}
|
252
|
+
|
253
|
+
template <concepts::ConvertibleFromValue T, detail::cxstring name>
|
254
|
+
inline typename ArgOpt<T, name>::ResultType ArgOpt<T, name>::parse(
|
255
|
+
Ruby &, Value, std::span<Value> &args) {
|
256
|
+
if(args.empty()) {
|
257
|
+
return std::nullopt;
|
258
|
+
}
|
259
|
+
auto arg = from_Value<T>(args.front());
|
260
|
+
args = std::ranges::drop_view(args, 1);
|
261
|
+
return arg;
|
262
|
+
}
|
263
|
+
|
264
|
+
inline ArgSplat::ResultType ArgSplat::parse(Ruby &, Value self, std::span<Value> &args) {
|
265
|
+
auto result = Array::new_from(args);
|
266
|
+
args = {};
|
267
|
+
return result;
|
268
|
+
}
|
269
|
+
|
270
|
+
inline Block::ResultType Block::parse(Ruby &, Value, std::span<Value> &) {
|
271
|
+
return detail::unsafe_coerce<Proc>(
|
272
|
+
detail::protect([]() noexcept { return ::rb_block_proc(); }));
|
273
|
+
}
|
274
|
+
|
275
|
+
inline BlockOpt::ResultType BlockOpt::parse(Ruby &ruby, Value self, std::span<Value> &args) {
|
276
|
+
if(!::rb_block_given_p()) {
|
277
|
+
return std::nullopt;
|
278
|
+
}
|
279
|
+
return block.parse(ruby, self, args);
|
280
|
+
}
|
281
|
+
}
|
282
|
+
|
283
|
+
namespace convert {
|
284
|
+
template <typename T> inline Value into_Value(T value) {
|
285
|
+
if constexpr(std::convertible_to<T, Value>) {
|
286
|
+
return value;
|
287
|
+
} else {
|
288
|
+
return IntoValue<std::remove_reference_t<T>>().convert(value);
|
289
|
+
}
|
290
|
+
}
|
291
|
+
template <typename T> inline auto from_Value(Value value) -> auto {
|
292
|
+
if constexpr(std::convertible_to<Value, T>) {
|
293
|
+
return value;
|
294
|
+
} else {
|
295
|
+
return FromValue<std::remove_reference_t<T>>().convert(value);
|
296
|
+
}
|
297
|
+
}
|
298
|
+
|
299
|
+
inline bool FromValue<bool>::convert(Value value) {
|
300
|
+
return RB_TEST(value.as_VALUE());
|
301
|
+
}
|
302
|
+
inline Value IntoValue<bool>::convert(bool value) {
|
303
|
+
return detail::unsafe_coerce<Value>(value ? RUBY_Qtrue : RUBY_Qfalse);
|
304
|
+
};
|
305
|
+
|
306
|
+
inline signed char FromValue<signed char>::convert(Value value) {
|
307
|
+
int const v = NUM2INT(value.as_VALUE());
|
308
|
+
signed char const r = static_cast<signed char>(v);
|
309
|
+
if(v != static_cast<int>(r)) {
|
310
|
+
throw Exception::format(builtin::RangeError,
|
311
|
+
"integer {} too {} to convert to 'signed char'", v, v < 0 ? "small" : "big");
|
312
|
+
}
|
313
|
+
return r;
|
314
|
+
}
|
315
|
+
inline Value IntoValue<signed char>::convert(signed char value) {
|
316
|
+
return detail::unsafe_coerce<Value>(INT2FIX(value));
|
317
|
+
};
|
318
|
+
|
319
|
+
inline unsigned char FromValue<unsigned char>::convert(Value value) {
|
320
|
+
int const v = NUM2INT(value.as_VALUE());
|
321
|
+
unsigned char const r = static_cast<unsigned char>(v);
|
322
|
+
if(v != static_cast<int>(r)) {
|
323
|
+
throw Exception::format(builtin::RangeError,
|
324
|
+
"integer {} too {} to convert to 'unsigned char'", v, v < 0 ? "small" : "big");
|
325
|
+
}
|
326
|
+
return r;
|
327
|
+
}
|
328
|
+
inline Value IntoValue<unsigned char>::convert(unsigned char value) {
|
329
|
+
return detail::unsafe_coerce<Value>(INT2FIX(value));
|
330
|
+
};
|
331
|
+
|
332
|
+
#define RCX_DEFINE_CONV(TYPE, FROM_VALUE, INTO_VALUE) \
|
333
|
+
inline TYPE FromValue<TYPE>::convert(Value value) { \
|
334
|
+
return detail::protect([v = value.as_VALUE()]() noexcept { return FROM_VALUE(v); }); \
|
335
|
+
} \
|
336
|
+
inline Value IntoValue<TYPE>::convert(TYPE value) { \
|
337
|
+
return detail::unsafe_coerce<Value>(INTO_VALUE(value)); \
|
338
|
+
}
|
339
|
+
|
340
|
+
RCX_DEFINE_CONV(short, RB_NUM2SHORT, RB_INT2FIX);
|
341
|
+
RCX_DEFINE_CONV(unsigned short, RB_NUM2USHORT, RB_INT2FIX);
|
342
|
+
RCX_DEFINE_CONV(int, RB_NUM2INT, RB_INT2NUM);
|
343
|
+
RCX_DEFINE_CONV(unsigned int, RB_NUM2UINT, RB_UINT2NUM);
|
344
|
+
RCX_DEFINE_CONV(long, RB_NUM2LONG, RB_LONG2NUM);
|
345
|
+
RCX_DEFINE_CONV(unsigned long, RB_NUM2ULONG, RB_ULONG2NUM);
|
346
|
+
RCX_DEFINE_CONV(long long, RB_NUM2LL, RB_LL2NUM);
|
347
|
+
RCX_DEFINE_CONV(unsigned long long, RB_NUM2ULL, RB_ULL2NUM);
|
348
|
+
RCX_DEFINE_CONV(double, rb_num2dbl, rb_float_new);
|
349
|
+
#undef RCX_DEFINE_CONV
|
350
|
+
|
351
|
+
#define RCX_DEFINE_CLASS_CONV(CLASS) \
|
352
|
+
inline ClassT<CLASS> FromValue<ClassT<CLASS>>::convert(Value value) { \
|
353
|
+
auto cls = from_Value<Class>(value); \
|
354
|
+
if(cls.is_subclass_of(builtin::CLASS)) { \
|
355
|
+
return detail::unsafe_coerce<ClassT<CLASS>>(cls.as_VALUE()); \
|
356
|
+
} \
|
357
|
+
throw Exception::format( \
|
358
|
+
builtin::ArgumentError, "Expected a subclass of {} but got {}", builtin::CLASS, cls); \
|
359
|
+
}
|
360
|
+
|
361
|
+
RCX_DEFINE_CLASS_CONV(Module);
|
362
|
+
RCX_DEFINE_CLASS_CONV(Class);
|
363
|
+
RCX_DEFINE_CLASS_CONV(Symbol);
|
364
|
+
RCX_DEFINE_CLASS_CONV(Proc);
|
365
|
+
RCX_DEFINE_CLASS_CONV(String);
|
366
|
+
RCX_DEFINE_CLASS_CONV(Array);
|
367
|
+
RCX_DEFINE_CLASS_CONV(Exception);
|
368
|
+
#ifdef RCX_IO_BUFFER
|
369
|
+
RCX_DEFINE_CLASS_CONV(IOBuffer);
|
370
|
+
#endif
|
371
|
+
#undef RCX_DEFINE_CLASS_CONV
|
372
|
+
|
373
|
+
template <std::derived_from<typed_data::WrappedStructBase> T>
|
374
|
+
inline std::reference_wrapper<T> FromValue<T>::convert(Value value) {
|
375
|
+
if constexpr(!std::is_const_v<T>) {
|
376
|
+
detail::protect([&]() noexcept { ::rb_check_frozen(value.as_VALUE()); });
|
377
|
+
}
|
378
|
+
auto const data = detail::protect([&]() noexcept {
|
379
|
+
return ::rb_check_typeddata(value.as_VALUE(), typed_data::DataType<T>::get());
|
380
|
+
});
|
381
|
+
if(!data) {
|
382
|
+
throw std::runtime_error{"Object is not yet initialized"};
|
383
|
+
}
|
384
|
+
return std::ref(*static_cast<T *>(data));
|
385
|
+
}
|
386
|
+
|
387
|
+
template <std::derived_from<typed_data::TwoWayAssociation> T>
|
388
|
+
inline Value IntoValue<T>::convert(T &value) {
|
389
|
+
if(auto const v = value.get_associated_value()) {
|
390
|
+
return *v;
|
391
|
+
}
|
392
|
+
throw std::runtime_error{"This object is not managed by Ruby"};
|
393
|
+
}
|
394
|
+
|
395
|
+
template <std::derived_from<typed_data::WrappedStructBase> T>
|
396
|
+
inline ClassT<T> FromValue<ClassT<T>>::convert(Value value) {
|
397
|
+
auto cls = from_Value<Class>(value);
|
398
|
+
if(cls.is_subclass_of(typed_data::DataType<T>::bound_class())) {
|
399
|
+
return detail::unsafe_coerce<ClassT<T>>(cls.as_VALUE());
|
400
|
+
}
|
401
|
+
throw Exception::format(builtin::ArgumentError, "Expected a subclass of {} but got {}",
|
402
|
+
typed_data::DataType<T>::bound_class(), cls);
|
403
|
+
}
|
404
|
+
}
|
405
|
+
|
406
|
+
namespace typed_data {
|
407
|
+
template <typename T> void dmark(gc::Gc, T *RCX_Nonnull) noexcept {
|
408
|
+
// noop
|
409
|
+
}
|
410
|
+
template <typename T> void dfree(T *RCX_Nonnull p) noexcept {
|
411
|
+
delete p;
|
412
|
+
}
|
413
|
+
template <typename T> size_t dsize(T const *RCX_Nonnull) noexcept {
|
414
|
+
return sizeof(T);
|
415
|
+
}
|
416
|
+
|
417
|
+
template <typename T, typename S>
|
418
|
+
inline ClassT<T> bind_data_type(ClassT<T> klass, ClassT<S> superclass) {
|
419
|
+
if constexpr(std::derived_from<T, typed_data::WrappedStructBase>) {
|
420
|
+
rb_data_type_t const *RCX_Nullable parent = nullptr;
|
421
|
+
if constexpr(!std::derived_from<S, Value>) {
|
422
|
+
parent = DataType<S>::get();
|
423
|
+
|
424
|
+
if(reinterpret_cast<VALUE>(parent->data) != superclass.as_VALUE()) {
|
425
|
+
throw std::runtime_error("superclass has mismatching static type");
|
426
|
+
}
|
427
|
+
}
|
428
|
+
|
429
|
+
DataType<T>::bind(klass, parent);
|
430
|
+
}
|
431
|
+
return klass;
|
432
|
+
}
|
433
|
+
|
434
|
+
template <typename T> inline rb_data_type_t const *RCX_Nonnull DataTypeStorage<T>::get() {
|
435
|
+
if(!data_type_)
|
436
|
+
throw std::runtime_error(
|
437
|
+
std::format("Type '{}' is not yet bound to a Ruby Class", typeid(T).name()));
|
438
|
+
return &*data_type_;
|
439
|
+
}
|
440
|
+
|
441
|
+
template <typename T> inline ClassT<T> DataTypeStorage<T>::bound_class() {
|
442
|
+
return detail::unsafe_coerce<ClassT<T>>(reinterpret_cast<VALUE>(get()->data));
|
443
|
+
}
|
444
|
+
|
445
|
+
template <typename T>
|
446
|
+
inline void DataTypeStorage<T>::bind(
|
447
|
+
ClassT<T> klass, rb_data_type_t const *RCX_Nullable parent) {
|
448
|
+
if(data_type_) {
|
449
|
+
throw std::runtime_error{"This type is already bound to a Ruby Class"};
|
450
|
+
}
|
451
|
+
|
452
|
+
data_type_ = rb_data_type_t{
|
453
|
+
.wrap_struct_name = strdup(klass.name().data()), // let it leek
|
454
|
+
.function = {
|
455
|
+
.dmark =
|
456
|
+
[](void *RCX_Nonnull p) noexcept {
|
457
|
+
using typed_data::dmark;
|
458
|
+
dmark(gc::Gc(gc::Phase::Marking), static_cast<T *>(p));
|
459
|
+
},
|
460
|
+
.dfree =
|
461
|
+
[](void *RCX_Nonnull p) noexcept {
|
462
|
+
using typed_data::dfree;
|
463
|
+
dfree(static_cast<T *>(p));
|
464
|
+
},
|
465
|
+
.dsize =
|
466
|
+
[](void const *RCX_Nonnull p) noexcept {
|
467
|
+
using typed_data::dsize;
|
468
|
+
return dsize(static_cast<T const *>(p));
|
469
|
+
},
|
470
|
+
.dcompact =
|
471
|
+
[](void *RCX_Nonnull p) noexcept {
|
472
|
+
using typed_data::dmark;
|
473
|
+
dmark(gc::Gc(gc::Phase::Compaction), static_cast<T *>(p));
|
474
|
+
},
|
475
|
+
// .reserved is zero-initialized
|
476
|
+
},
|
477
|
+
.parent = parent,
|
478
|
+
.data = reinterpret_cast<void *>(klass.as_VALUE()),
|
479
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
480
|
+
};
|
481
|
+
::rb_gc_register_address(reinterpret_cast<VALUE *>(&data_type_->data));
|
482
|
+
|
483
|
+
::rb_define_alloc_func(klass.as_VALUE(),
|
484
|
+
[](VALUE klass) { return ::rb_data_typed_object_wrap(klass, nullptr, get()); });
|
485
|
+
}
|
486
|
+
|
487
|
+
template <typename T>
|
488
|
+
template <typename... A>
|
489
|
+
requires std::constructible_from<T, A...>
|
490
|
+
inline Value DataTypeStorage<T>::initialize(Value value, A &&...args) {
|
491
|
+
auto data = new T(std::forward<A>(args)...);
|
492
|
+
RTYPEDDATA_DATA(value.as_VALUE()) = data; // Tracked by Ruby GC
|
493
|
+
if constexpr(std::derived_from<T, typed_data::TwoWayAssociation>) {
|
494
|
+
data->associate_value(value);
|
495
|
+
}
|
496
|
+
return value;
|
497
|
+
}
|
498
|
+
|
499
|
+
template <typename T>
|
500
|
+
inline Value DataTypeStorage<T>::initialize_copy(Value value, T const &obj)
|
501
|
+
requires std::copy_constructible<T>
|
502
|
+
{
|
503
|
+
auto data = new T(obj);
|
504
|
+
RTYPEDDATA_DATA(value.as_VALUE()) = data; // Tracked by Ruby GC
|
505
|
+
if constexpr(std::derived_from<T, typed_data::TwoWayAssociation>) {
|
506
|
+
data->associate_value(value);
|
507
|
+
}
|
508
|
+
return value;
|
509
|
+
}
|
510
|
+
|
511
|
+
template <std::derived_from<TwoWayAssociation> T>
|
512
|
+
inline void dmark(gc::Gc gc, T *RCX_Nonnull p) noexcept {
|
513
|
+
p->mark_associated_value(gc);
|
514
|
+
}
|
515
|
+
}
|
516
|
+
|
517
|
+
namespace gc {
|
518
|
+
inline Gc::Gc(Phase phase): phase_(phase) {
|
519
|
+
}
|
520
|
+
|
521
|
+
template <std::derived_from<ValueBase> T>
|
522
|
+
inline void Gc::mark_movable(T &value) const noexcept {
|
523
|
+
switch(phase_) {
|
524
|
+
case Phase::Marking:
|
525
|
+
::rb_gc_mark_movable(value.as_VALUE());
|
526
|
+
break;
|
527
|
+
case Phase::Compaction:
|
528
|
+
value = detail::unsafe_coerce<T>(::rb_gc_location(value.as_VALUE()));
|
529
|
+
break;
|
530
|
+
default:; // unreachable
|
531
|
+
}
|
532
|
+
}
|
533
|
+
inline void Gc::mark_pinned(ValueBase value) const noexcept {
|
534
|
+
switch(phase_) {
|
535
|
+
case Phase::Marking:
|
536
|
+
::rb_gc_mark(value.as_VALUE());
|
537
|
+
break;
|
538
|
+
case Phase::Compaction:
|
539
|
+
// no-op
|
540
|
+
break;
|
541
|
+
default:; // unreachable
|
542
|
+
}
|
543
|
+
}
|
544
|
+
}
|
545
|
+
|
546
|
+
/// Id
|
547
|
+
|
548
|
+
inline Id::Id(ID id): id_(id) {
|
549
|
+
}
|
550
|
+
|
551
|
+
inline ID Id::as_ID() const noexcept {
|
552
|
+
return id_;
|
553
|
+
}
|
554
|
+
|
555
|
+
namespace value {
|
556
|
+
/// ValueBase
|
557
|
+
|
558
|
+
inline constexpr ValueBase::ValueBase(): value_(RUBY_Qnil) {
|
559
|
+
}
|
560
|
+
|
561
|
+
inline constexpr ValueBase::ValueBase(VALUE value): value_(value) {
|
562
|
+
}
|
563
|
+
|
564
|
+
inline constexpr VALUE ValueBase::as_VALUE() const {
|
565
|
+
return value_;
|
566
|
+
}
|
567
|
+
|
568
|
+
inline bool ValueBase::is_nil() const {
|
569
|
+
return RB_NIL_P(value_);
|
570
|
+
}
|
571
|
+
|
572
|
+
inline bool ValueBase::is_frozen() const {
|
573
|
+
return ::rb_obj_frozen_p(as_VALUE());
|
574
|
+
}
|
575
|
+
|
576
|
+
template <typename T> inline bool ValueBase::is_instance_of(ClassT<T> klass) const {
|
577
|
+
return detail::protect([&]() noexcept {
|
578
|
+
Value const result =
|
579
|
+
detail::unsafe_coerce<Value>(::rb_obj_is_instance_of(as_VALUE(), klass.as_VALUE()));
|
580
|
+
return result.test();
|
581
|
+
});
|
582
|
+
}
|
583
|
+
|
584
|
+
template <typename T> inline bool ValueBase::is_kind_of(ClassT<T> klass) const {
|
585
|
+
return detail::protect([&]() noexcept {
|
586
|
+
Value const result =
|
587
|
+
detail::unsafe_coerce<Value>(::rb_obj_is_kind_of(as_VALUE(), klass.as_VALUE()));
|
588
|
+
return result.test();
|
589
|
+
});
|
590
|
+
}
|
591
|
+
|
592
|
+
/// ValueT
|
593
|
+
|
594
|
+
template <typename Derived, std::derived_from<ValueBase> Super, Nilability nilable>
|
595
|
+
ClassT<Derived> ValueT<Derived, Super, nilable>::get_class() const {
|
596
|
+
return detail::unsafe_coerce<ClassT<Derived>>(
|
597
|
+
detail::protect(detail::assume_noexcept(::rb_obj_class), this->as_VALUE()));
|
598
|
+
}
|
599
|
+
|
600
|
+
template <typename Derived, std::derived_from<ValueBase> Super, Nilability nilable>
|
601
|
+
Derived ValueT<Derived, Super, nilable>::freeze() const {
|
602
|
+
return detail::unsafe_coerce<Derived>(
|
603
|
+
detail::protect(detail::assume_noexcept(::rb_obj_freeze), this->as_VALUE()));
|
604
|
+
}
|
605
|
+
|
606
|
+
template <typename Derived, std::derived_from<ValueBase> Super, Nilability nilable>
|
607
|
+
template <concepts::ConvertibleFromValue Self, concepts::ArgSpec... ArgSpec>
|
608
|
+
inline Derived ValueT<Derived, Super, nilable>::define_singleton_method(
|
609
|
+
concepts::Identifier auto &&mid,
|
610
|
+
std::invocable<Self, typename ArgSpec::ResultType...> auto &&function, ArgSpec...) const {
|
611
|
+
auto const callback = detail::method_callback<arg::Self<Self>, ArgSpec...>::alloc(
|
612
|
+
std::forward<decltype(function)>(function));
|
613
|
+
detail::protect([&]() noexcept {
|
614
|
+
auto const singleton = ::rb_singleton_class(this->as_VALUE());
|
615
|
+
rb_define_method_id(
|
616
|
+
singleton, detail::into_ID(std::forward<decltype(mid)>(mid)), callback, -1);
|
617
|
+
});
|
618
|
+
return *static_cast<Derived const *>(this);
|
619
|
+
}
|
620
|
+
|
621
|
+
/// Value
|
622
|
+
|
623
|
+
template <concepts::ConvertibleFromValue R>
|
624
|
+
inline R Value::send(
|
625
|
+
concepts::Identifier auto &&mid, concepts::ConvertibleIntoValue auto &&...args) const {
|
626
|
+
return from_Value<R>(detail::unsafe_coerce<Value>(detail::protect([&]() noexcept {
|
627
|
+
return ::rb_funcall(as_VALUE(), detail::into_ID(std::forward<decltype(mid)>(mid)),
|
628
|
+
sizeof...(args), into_Value(std::forward<decltype(args)>(args)).as_VALUE()...);
|
629
|
+
})));
|
630
|
+
}
|
631
|
+
|
632
|
+
inline bool Value::test() const noexcept {
|
633
|
+
return RB_TEST(as_VALUE());
|
634
|
+
}
|
635
|
+
|
636
|
+
inline String Value::inspect() const {
|
637
|
+
return detail::unsafe_coerce<String>(
|
638
|
+
detail::protect([this]() noexcept { return ::rb_inspect(as_VALUE()); }));
|
639
|
+
}
|
640
|
+
|
641
|
+
inline String Value::to_string() const {
|
642
|
+
return detail::unsafe_coerce<String>(
|
643
|
+
detail::protect([this]() noexcept { return ::rb_obj_as_string(as_VALUE()); }));
|
644
|
+
}
|
645
|
+
|
646
|
+
inline bool Value::instance_variable_defined(concepts::Identifier auto &&name) const {
|
647
|
+
return detail::protect([&]() noexcept {
|
648
|
+
return ::rb_ivar_defined(as_VALUE(), detail::into_ID(std::forward<decltype(name)>(name)));
|
649
|
+
});
|
650
|
+
}
|
651
|
+
|
652
|
+
template <concepts::ConvertibleFromValue T>
|
653
|
+
inline auto Value::instance_variable_get(concepts::Identifier auto &&name) const -> auto {
|
654
|
+
return from_Value<T>(detail::unsafe_coerce<Value>(detail::protect([&]() noexcept {
|
655
|
+
return ::rb_ivar_get(as_VALUE(), detail::into_ID(std::forward<decltype(name)>(name)));
|
656
|
+
})));
|
657
|
+
}
|
658
|
+
|
659
|
+
inline void Value::instance_variable_set(
|
660
|
+
concepts::Identifier auto &&name, concepts::ConvertibleIntoValue auto &&value) const {
|
661
|
+
auto const v = into_Value(std::forward<decltype(value)>(value));
|
662
|
+
return detail::protect([&]() noexcept {
|
663
|
+
::rb_ivar_set(
|
664
|
+
as_VALUE(), detail::into_ID(std::forward<decltype(name)>(name)), v.as_VALUE());
|
665
|
+
});
|
666
|
+
}
|
667
|
+
|
668
|
+
inline Value const Value::qnil = {RUBY_Qnil};
|
669
|
+
inline Value const Value::qtrue = {RUBY_Qtrue};
|
670
|
+
inline Value const Value::qfalse = {RUBY_Qfalse};
|
671
|
+
inline Value const Value::qundef = {RUBY_Qundef};
|
672
|
+
}
|
673
|
+
|
674
|
+
/// Module
|
675
|
+
|
676
|
+
namespace value {
|
677
|
+
inline Module Module::define_module(concepts::Identifier auto &&name) const {
|
678
|
+
return detail::unsafe_coerce<Module>(detail::protect([&]() noexcept {
|
679
|
+
return ::rb_define_module_id_under(
|
680
|
+
as_VALUE(), detail::into_ID(std::forward<decltype(name)>(name)));
|
681
|
+
}));
|
682
|
+
}
|
683
|
+
|
684
|
+
inline String Module::name() const {
|
685
|
+
return detail::unsafe_coerce<String>(::rb_class_path(as_VALUE()));
|
686
|
+
}
|
687
|
+
|
688
|
+
template <typename T, typename S>
|
689
|
+
inline ClassT<T> Module::define_class(
|
690
|
+
concepts::Identifier auto &&name, ClassT<S> superclass) const {
|
691
|
+
ClassT<T> klass = detail::unsafe_coerce<ClassT<T>>(detail::protect([&]() noexcept {
|
692
|
+
return ::rb_define_class_id_under(
|
693
|
+
as_VALUE(), detail::into_ID(std::forward<decltype(name)>(name)), superclass.as_VALUE());
|
694
|
+
}));
|
695
|
+
return typed_data::bind_data_type(klass, superclass);
|
696
|
+
}
|
697
|
+
|
698
|
+
template <typename T>
|
699
|
+
inline ClassT<T> Module::define_class(concepts::Identifier auto &&name) const {
|
700
|
+
return define_class<T>(std::forward<decltype(name)>(name), builtin::Object);
|
701
|
+
}
|
702
|
+
|
703
|
+
template <concepts::ConvertibleFromValue Self, concepts::ArgSpec... ArgSpec>
|
704
|
+
inline Module Module::define_method(concepts::Identifier auto &&mid,
|
705
|
+
std::invocable<Self, typename ArgSpec::ResultType...> auto &&function, ArgSpec...) const {
|
706
|
+
auto const callback = detail::method_callback<arg::Self<Self>, ArgSpec...>::alloc(function);
|
707
|
+
detail::protect([&]() noexcept {
|
708
|
+
rb_define_method_id(
|
709
|
+
as_VALUE(), detail::into_ID(std::forward<decltype(mid)>(mid)), callback, -1);
|
710
|
+
});
|
711
|
+
return *this;
|
712
|
+
}
|
713
|
+
|
714
|
+
inline Module Module::new_module() {
|
715
|
+
return detail::unsafe_coerce<Module>(
|
716
|
+
detail::protect([]() noexcept { return ::rb_module_new(); }));
|
717
|
+
}
|
718
|
+
|
719
|
+
inline bool Module::const_defined(concepts::Identifier auto &&name) const {
|
720
|
+
return detail::protect([&]() noexcept {
|
721
|
+
return ::rb_const_defined(as_VALUE(), detail::into_ID(std::forward<decltype(name)>(name)));
|
722
|
+
});
|
723
|
+
}
|
724
|
+
|
725
|
+
template <concepts::ConvertibleFromValue T>
|
726
|
+
inline T Module::const_get(concepts::Identifier auto &&name) const {
|
727
|
+
return from_Value<T>(detail::unsafe_coerce<Value>(detail::protect([&]() noexcept {
|
728
|
+
return ::rb_const_get(as_VALUE(), detail::into_ID(std::forward<decltype(name)>(name)));
|
729
|
+
})));
|
730
|
+
}
|
731
|
+
|
732
|
+
inline void Module::const_set(
|
733
|
+
concepts::Identifier auto &&name, concepts::ConvertibleIntoValue auto &&value) const {
|
734
|
+
auto const v = into_Value(std::forward<decltype(value)>(value));
|
735
|
+
return detail::protect([&]() noexcept {
|
736
|
+
::rb_const_set(
|
737
|
+
as_VALUE(), detail::into_ID(std::forward<decltype(name)>(name)), v.as_VALUE());
|
738
|
+
});
|
739
|
+
}
|
740
|
+
}
|
741
|
+
|
742
|
+
inline Module convert::FromValue<Module>::convert(Value value) {
|
743
|
+
auto const type = ::rb_type(value.as_VALUE());
|
744
|
+
if(type != RUBY_T_MODULE && type != RUBY_T_CLASS) {
|
745
|
+
throw Exception::format(
|
746
|
+
builtin::TypeError, "Expected a Module but got a {}", value.get_class());
|
747
|
+
}
|
748
|
+
return detail::unsafe_coerce<Module>{value.as_VALUE()};
|
749
|
+
};
|
750
|
+
|
751
|
+
/// Class
|
752
|
+
|
753
|
+
namespace value {
|
754
|
+
template <typename T>
|
755
|
+
inline T ClassT<T>::new_instance(concepts::ConvertibleIntoValue auto &&...args) const
|
756
|
+
requires std::derived_from<T, ValueBase>
|
757
|
+
{
|
758
|
+
std::array<VALUE, sizeof...(args)> vargs{
|
759
|
+
into_Value(std::forward<decltype(args)>(args)).as_VALUE()...};
|
760
|
+
return detail::unsafe_coerce<T>(detail::protect([&]() noexcept {
|
761
|
+
return ::rb_class_new_instance(vargs.size(), vargs.data(), this->as_VALUE());
|
762
|
+
}));
|
763
|
+
}
|
764
|
+
|
765
|
+
template <typename T> inline Value ClassT<T>::allocate() const {
|
766
|
+
return detail::unsafe_coerce<Value>(
|
767
|
+
detail::protect(detail::assume_noexcept(::rb_obj_alloc), this->as_VALUE()));
|
768
|
+
}
|
769
|
+
|
770
|
+
template <typename T>
|
771
|
+
template <typename S>
|
772
|
+
inline bool ClassT<T>::is_subclass_of(ClassT<S> klass) const {
|
773
|
+
Value const result =
|
774
|
+
detail::unsafe_coerce<Value>(::rb_class_inherited_p(this->as_VALUE(), klass.as_VALUE()));
|
775
|
+
return result.test();
|
776
|
+
}
|
777
|
+
|
778
|
+
template <typename T>
|
779
|
+
template <typename S>
|
780
|
+
inline bool ClassT<T>::is_superclass_of(ClassT<S> klass) const {
|
781
|
+
Value const result =
|
782
|
+
detail::unsafe_coerce<Value>(::rb_class_inherited_p(klass.as_VALUE(), this->as_VALUE()));
|
783
|
+
return result.test();
|
784
|
+
}
|
785
|
+
|
786
|
+
template <typename T>
|
787
|
+
template <concepts::ArgSpec... ArgSpec>
|
788
|
+
inline ClassT<T> ClassT<T>::define_method(concepts::Identifier auto &&mid,
|
789
|
+
std::invocable<T &, typename ArgSpec::ResultType...> auto &&function, ArgSpec...) const {
|
790
|
+
auto const callback =
|
791
|
+
detail::method_callback<arg::Self<detail::self_type<T>>, ArgSpec...>::alloc(
|
792
|
+
std::forward<decltype(function)>(function));
|
793
|
+
detail::protect([&]() noexcept {
|
794
|
+
rb_define_method_id(
|
795
|
+
this->as_VALUE(), detail::into_ID(std::forward<decltype(mid)>(mid)), callback, -1);
|
796
|
+
});
|
797
|
+
return *this;
|
798
|
+
}
|
799
|
+
|
800
|
+
template <typename T>
|
801
|
+
template <concepts::ArgSpec... ArgSpec>
|
802
|
+
inline ClassT<T> ClassT<T>::define_method_const(concepts::Identifier auto &&mid,
|
803
|
+
std::invocable<T const &, typename ArgSpec::ResultType...> auto &&function,
|
804
|
+
ArgSpec...) const {
|
805
|
+
auto const callback =
|
806
|
+
detail::method_callback<arg::Self<detail::self_type_const<T>>, ArgSpec...>::alloc(
|
807
|
+
function);
|
808
|
+
detail::protect([&]() noexcept {
|
809
|
+
rb_define_method_id(
|
810
|
+
this->as_VALUE(), detail::into_ID(std::forward<decltype(mid)>(mid)), callback, -1);
|
811
|
+
});
|
812
|
+
return *this;
|
813
|
+
}
|
814
|
+
|
815
|
+
template <typename T>
|
816
|
+
template <concepts::ArgSpec... ArgSpec>
|
817
|
+
requires std::constructible_from<T, typename ArgSpec::ResultType...>
|
818
|
+
inline ClassT<T> ClassT<T>::define_constructor(ArgSpec...) const {
|
819
|
+
auto const callback = detail::method_callback<arg::Self<Value>, ArgSpec...>::alloc(
|
820
|
+
typed_data::DataType<T>::template initialize<typename ArgSpec::ResultType...>);
|
821
|
+
detail::protect([&]() noexcept {
|
822
|
+
using namespace literals;
|
823
|
+
rb_define_method_id(this->as_VALUE(), detail::into_ID("initialize"_id), callback, -1);
|
824
|
+
});
|
825
|
+
return *this;
|
826
|
+
}
|
827
|
+
|
828
|
+
template <typename T>
|
829
|
+
inline ClassT<T> ClassT<T>::define_copy_constructor() const
|
830
|
+
requires std::copy_constructible<T>
|
831
|
+
{
|
832
|
+
auto const callback = detail::method_callback<arg::Self<Value>, arg::Arg<T const &>>::alloc(
|
833
|
+
typed_data::DataType<T>::initialize_copy);
|
834
|
+
detail::protect([&]() noexcept {
|
835
|
+
using namespace literals;
|
836
|
+
rb_define_method_id(this->as_VALUE(), detail::into_ID("initialize_copy"_id), callback, -1);
|
837
|
+
});
|
838
|
+
return *this;
|
839
|
+
}
|
840
|
+
|
841
|
+
template <typename T> inline Class ClassT<T>::new_class() {
|
842
|
+
return new_class(builtin::Object);
|
843
|
+
}
|
844
|
+
template <typename T>
|
845
|
+
template <typename S>
|
846
|
+
inline ClassT<S> ClassT<T>::new_class(ClassT<S> superclass) {
|
847
|
+
return detail::unsafe_coerce<ClassT<S>>(
|
848
|
+
detail::protect(detail::assume_noexcept(::rb_class_new), superclass.as_VALUE()));
|
849
|
+
}
|
850
|
+
}
|
851
|
+
|
852
|
+
inline Class convert::FromValue<Class>::convert(Value value) {
|
853
|
+
if(::rb_type(value.as_VALUE()) != RUBY_T_CLASS) {
|
854
|
+
throw Exception::format(
|
855
|
+
builtin::TypeError, "Expected a Class but got a {}", value.get_class());
|
856
|
+
}
|
857
|
+
return detail::unsafe_coerce<Class>{value.as_VALUE()};
|
858
|
+
};
|
859
|
+
|
860
|
+
/// Symbol
|
861
|
+
|
862
|
+
namespace value {
|
863
|
+
template <size_t N> inline Symbol::Symbol(char const (&s)[N]) noexcept: Symbol({&s[0], N - 1}) {
|
864
|
+
}
|
865
|
+
|
866
|
+
inline Symbol::Symbol(std::string_view sv) noexcept
|
867
|
+
: Symbol(detail::protect([&]() noexcept {
|
868
|
+
return detail::assume_noexcept(::rb_to_symbol)(
|
869
|
+
detail::assume_noexcept(::rb_interned_str)(sv.data(), sv.size()));
|
870
|
+
})) {
|
871
|
+
}
|
872
|
+
|
873
|
+
inline ID Symbol::as_ID() const noexcept {
|
874
|
+
return detail::protect(detail::assume_noexcept(::rb_sym2id), as_VALUE());
|
875
|
+
}
|
876
|
+
}
|
877
|
+
|
878
|
+
inline Symbol convert::FromValue<Symbol>::convert(Value value) {
|
879
|
+
if(::rb_type(value.as_VALUE()) != RUBY_T_SYMBOL) {
|
880
|
+
throw Exception::format(
|
881
|
+
builtin::TypeError, "Expected a Symbol but got a {}", value.get_class());
|
882
|
+
}
|
883
|
+
return detail::unsafe_coerce<Symbol>(value.as_VALUE());
|
884
|
+
}
|
885
|
+
|
886
|
+
/// String
|
887
|
+
|
888
|
+
namespace value {
|
889
|
+
template <concepts::StringLike S> inline String String::intern_from(S &&s) {
|
890
|
+
using CharT = typename std::remove_cvref_t<S>::value_type;
|
891
|
+
using Traits = typename std::remove_cvref_t<S>::traits_type;
|
892
|
+
std::basic_string_view<CharT, Traits> sv(std::forward<S>(s));
|
893
|
+
return detail::unsafe_coerce<String>(detail::protect([&]() noexcept {
|
894
|
+
return (::rb_enc_interned_str)(
|
895
|
+
reinterpret_cast<char const *>(sv.data()), sv.size(), CharTraits<CharT>::encoding());
|
896
|
+
}));
|
897
|
+
}
|
898
|
+
|
899
|
+
template <concepts::CharLike CharT>
|
900
|
+
inline String String::intern_from(CharT const *RCX_Nonnull s) {
|
901
|
+
return intern_from(std::basic_string_view<CharT>(s));
|
902
|
+
}
|
903
|
+
|
904
|
+
template <concepts::StringLike S> inline String String::copy_from(S &&s) {
|
905
|
+
using CharT = typename std::remove_cvref_t<S>::value_type;
|
906
|
+
using Traits = typename std::remove_cvref_t<S>::traits_type;
|
907
|
+
std::basic_string_view<CharT, Traits> sv(std::forward<S>(s));
|
908
|
+
return detail::unsafe_coerce<String>(detail::protect([&]() noexcept {
|
909
|
+
return (::rb_enc_str_new)(
|
910
|
+
reinterpret_cast<char const *>(sv.data()), sv.size(), CharTraits<CharT>::encoding());
|
911
|
+
}));
|
912
|
+
}
|
913
|
+
|
914
|
+
template <concepts::CharLike CharT>
|
915
|
+
inline String String::copy_from(CharT const *RCX_Nonnull s) {
|
916
|
+
return copy_from(std::basic_string_view<CharT>(s));
|
917
|
+
}
|
918
|
+
|
919
|
+
inline size_t String::size() const noexcept {
|
920
|
+
return RSTRING_LEN(as_VALUE());
|
921
|
+
}
|
922
|
+
|
923
|
+
inline char *RCX_Nonnull String::data() const {
|
924
|
+
detail::protect([&]() noexcept { ::rb_check_frozen(as_VALUE()); });
|
925
|
+
return RSTRING_PTR(as_VALUE());
|
926
|
+
}
|
927
|
+
|
928
|
+
inline char const *RCX_Nonnull String::cdata() const noexcept {
|
929
|
+
return RSTRING_PTR(as_VALUE());
|
930
|
+
}
|
931
|
+
|
932
|
+
inline String::operator std::string_view() const noexcept {
|
933
|
+
return {data(), size()};
|
934
|
+
}
|
935
|
+
|
936
|
+
inline String String::lock() const {
|
937
|
+
return detail::unsafe_coerce<String>(
|
938
|
+
detail::protect(detail::assume_noexcept(::rb_str_locktmp), as_VALUE()));
|
939
|
+
}
|
940
|
+
|
941
|
+
inline String String::unlock() const {
|
942
|
+
return detail::unsafe_coerce<String>(
|
943
|
+
detail::protect(detail::assume_noexcept(::rb_str_unlocktmp), as_VALUE()));
|
944
|
+
}
|
945
|
+
}
|
946
|
+
|
947
|
+
inline String convert::FromValue<String>::convert(Value value) {
|
948
|
+
if(::rb_type(value.as_VALUE()) != RUBY_T_STRING) {
|
949
|
+
throw Exception::format(
|
950
|
+
builtin::TypeError, "Expected a String but got a {}", value.get_class());
|
951
|
+
}
|
952
|
+
return detail::unsafe_coerce<String>(value.as_VALUE());
|
953
|
+
}
|
954
|
+
inline std::string_view convert::FromValue<std::string_view>::convert(Value value) {
|
955
|
+
return std::string_view(from_Value<String>(value));
|
956
|
+
}
|
957
|
+
|
958
|
+
/// Proc
|
959
|
+
|
960
|
+
namespace value {
|
961
|
+
inline bool Proc::is_lambda() const {
|
962
|
+
Value const v = detail::unsafe_coerce<Value>(
|
963
|
+
detail::protect([&]() noexcept { return ::rb_proc_lambda_p(as_VALUE()); }));
|
964
|
+
return v.test();
|
965
|
+
}
|
966
|
+
|
967
|
+
inline Value Proc::call(Array args) const {
|
968
|
+
return detail::unsafe_coerce<Value>(
|
969
|
+
detail::protect([&]() noexcept { return ::rb_proc_call(as_VALUE(), args.as_VALUE()); }));
|
970
|
+
}
|
971
|
+
}
|
972
|
+
|
973
|
+
namespace convert {
|
974
|
+
inline Proc convert::FromValue<Proc>::convert(Value value) {
|
975
|
+
if(!rb_obj_is_proc(value.as_VALUE())) {
|
976
|
+
throw Exception::format(
|
977
|
+
builtin::TypeError, "Expected a Proc but got a {}", value.get_class());
|
978
|
+
}
|
979
|
+
return detail::unsafe_coerce<Proc>{value.as_VALUE()};
|
980
|
+
};
|
981
|
+
}
|
982
|
+
|
983
|
+
/// Exception
|
984
|
+
|
985
|
+
namespace value {
|
986
|
+
template <std::derived_from<Exception> E, typename... Args>
|
987
|
+
inline E Exception::format(ClassT<E> cls, std::format_string<Args...> fmt, Args &&...args) {
|
988
|
+
auto const msg = std::vformat(fmt.get(), std::make_format_args(args...));
|
989
|
+
return cls.new_instance(String::intern_from(msg));
|
990
|
+
}
|
991
|
+
}
|
992
|
+
|
993
|
+
namespace convert {
|
994
|
+
inline Exception convert::FromValue<Exception>::convert(Value value) {
|
995
|
+
if(!value.is_kind_of(builtin::Exception)) {
|
996
|
+
throw Exception::format(
|
997
|
+
builtin::TypeError, "Expected an Exception but got a {}", value.get_class());
|
998
|
+
}
|
999
|
+
return detail::unsafe_coerce<Exception>(value.as_VALUE());
|
1000
|
+
}
|
1001
|
+
}
|
1002
|
+
|
1003
|
+
#ifdef RCX_IO_BUFFER
|
1004
|
+
/// IOBuffer
|
1005
|
+
namespace value {
|
1006
|
+
|
1007
|
+
inline IOBuffer IOBuffer::new_internal(size_t size) {
|
1008
|
+
return detail::unsafe_coerce<IOBuffer>(detail::protect([size]() noexcept {
|
1009
|
+
// Let Ruby allocate a buffer
|
1010
|
+
return ::rb_io_buffer_new(nullptr, size, RB_IO_BUFFER_INTERNAL);
|
1011
|
+
}));
|
1012
|
+
}
|
1013
|
+
|
1014
|
+
inline IOBuffer IOBuffer::new_mapped(size_t size) {
|
1015
|
+
return detail::unsafe_coerce<IOBuffer>(detail::protect([size]() noexcept {
|
1016
|
+
// Let Ruby allocate a buffer
|
1017
|
+
return ::rb_io_buffer_new(nullptr, size, RB_IO_BUFFER_MAPPED);
|
1018
|
+
}));
|
1019
|
+
}
|
1020
|
+
|
1021
|
+
template <size_t N> inline IOBuffer IOBuffer::new_external(std::span<std::byte, N> bytes) {
|
1022
|
+
return detail::unsafe_coerce<IOBuffer>(detail::protect([bytes]() noexcept {
|
1023
|
+
return ::rb_io_buffer_new(bytes.data(), bytes.size(), RB_IO_BUFFER_EXTERNAL);
|
1024
|
+
}));
|
1025
|
+
}
|
1026
|
+
|
1027
|
+
template <size_t N>
|
1028
|
+
inline IOBuffer IOBuffer::new_external(std::span<std::byte const, N> bytes) {
|
1029
|
+
return detail::unsafe_coerce<IOBuffer>(detail::protect([bytes]() noexcept {
|
1030
|
+
return ::rb_io_buffer_new(const_cast<std::byte *>(bytes.data()), bytes.size(),
|
1031
|
+
static_cast<rb_io_buffer_flags>(RB_IO_BUFFER_EXTERNAL | RB_IO_BUFFER_READONLY));
|
1032
|
+
}));
|
1033
|
+
}
|
1034
|
+
|
1035
|
+
inline void IOBuffer::free() const {
|
1036
|
+
detail::protect([this]() noexcept { ::rb_io_buffer_free(as_VALUE()); });
|
1037
|
+
}
|
1038
|
+
|
1039
|
+
inline void IOBuffer::resize(size_t size) const {
|
1040
|
+
detail::protect([this, size]() noexcept { ::rb_io_buffer_resize(as_VALUE(), size); });
|
1041
|
+
}
|
1042
|
+
|
1043
|
+
inline std::span<std::byte> IOBuffer::bytes() const {
|
1044
|
+
void *ptr;
|
1045
|
+
size_t size;
|
1046
|
+
detail::protect(
|
1047
|
+
[&]() noexcept { ::rb_io_buffer_get_bytes_for_writing(as_VALUE(), &ptr, &size); });
|
1048
|
+
return {static_cast<std::byte *>(ptr), size};
|
1049
|
+
}
|
1050
|
+
|
1051
|
+
inline std::span<std::byte const> IOBuffer::cbytes() const {
|
1052
|
+
void const *ptr;
|
1053
|
+
size_t size;
|
1054
|
+
detail::protect(
|
1055
|
+
[&]() noexcept { ::rb_io_buffer_get_bytes_for_reading(as_VALUE(), &ptr, &size); });
|
1056
|
+
return {static_cast<std::byte const *>(ptr), size};
|
1057
|
+
}
|
1058
|
+
|
1059
|
+
inline void IOBuffer::lock() const {
|
1060
|
+
detail::protect([this]() noexcept { ::rb_io_buffer_lock(as_VALUE()); });
|
1061
|
+
}
|
1062
|
+
|
1063
|
+
inline void IOBuffer::unlock() const {
|
1064
|
+
detail::protect([this]() noexcept { ::rb_io_buffer_unlock(as_VALUE()); });
|
1065
|
+
}
|
1066
|
+
|
1067
|
+
inline bool IOBuffer::try_lock() const {
|
1068
|
+
try {
|
1069
|
+
lock();
|
1070
|
+
return true;
|
1071
|
+
} catch(Exception const &) {
|
1072
|
+
return false;
|
1073
|
+
}
|
1074
|
+
}
|
1075
|
+
}
|
1076
|
+
|
1077
|
+
namespace convert {
|
1078
|
+
inline IOBuffer FromValue<IOBuffer>::convert(Value value) {
|
1079
|
+
if(!value.is_kind_of(builtin::IOBuffer)) {
|
1080
|
+
throw Exception::format(
|
1081
|
+
builtin::TypeError, "Expected an IO::Buffer but got a {}", value.get_class());
|
1082
|
+
}
|
1083
|
+
return detail::unsafe_coerce<IOBuffer>(value.as_VALUE());
|
1084
|
+
}
|
1085
|
+
}
|
1086
|
+
#endif
|
1087
|
+
|
1088
|
+
// Array
|
1089
|
+
|
1090
|
+
namespace value {
|
1091
|
+
inline size_t Array::size() const noexcept {
|
1092
|
+
return rb_array_len(as_VALUE());
|
1093
|
+
}
|
1094
|
+
|
1095
|
+
template <concepts::ConvertibleFromValue T> inline decltype(auto) Array::at(size_t i) const {
|
1096
|
+
return from_Value<T>((*this)[i]);
|
1097
|
+
}
|
1098
|
+
|
1099
|
+
inline Value Array::operator[](size_t i) const {
|
1100
|
+
VALUE const index = RB_SIZE2NUM(i);
|
1101
|
+
return detail::unsafe_coerce<Value>(
|
1102
|
+
detail::protect([&]() noexcept { return ::rb_ary_aref(1, &index, as_VALUE()); }));
|
1103
|
+
;
|
1104
|
+
}
|
1105
|
+
|
1106
|
+
template <std::ranges::contiguous_range R>
|
1107
|
+
#ifdef HAVE_STD_IS_LAYOUT_COMPATIBLE
|
1108
|
+
requires std::is_layout_compatible_v<std::ranges::range_value_t<R>, ValueBase>
|
1109
|
+
#else
|
1110
|
+
requires(std::derived_from<std::ranges::range_value_t<R>, ValueBase> &&
|
1111
|
+
sizeof(std::ranges::range_value_t<R>) == sizeof(ValueBase))
|
1112
|
+
#endif
|
1113
|
+
inline Array Array::new_from(R const &elements) {
|
1114
|
+
// contiguous_range<T> has a layout combatible to VALUE[]
|
1115
|
+
return detail::unsafe_coerce<Array>(detail::protect([&]() noexcept {
|
1116
|
+
return ::rb_ary_new_from_values(
|
1117
|
+
elements.size(), reinterpret_cast<VALUE const *>(elements.data()));
|
1118
|
+
}));
|
1119
|
+
};
|
1120
|
+
|
1121
|
+
inline Array Array::new_from(std::initializer_list<ValueBase> elements) {
|
1122
|
+
return detail::unsafe_coerce<Array>(detail::protect([&]() noexcept {
|
1123
|
+
return ::rb_ary_new_from_values(
|
1124
|
+
elements.size(), reinterpret_cast<VALUE const *>(elements.begin()));
|
1125
|
+
}));
|
1126
|
+
}
|
1127
|
+
|
1128
|
+
template <std::derived_from<ValueBase>... T>
|
1129
|
+
inline Array Array::new_from(std::tuple<T...> const &elements) {
|
1130
|
+
return detail::unsafe_coerce<Array>(detail::protect([&]() noexcept {
|
1131
|
+
return std::apply(
|
1132
|
+
[](auto... v) { return ::rb_ary_new_from_args(sizeof...(v), v.as_VALUE()...); },
|
1133
|
+
elements);
|
1134
|
+
}));
|
1135
|
+
}
|
1136
|
+
|
1137
|
+
inline Array Array::new_array() {
|
1138
|
+
return detail::unsafe_coerce<Array>(
|
1139
|
+
detail::protect([]() noexcept { return ::rb_ary_new(); }));
|
1140
|
+
}
|
1141
|
+
|
1142
|
+
inline Array Array::new_array(long capacity) {
|
1143
|
+
return detail::unsafe_coerce<Array>(
|
1144
|
+
detail::protect([capacity]() noexcept { return ::rb_ary_new_capa(capacity); }));
|
1145
|
+
}
|
1146
|
+
|
1147
|
+
template <concepts::ConvertibleIntoValue T> Array Array::push_back(T value) const {
|
1148
|
+
auto const v = into_Value<T>(value);
|
1149
|
+
detail::protect([v, this]() noexcept { ::rb_ary_push(as_VALUE(), v.as_VALUE()); });
|
1150
|
+
return *this;
|
1151
|
+
}
|
1152
|
+
|
1153
|
+
template <concepts::ConvertibleFromValue T> inline T Array::pop_back() const {
|
1154
|
+
return from_Value<T>(detail::unsafe_coerce<Value>(
|
1155
|
+
detail::protect([this]() noexcept { return ::rb_ary_pop(as_VALUE()); })));
|
1156
|
+
}
|
1157
|
+
|
1158
|
+
template <concepts::ConvertibleIntoValue T> Array Array::push_front(T value) const {
|
1159
|
+
auto const v = into_Value<T>(value);
|
1160
|
+
detail::protect([v, this]() noexcept { ::rb_ary_unshift(as_VALUE(), v.as_VALUE()); });
|
1161
|
+
return *this;
|
1162
|
+
}
|
1163
|
+
|
1164
|
+
template <concepts::ConvertibleFromValue T> inline T Array::pop_front() const {
|
1165
|
+
return from_Value<T>(detail::unsafe_coerce<Value>(
|
1166
|
+
detail::protect([this]() noexcept { return ::rb_ary_shift(as_VALUE()); })));
|
1167
|
+
}
|
1168
|
+
}
|
1169
|
+
|
1170
|
+
inline Array convert::FromValue<Array>::convert(Value value) {
|
1171
|
+
if(::rb_type(value.as_VALUE()) != RUBY_T_ARRAY) {
|
1172
|
+
throw Exception::format(
|
1173
|
+
builtin::TypeError, "Expected an Array but got a {}", value.get_class());
|
1174
|
+
}
|
1175
|
+
return detail::unsafe_coerce<Array>(value.as_VALUE());
|
1176
|
+
}
|
1177
|
+
|
1178
|
+
template <concepts::ConvertibleFromValue T>
|
1179
|
+
decltype(auto) FromValue<std::optional<T>>::convert(Value v) {
|
1180
|
+
return v.is_nil() ? std::nullopt : from_Value<T>(v);
|
1181
|
+
}
|
1182
|
+
|
1183
|
+
template <concepts::ConvertibleIntoValue T>
|
1184
|
+
Value IntoValue<std::optional<T>>::convert(std::optional<T> value) {
|
1185
|
+
return value ? Value::qnil : into_Value(*value);
|
1186
|
+
}
|
1187
|
+
|
1188
|
+
template <concepts::ConvertibleFromValue... T>
|
1189
|
+
inline decltype(auto) convert::FromValue<std::tuple<T...>>::convert(Value value) {
|
1190
|
+
auto array = from_Value<Array>(value);
|
1191
|
+
if(array.size() != sizeof...(T)) {
|
1192
|
+
throw Exception::format(
|
1193
|
+
builtin::ArgumentError, "Array of length {} is expected", sizeof...(T));
|
1194
|
+
}
|
1195
|
+
return [array]<size_t... I>(std::index_sequence<I...>) {
|
1196
|
+
return std::make_tuple(array.at<T>(I)...);
|
1197
|
+
}(std::make_index_sequence<sizeof...(T)>());
|
1198
|
+
}
|
1199
|
+
|
1200
|
+
template <concepts::ConvertibleIntoValue... T>
|
1201
|
+
Value IntoValue<std::tuple<T...>>::convert(std::tuple<T...> value) {
|
1202
|
+
return [&value]<size_t... I>(std::index_sequence<I...>) {
|
1203
|
+
return Array::new_from(
|
1204
|
+
std::tuple{into_Value<decltype(std::get<I>(value))>(std::get<I>(value))...});
|
1205
|
+
}(std::make_index_sequence<sizeof...(T)>());
|
1206
|
+
};
|
1207
|
+
|
1208
|
+
// Misc
|
1209
|
+
|
1210
|
+
namespace literals {
|
1211
|
+
template <detail::cxstring s> String operator""_str() {
|
1212
|
+
return String::copy_from(s);
|
1213
|
+
}
|
1214
|
+
|
1215
|
+
template <detail::u8cxstring s> String operator""_str() {
|
1216
|
+
return String::copy_from(s);
|
1217
|
+
}
|
1218
|
+
|
1219
|
+
template <detail::cxstring s> String operator""_fstr() {
|
1220
|
+
return String::intern_from(s);
|
1221
|
+
}
|
1222
|
+
|
1223
|
+
template <detail::u8cxstring s> String operator""_fstr() {
|
1224
|
+
return String::intern_from(s);
|
1225
|
+
}
|
1226
|
+
|
1227
|
+
template <detail::cxstring s> Symbol operator""_sym() {
|
1228
|
+
return detail::unsafe_coerce<Symbol>(
|
1229
|
+
detail::protect([&]() noexcept { return ::rb_id2sym(operator""_id < s>().as_ID()); }));
|
1230
|
+
}
|
1231
|
+
|
1232
|
+
template <detail::u8cxstring s> Symbol operator""_sym() {
|
1233
|
+
return detail::unsafe_coerce<Symbol>(
|
1234
|
+
detail::protect([&]() noexcept { return ::rb_id2sym(operator""_id < s>().as_VALUE()); }));
|
1235
|
+
}
|
1236
|
+
|
1237
|
+
template <detail::cxstring s> Id operator""_id() {
|
1238
|
+
static Id const id{
|
1239
|
+
detail::protect([&]() noexcept { return ::rb_intern2(s.data(), s.size()); })};
|
1240
|
+
return id;
|
1241
|
+
}
|
1242
|
+
|
1243
|
+
template <detail::u8cxstring s> Id operator""_id() {
|
1244
|
+
static Id const id{detail::protect(
|
1245
|
+
[&]() noexcept { return ::rb_intern_str(operator""_fstr < s>().as_VALUE()); })};
|
1246
|
+
return id;
|
1247
|
+
}
|
1248
|
+
|
1249
|
+
}
|
1250
|
+
|
1251
|
+
// Leak
|
1252
|
+
|
1253
|
+
template <std::derived_from<ValueBase> T>
|
1254
|
+
inline Leak<T>::Leak() noexcept: init_(false), raw_value_(RUBY_Qnil) {
|
1255
|
+
}
|
1256
|
+
|
1257
|
+
template <std::derived_from<ValueBase> T>
|
1258
|
+
inline Leak<T>::Leak(T value) noexcept(noexcept(T(value))): Leak() {
|
1259
|
+
set(value);
|
1260
|
+
}
|
1261
|
+
|
1262
|
+
template <std::derived_from<ValueBase> T>
|
1263
|
+
inline Leak<T> &Leak<T>::operator=(T value) noexcept(noexcept(T(value))) {
|
1264
|
+
set(value);
|
1265
|
+
return *this;
|
1266
|
+
}
|
1267
|
+
|
1268
|
+
template <std::derived_from<ValueBase> T> inline T Leak<T>::get() const {
|
1269
|
+
if(!init_) {
|
1270
|
+
throw std::runtime_error{"Leak is not initialized yet"};
|
1271
|
+
}
|
1272
|
+
return value_;
|
1273
|
+
}
|
1274
|
+
|
1275
|
+
template <std::derived_from<ValueBase> T>
|
1276
|
+
inline void Leak<T>::set(T value) noexcept(noexcept(T(value))) {
|
1277
|
+
if(init_) {
|
1278
|
+
value_.~T();
|
1279
|
+
} else {
|
1280
|
+
::rb_gc_register_address(&raw_value_);
|
1281
|
+
}
|
1282
|
+
new(&value_) T(value);
|
1283
|
+
init_ = true;
|
1284
|
+
}
|
1285
|
+
|
1286
|
+
template <std::derived_from<ValueBase> T> inline T Leak<T>::operator*() const {
|
1287
|
+
return get();
|
1288
|
+
}
|
1289
|
+
|
1290
|
+
template <std::derived_from<ValueBase> T>
|
1291
|
+
inline T const *RCX_Nonnull Leak<T>::operator->() const {
|
1292
|
+
if(!init_) {
|
1293
|
+
throw std::runtime_error{"Leak is not initialized yet"};
|
1294
|
+
}
|
1295
|
+
return &value_;
|
1296
|
+
}
|
1297
|
+
|
1298
|
+
template <std::derived_from<ValueBase> T> inline void Leak<T>::clear() noexcept {
|
1299
|
+
if(init_) {
|
1300
|
+
value_.~T();
|
1301
|
+
raw_value_ = RUBY_Qnil;
|
1302
|
+
::rb_gc_unregister_address(&raw_value_);
|
1303
|
+
init_ = false;
|
1304
|
+
}
|
1305
|
+
}
|
1306
|
+
|
1307
|
+
// Ruby
|
1308
|
+
|
1309
|
+
inline Module Ruby::define_module(concepts::Identifier auto &&name) {
|
1310
|
+
return builtin::Object.define_module(std::forward<decltype(name)>(name));
|
1311
|
+
}
|
1312
|
+
|
1313
|
+
template <typename T, typename S>
|
1314
|
+
inline ClassT<T> Ruby::define_class(concepts::Identifier auto &&name, ClassT<S> superclass) {
|
1315
|
+
return builtin::Object.define_class<T>(std::forward<decltype(name)>(name), superclass);
|
1316
|
+
}
|
1317
|
+
|
1318
|
+
template <typename T> inline ClassT<T> Ruby::define_class(concepts::Identifier auto &&name) {
|
1319
|
+
return define_class<T>(std::forward<decltype(name)>(name), builtin::Object);
|
1320
|
+
}
|
1321
|
+
|
1322
|
+
namespace detail {
|
1323
|
+
inline std::string demangle_type_info(std::type_info const &ti) {
|
1324
|
+
if constexpr(have_abi_cxa_demangle) {
|
1325
|
+
std::unique_ptr<char, decltype(&free)> const name = {
|
1326
|
+
abi::__cxa_demangle(ti.name(), nullptr, 0, nullptr),
|
1327
|
+
free,
|
1328
|
+
};
|
1329
|
+
return name.get();
|
1330
|
+
}
|
1331
|
+
return {};
|
1332
|
+
}
|
1333
|
+
|
1334
|
+
inline Value make_ruby_exception(
|
1335
|
+
std::exception const *RCX_Nullable exc, std::type_info const *RCX_Nullable ti) {
|
1336
|
+
std::string name, msg;
|
1337
|
+
if(ti)
|
1338
|
+
name = demangle_type_info(*ti);
|
1339
|
+
if(exc)
|
1340
|
+
msg = exc->what();
|
1341
|
+
return builtin::RuntimeError.new_instance(String::copy_from(
|
1342
|
+
std::format("{}: {}", name.empty() ? std::string{"unknown"} : name, msg)));
|
1343
|
+
}
|
1344
|
+
|
1345
|
+
inline auto cxx_protect(std::invocable<> auto const &functor) noexcept
|
1346
|
+
-> std::invoke_result_t<decltype(functor)> {
|
1347
|
+
try {
|
1348
|
+
return functor();
|
1349
|
+
} catch(Jump const &jump) {
|
1350
|
+
::rb_jump_tag(jump.state);
|
1351
|
+
} catch(Exception const &exc) {
|
1352
|
+
::rb_exc_raise(exc.as_VALUE());
|
1353
|
+
} catch(std::exception const &exc) {
|
1354
|
+
::rb_exc_raise(make_ruby_exception(&exc, &typeid(exc)).as_VALUE());
|
1355
|
+
} catch(...) {
|
1356
|
+
if constexpr(have_abi_cxa_current_exception_type) {
|
1357
|
+
::rb_exc_raise(
|
1358
|
+
make_ruby_exception(nullptr, abi::__cxa_current_exception_type()).as_VALUE());
|
1359
|
+
} else {
|
1360
|
+
::rb_exc_raise(make_ruby_exception(nullptr, nullptr).as_VALUE());
|
1361
|
+
}
|
1362
|
+
}
|
1363
|
+
}
|
1364
|
+
}
|
1365
|
+
}
|