rcx 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,1258 @@
|
|
1
|
+
// SPDX-License-Identifier: BSL-1.0
|
2
|
+
// SPDX-FileCopyrightText: Copyright 2024-2025 Kasumi Hanazuki <kasumi@rollingapple.net>
|
3
|
+
#pragma once
|
4
|
+
|
5
|
+
#include <algorithm>
|
6
|
+
#include <cassert>
|
7
|
+
#include <concepts>
|
8
|
+
#include <format>
|
9
|
+
#include <functional>
|
10
|
+
#include <initializer_list>
|
11
|
+
#include <optional>
|
12
|
+
#include <span>
|
13
|
+
#include <stdexcept>
|
14
|
+
#include <string>
|
15
|
+
#include <string_view>
|
16
|
+
#include <type_traits>
|
17
|
+
#include <vector>
|
18
|
+
|
19
|
+
#include <ruby.h>
|
20
|
+
#include <ruby/encoding.h>
|
21
|
+
#include <ruby/io/buffer.h>
|
22
|
+
|
23
|
+
#define rcx_assert(expr) assert((expr))
|
24
|
+
#define rcx_delete(reason) delete
|
25
|
+
|
26
|
+
#ifdef HAVE_FEATURE_NULLABILITY
|
27
|
+
#define RCX_Nullable _Nullable
|
28
|
+
#define RCX_Nonnull _Nonnull
|
29
|
+
#else
|
30
|
+
#define RCX_Nullable
|
31
|
+
#define RCX_Nonnull
|
32
|
+
#endif
|
33
|
+
|
34
|
+
#if RUBY_IO_BUFFER_VERSION == 2
|
35
|
+
#define RCX_IO_BUFFER
|
36
|
+
#endif
|
37
|
+
|
38
|
+
/// RCX
|
39
|
+
///
|
40
|
+
namespace rcx {
|
41
|
+
class Ruby;
|
42
|
+
class Id;
|
43
|
+
|
44
|
+
/// Value wrappers.
|
45
|
+
///
|
46
|
+
namespace value {
|
47
|
+
enum Nilability : bool;
|
48
|
+
|
49
|
+
class ValueBase;
|
50
|
+
template <typename Derived, std::derived_from<ValueBase> Super, Nilability> class ValueT;
|
51
|
+
class Value;
|
52
|
+
class Module;
|
53
|
+
template <typename T = Value> class ClassT;
|
54
|
+
using Class = ClassT<Value>;
|
55
|
+
class Symbol;
|
56
|
+
class Proc;
|
57
|
+
class String;
|
58
|
+
class Array;
|
59
|
+
class Exception;
|
60
|
+
#ifdef RCX_IO_BUFFER
|
61
|
+
class IOBuffer;
|
62
|
+
#endif
|
63
|
+
}
|
64
|
+
using namespace value;
|
65
|
+
|
66
|
+
/// Concepts
|
67
|
+
///
|
68
|
+
namespace concepts {
|
69
|
+
template <typename T>
|
70
|
+
concept StringLike = requires {
|
71
|
+
typename std::remove_cvref_t<T>::value_type;
|
72
|
+
typename std::remove_cvref_t<T>::traits_type;
|
73
|
+
typename std::basic_string_view<typename std::remove_cvref_t<T>::value_type,
|
74
|
+
typename std::remove_cvref_t<T>::traits_type>;
|
75
|
+
};
|
76
|
+
|
77
|
+
/// Specifies the types that can be used as Ruby identifiers.
|
78
|
+
///
|
79
|
+
/// This includes \ref rcx::Id, \ref rcx::value::Symbol and C++ strings.
|
80
|
+
template <typename T>
|
81
|
+
concept Identifier = requires(T id) {
|
82
|
+
{ id.as_ID() } noexcept -> std::same_as<ID>;
|
83
|
+
} || std::is_nothrow_constructible_v<Symbol, T>;
|
84
|
+
}
|
85
|
+
|
86
|
+
/// Implementation details.
|
87
|
+
///
|
88
|
+
/// @internal
|
89
|
+
namespace detail {
|
90
|
+
template <typename T> struct unsafe_coerce {
|
91
|
+
VALUE value;
|
92
|
+
|
93
|
+
constexpr unsafe_coerce(VALUE value): value{value} {
|
94
|
+
}
|
95
|
+
|
96
|
+
template <std::derived_from<T> U>
|
97
|
+
constexpr unsafe_coerce(unsafe_coerce<U> other): value{other.value} {
|
98
|
+
}
|
99
|
+
};
|
100
|
+
|
101
|
+
template <typename T> inline constexpr bool always_false_v = false;
|
102
|
+
|
103
|
+
// Duplicated definition for cxstring and u8cxstring instead of parameterize char type, because
|
104
|
+
// clang-18 does not support template parameter deduction for type aliases.
|
105
|
+
|
106
|
+
/// Represents a compile-time binary string.
|
107
|
+
///
|
108
|
+
template <size_t N> struct cxstring {
|
109
|
+
using value_type = char;
|
110
|
+
using traits_type = std::char_traits<value_type>;
|
111
|
+
|
112
|
+
std::array<value_type, N> data_;
|
113
|
+
|
114
|
+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays)
|
115
|
+
consteval cxstring(value_type const (&str)[N]) {
|
116
|
+
std::copy_n(str, N, data_.data());
|
117
|
+
}
|
118
|
+
|
119
|
+
constexpr value_type const *RCX_Nonnull data() const {
|
120
|
+
return data_.data();
|
121
|
+
}
|
122
|
+
|
123
|
+
constexpr size_t size() const {
|
124
|
+
return N - 1;
|
125
|
+
}
|
126
|
+
|
127
|
+
constexpr operator std::basic_string_view<value_type>() const {
|
128
|
+
return {data(), size()};
|
129
|
+
}
|
130
|
+
};
|
131
|
+
|
132
|
+
/// Represents a compile-time UTF-8 string.
|
133
|
+
///
|
134
|
+
template <size_t N> struct u8cxstring {
|
135
|
+
using value_type = char8_t;
|
136
|
+
using traits_type = std::char_traits<value_type>;
|
137
|
+
|
138
|
+
std::array<value_type, N> data_;
|
139
|
+
|
140
|
+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays)
|
141
|
+
consteval u8cxstring(value_type const (&str)[N]) {
|
142
|
+
std::copy_n(str, N, data_.data());
|
143
|
+
}
|
144
|
+
|
145
|
+
constexpr value_type const *RCX_Nonnull data() const {
|
146
|
+
return data_.data();
|
147
|
+
}
|
148
|
+
|
149
|
+
constexpr size_t size() const {
|
150
|
+
return N - 1;
|
151
|
+
}
|
152
|
+
|
153
|
+
constexpr operator std::basic_string_view<value_type>() const {
|
154
|
+
return {data(), size()};
|
155
|
+
}
|
156
|
+
};
|
157
|
+
|
158
|
+
using RbFunc = Value(std::span<Value> args, Value self);
|
159
|
+
using NativeRbFunc = VALUE(int argc, VALUE *RCX_Nonnull argv, VALUE self);
|
160
|
+
NativeRbFunc *RCX_Nonnull alloc_callback(std::function<RbFunc> f);
|
161
|
+
|
162
|
+
struct Jump {
|
163
|
+
int state;
|
164
|
+
};
|
165
|
+
|
166
|
+
template <std::invocable<> F>
|
167
|
+
auto protect(F functor) -> auto
|
168
|
+
requires(noexcept(functor()));
|
169
|
+
template <typename A, typename R>
|
170
|
+
requires(std::is_integral_v<A> && sizeof(A) == sizeof(VALUE) && std::is_integral_v<R> &&
|
171
|
+
sizeof(R) == sizeof(VALUE))
|
172
|
+
VALUE protect(VALUE (*RCX_Nonnull func)(A) noexcept, A arg);
|
173
|
+
|
174
|
+
template <typename T> struct wrap_ref {
|
175
|
+
using type = T;
|
176
|
+
};
|
177
|
+
template <typename U> struct wrap_ref<U &> {
|
178
|
+
using type = std::reference_wrapper<U>;
|
179
|
+
};
|
180
|
+
template <typename T> using wrap_ref_t = wrap_ref<T>::type;
|
181
|
+
}
|
182
|
+
|
183
|
+
/// Conversion between C++ and Ruby values.
|
184
|
+
///
|
185
|
+
namespace convert {
|
186
|
+
/// Converts a C++ value into a Ruby value.
|
187
|
+
///
|
188
|
+
/// @tparam T The type of the C++ value.
|
189
|
+
/// @param value The C++ value to be converted.
|
190
|
+
/// @return The converted Ruby value.
|
191
|
+
template <typename T> Value into_Value(T value);
|
192
|
+
/// Converts a Ruby value into a C++ value.
|
193
|
+
///
|
194
|
+
/// @tparam T The type the C++ value.
|
195
|
+
/// @param value The Ruby value to be converted.
|
196
|
+
/// @return The converted C++ value.
|
197
|
+
template <typename T> auto from_Value(Value value) -> auto;
|
198
|
+
|
199
|
+
template <typename T> struct FromValue {
|
200
|
+
static_assert(detail::always_false_v<T>, "conversion from Value not defined");
|
201
|
+
};
|
202
|
+
template <typename T> struct IntoValue {
|
203
|
+
static_assert(detail::always_false_v<T>, "conversion into Value not defined");
|
204
|
+
};
|
205
|
+
|
206
|
+
#define RCX_DECLARE_CONV(TYPE) \
|
207
|
+
template <> struct FromValue<TYPE> { \
|
208
|
+
TYPE convert(Value value); \
|
209
|
+
}; \
|
210
|
+
template <> struct IntoValue<TYPE> { \
|
211
|
+
Value convert(TYPE value); \
|
212
|
+
};
|
213
|
+
|
214
|
+
RCX_DECLARE_CONV(bool);
|
215
|
+
RCX_DECLARE_CONV(signed char);
|
216
|
+
RCX_DECLARE_CONV(unsigned char);
|
217
|
+
RCX_DECLARE_CONV(short);
|
218
|
+
RCX_DECLARE_CONV(unsigned short);
|
219
|
+
RCX_DECLARE_CONV(int);
|
220
|
+
RCX_DECLARE_CONV(unsigned int);
|
221
|
+
RCX_DECLARE_CONV(long);
|
222
|
+
RCX_DECLARE_CONV(unsigned long);
|
223
|
+
RCX_DECLARE_CONV(long long);
|
224
|
+
RCX_DECLARE_CONV(unsigned long long);
|
225
|
+
RCX_DECLARE_CONV(double);
|
226
|
+
|
227
|
+
#undef RCX_DECLARE_CONV
|
228
|
+
template <> struct FromValue<std::string_view> {
|
229
|
+
std::string_view convert(Value value);
|
230
|
+
};
|
231
|
+
|
232
|
+
template <> struct FromValue<Module> {
|
233
|
+
Module convert(Value value);
|
234
|
+
};
|
235
|
+
|
236
|
+
template <> struct FromValue<Class> {
|
237
|
+
Class convert(Value value);
|
238
|
+
};
|
239
|
+
template <> struct FromValue<Symbol> {
|
240
|
+
Symbol convert(Value value);
|
241
|
+
};
|
242
|
+
template <> struct FromValue<Proc> {
|
243
|
+
Proc convert(Value value);
|
244
|
+
};
|
245
|
+
template <> struct FromValue<String> {
|
246
|
+
String convert(Value value);
|
247
|
+
};
|
248
|
+
template <> struct FromValue<Array> {
|
249
|
+
Array convert(Value value);
|
250
|
+
};
|
251
|
+
template <> struct FromValue<Exception> {
|
252
|
+
Exception convert(Value value);
|
253
|
+
};
|
254
|
+
#ifdef RCX_IO_BUFFER
|
255
|
+
template <> struct FromValue<IOBuffer> {
|
256
|
+
IOBuffer convert(Value value);
|
257
|
+
};
|
258
|
+
#endif
|
259
|
+
|
260
|
+
#define RCX_DECLARE_CLASS_CONV(CLS) \
|
261
|
+
template <> struct FromValue<ClassT<CLS>> { \
|
262
|
+
ClassT<CLS> convert(Value value); \
|
263
|
+
};
|
264
|
+
|
265
|
+
RCX_DECLARE_CLASS_CONV(Module);
|
266
|
+
RCX_DECLARE_CLASS_CONV(Class);
|
267
|
+
RCX_DECLARE_CLASS_CONV(Symbol);
|
268
|
+
RCX_DECLARE_CLASS_CONV(Proc);
|
269
|
+
RCX_DECLARE_CLASS_CONV(String);
|
270
|
+
RCX_DECLARE_CLASS_CONV(Array);
|
271
|
+
RCX_DECLARE_CLASS_CONV(Exception);
|
272
|
+
#ifdef RCX_IO_BUFFER
|
273
|
+
RCX_DECLARE_CLASS_CONV(IOBuffer);
|
274
|
+
#endif
|
275
|
+
|
276
|
+
#undef RCX_DECLARE_CLASS_CONV
|
277
|
+
}
|
278
|
+
using namespace convert;
|
279
|
+
|
280
|
+
namespace concepts {
|
281
|
+
/// Specifies the types that can be converted from Ruby values.
|
282
|
+
///
|
283
|
+
template <typename T>
|
284
|
+
concept ConvertibleFromValue = requires(Value v) { from_Value<T>(v); };
|
285
|
+
|
286
|
+
/// Specifies the types that can be converted into Ruby values.
|
287
|
+
///
|
288
|
+
template <typename T>
|
289
|
+
concept ConvertibleIntoValue = requires(T v) {
|
290
|
+
{ into_Value<T>(v) } -> std::same_as<Value>;
|
291
|
+
};
|
292
|
+
}
|
293
|
+
|
294
|
+
namespace convert {
|
295
|
+
template <concepts::ConvertibleFromValue T> struct FromValue<std::optional<T>> {
|
296
|
+
decltype(auto) convert(Value v);
|
297
|
+
};
|
298
|
+
|
299
|
+
template <concepts::ConvertibleIntoValue T> struct IntoValue<std::optional<T>> {
|
300
|
+
Value convert(std::optional<T> value);
|
301
|
+
};
|
302
|
+
|
303
|
+
template <concepts::ConvertibleFromValue... T> struct FromValue<std::tuple<T...>> {
|
304
|
+
decltype(auto) convert(Value value);
|
305
|
+
};
|
306
|
+
|
307
|
+
template <concepts::ConvertibleIntoValue... T> struct IntoValue<std::tuple<T...>> {
|
308
|
+
Value convert(std::tuple<T...> value);
|
309
|
+
};
|
310
|
+
}
|
311
|
+
|
312
|
+
/// Argument parsing.
|
313
|
+
///
|
314
|
+
namespace arg {
|
315
|
+
template <concepts::ConvertibleFromValue T = Value> struct Self {
|
316
|
+
using ResultType = detail::wrap_ref_t<T>;
|
317
|
+
static ResultType parse(Ruby &, Value self, std::span<Value> &args);
|
318
|
+
};
|
319
|
+
|
320
|
+
template <concepts::ConvertibleFromValue T = Value, detail::cxstring name = ""> struct Arg {
|
321
|
+
using ResultType = detail::wrap_ref_t<T>;
|
322
|
+
static ResultType parse(Ruby &, Value self, std::span<Value> &args);
|
323
|
+
};
|
324
|
+
|
325
|
+
template <concepts::ConvertibleFromValue T = Value, detail::cxstring name = ""> struct ArgOpt {
|
326
|
+
using ResultType = std::optional<detail::wrap_ref_t<T>>;
|
327
|
+
static ResultType parse(Ruby &, Value self, std::span<Value> &args);
|
328
|
+
};
|
329
|
+
|
330
|
+
struct ArgSplat {
|
331
|
+
using ResultType = Array;
|
332
|
+
static ResultType parse(Ruby &, Value self, std::span<Value> &args);
|
333
|
+
};
|
334
|
+
|
335
|
+
struct Block {
|
336
|
+
using ResultType = Proc;
|
337
|
+
static ResultType parse(Ruby &, Value self, std::span<Value> &args);
|
338
|
+
};
|
339
|
+
|
340
|
+
struct BlockOpt {
|
341
|
+
using ResultType = std::optional<Proc>;
|
342
|
+
static ResultType parse(Ruby &, Value self, std::span<Value> &args);
|
343
|
+
};
|
344
|
+
|
345
|
+
/// The method receiver.
|
346
|
+
///
|
347
|
+
/// The method accepts the method receiver and converts it into type T.
|
348
|
+
template <concepts::ConvertibleFromValue T = Value> constexpr inline Self<T> self;
|
349
|
+
/// A positional argument.
|
350
|
+
///
|
351
|
+
template <concepts::ConvertibleFromValue T = Value, detail::cxstring name = "">
|
352
|
+
constexpr inline Arg<T, name> arg;
|
353
|
+
/// An optional positional argument.
|
354
|
+
///
|
355
|
+
template <concepts::ConvertibleFromValue T = Value, detail::cxstring name = "">
|
356
|
+
constexpr inline ArgOpt<T, name> arg_opt;
|
357
|
+
/// The rest of the positional arguments.
|
358
|
+
///
|
359
|
+
constexpr inline ArgSplat arg_splat;
|
360
|
+
/// Block.
|
361
|
+
constexpr inline Block block;
|
362
|
+
/// Optional block.
|
363
|
+
constexpr inline BlockOpt block_opt;
|
364
|
+
}
|
365
|
+
|
366
|
+
namespace concepts {
|
367
|
+
/// Specifies the types that can be used as argument specifications.
|
368
|
+
///
|
369
|
+
template <typename T>
|
370
|
+
concept ArgSpec = requires(Ruby &ruby, Value self, std::span<Value> &args) {
|
371
|
+
typename T::ResultType;
|
372
|
+
{ T::parse(ruby, self, args) } -> std::same_as<typename T::ResultType>;
|
373
|
+
};
|
374
|
+
}
|
375
|
+
|
376
|
+
/// Maps C++ character types to Ruby encodings.
|
377
|
+
///
|
378
|
+
template <typename CharT> struct CharTraits {
|
379
|
+
static_assert(detail::always_false_v<CharT>, "Encoding unknown for this character type");
|
380
|
+
};
|
381
|
+
|
382
|
+
template <> struct CharTraits<char> {
|
383
|
+
static inline constinit auto encoding = rb_ascii8bit_encoding;
|
384
|
+
};
|
385
|
+
template <> struct CharTraits<char8_t> {
|
386
|
+
static inline constinit auto encoding = rb_utf8_encoding;
|
387
|
+
};
|
388
|
+
|
389
|
+
namespace concepts {
|
390
|
+
template <typename T>
|
391
|
+
concept CharTraits = requires {
|
392
|
+
{ T::encoding() } -> std::same_as<rb_encoding *>;
|
393
|
+
};
|
394
|
+
|
395
|
+
/// Specifies the character types that can be mapped to Ruby strings.
|
396
|
+
///
|
397
|
+
template <typename T>
|
398
|
+
concept CharLike = requires {
|
399
|
+
requires CharTraits<::rcx::CharTraits<std::remove_cvref_t<T>>>;
|
400
|
+
typename std::basic_string_view<std::remove_cvref_t<T>>;
|
401
|
+
};
|
402
|
+
};
|
403
|
+
|
404
|
+
/// Literals
|
405
|
+
///
|
406
|
+
/// This namespace contains C++ user-defined literals to generate Ruby objects.
|
407
|
+
namespace literals {
|
408
|
+
/// Creates a mutable `String` in ASCII-8BIT encoding.
|
409
|
+
///
|
410
|
+
template <detail::cxstring> String operator""_str();
|
411
|
+
/// Creates a mutable `String` in UTF-8 encoding.
|
412
|
+
///
|
413
|
+
template <detail::u8cxstring> String operator""_str();
|
414
|
+
/// Creates a frozen `String` in ASCII-8BIT encoding.
|
415
|
+
///
|
416
|
+
template <detail::cxstring> String operator""_fstr();
|
417
|
+
/// Creates a frozen `String` in UTF-8 encoding.
|
418
|
+
///
|
419
|
+
template <detail::u8cxstring> String operator""_fstr();
|
420
|
+
/// Creates a `Symbol` for the name encoded in ASCII/ASCII-8BIT.
|
421
|
+
///
|
422
|
+
template <detail::cxstring> Symbol operator""_sym();
|
423
|
+
/// Creates a `Symbol` for the name encoded in UTF-8.
|
424
|
+
///
|
425
|
+
template <detail::u8cxstring> Symbol operator""_sym();
|
426
|
+
/// Creates an ID for the name encoded in ASCII/ASCII-8BIT.
|
427
|
+
///
|
428
|
+
/// IDs created this way is static and never garbage-collected.
|
429
|
+
template <detail::cxstring> Id operator""_id();
|
430
|
+
/// Creates an ID for the name encoded in UTF-8.
|
431
|
+
///
|
432
|
+
/// IDs created this way is static and never garbage-collected.
|
433
|
+
template <detail::u8cxstring> Id operator""_id();
|
434
|
+
}
|
435
|
+
|
436
|
+
/// Wrapper for static IDs.
|
437
|
+
///
|
438
|
+
/// Static IDs are never garbage-collected, and it's safe to store anywhere.
|
439
|
+
/// @sa rcx::literals::operator""_id()
|
440
|
+
class Id {
|
441
|
+
ID id_;
|
442
|
+
|
443
|
+
explicit Id(ID id);
|
444
|
+
|
445
|
+
public:
|
446
|
+
Id(Id const &) = default;
|
447
|
+
ID as_ID() const noexcept;
|
448
|
+
|
449
|
+
template <detail::cxstring> friend Id literals::operator""_id();
|
450
|
+
template <detail::u8cxstring> friend Id literals::operator""_id();
|
451
|
+
};
|
452
|
+
|
453
|
+
namespace value {
|
454
|
+
/// Whether the value wrapper can be nil.
|
455
|
+
enum Nilability : bool {
|
456
|
+
Nonnil = false,
|
457
|
+
Nilable = true,
|
458
|
+
};
|
459
|
+
|
460
|
+
/// Base class for all value wrappers.
|
461
|
+
///
|
462
|
+
/// Use \ref rcx::value::Value instead of this class.
|
463
|
+
class ValueBase {
|
464
|
+
VALUE value_;
|
465
|
+
|
466
|
+
protected:
|
467
|
+
/// Constructs a `ValueBase` from a Ruby value.
|
468
|
+
///
|
469
|
+
/// @param value The Ruby value to be wrapped.
|
470
|
+
constexpr ValueBase(VALUE value);
|
471
|
+
|
472
|
+
public:
|
473
|
+
/// Constructs a `ValueBase` from a nil value.
|
474
|
+
constexpr ValueBase();
|
475
|
+
/// Unwraps the `VALUE`.
|
476
|
+
///
|
477
|
+
/// @return The wrapped Ruby `VALUE`.
|
478
|
+
/// @warning This method should be used with caution and only when you have to call Ruby API
|
479
|
+
/// directly.
|
480
|
+
constexpr VALUE as_VALUE() const;
|
481
|
+
/// Constructs a `ValueBase` from a coerced value.
|
482
|
+
///
|
483
|
+
/// @param coerce The coerced value.
|
484
|
+
/// @warning This constructor is unsafe.
|
485
|
+
ValueBase(detail::unsafe_coerce<ValueBase> coerce): value_(coerce.value) {
|
486
|
+
}
|
487
|
+
|
488
|
+
/// Checks if the wrapped value is nil.
|
489
|
+
///
|
490
|
+
/// @return Whether the value is nil.
|
491
|
+
bool is_nil() const;
|
492
|
+
/// Checks if the wrapped value is frozen.
|
493
|
+
///
|
494
|
+
/// @return Whether the value is frozen.
|
495
|
+
bool is_frozen() const;
|
496
|
+
/// Checks if the wrapped value is an instance of a class.
|
497
|
+
///
|
498
|
+
/// @param klass The class to check against.
|
499
|
+
/// @return Whether the value is an instance of the class.
|
500
|
+
template <typename T> bool is_instance_of(ClassT<T> klass) const;
|
501
|
+
/// Checks if the wrapped value is a kind of a class.
|
502
|
+
///
|
503
|
+
/// @param klass The class to check against.
|
504
|
+
/// @return Whether the value is a kind of the class.
|
505
|
+
template <typename T> bool is_kind_of(ClassT<T> klass) const;
|
506
|
+
};
|
507
|
+
|
508
|
+
template <typename Derived, std::derived_from<ValueBase> Super, Nilability nilable = Nonnil>
|
509
|
+
class ValueT: public Super {
|
510
|
+
public:
|
511
|
+
constexpr ValueT()
|
512
|
+
requires(nilable == Nilability::Nilable)
|
513
|
+
= default;
|
514
|
+
constexpr ValueT()
|
515
|
+
requires(nilable != Nilability::Nilable)
|
516
|
+
= rcx_delete("This type of Value cannot be nil.");
|
517
|
+
|
518
|
+
template <std::derived_from<Derived> T> ValueT(T const &value): Super(value.as_VALUE()) {
|
519
|
+
}
|
520
|
+
constexpr ValueT(detail::unsafe_coerce<Derived> coerce): Super(coerce) {
|
521
|
+
}
|
522
|
+
Derived &operator=(Derived const &other) {
|
523
|
+
Super::operator=(other);
|
524
|
+
return *this;
|
525
|
+
}
|
526
|
+
|
527
|
+
ClassT<Derived> get_class() const;
|
528
|
+
Derived freeze() const;
|
529
|
+
|
530
|
+
template <concepts::ConvertibleFromValue Self = Derived, concepts::ArgSpec... ArgSpec>
|
531
|
+
Derived define_singleton_method(concepts::Identifier auto &&mid,
|
532
|
+
std::invocable<Self, typename ArgSpec::ResultType...> auto &&function,
|
533
|
+
ArgSpec... argspec) const;
|
534
|
+
};
|
535
|
+
|
536
|
+
class Value: public ValueT<Value, ValueBase, Nilable> {
|
537
|
+
public:
|
538
|
+
using ValueT<Value, ValueBase, Nilable>::ValueT;
|
539
|
+
|
540
|
+
template <concepts::ConvertibleFromValue R = Value>
|
541
|
+
R send(concepts::Identifier auto &&mid, concepts::ConvertibleIntoValue auto &&...args) const;
|
542
|
+
|
543
|
+
bool test() const noexcept;
|
544
|
+
|
545
|
+
/// Converts the object into a String using its `#inspect` method.
|
546
|
+
///
|
547
|
+
/// @return The converted string.
|
548
|
+
String inspect() const;
|
549
|
+
|
550
|
+
/// Converts the object into a String using its `#to_s` method.
|
551
|
+
///
|
552
|
+
/// @return The converted string.
|
553
|
+
String to_string() const;
|
554
|
+
|
555
|
+
bool instance_variable_defined(concepts::Identifier auto &&name) const;
|
556
|
+
template <concepts::ConvertibleFromValue T = Value>
|
557
|
+
auto instance_variable_get(concepts::Identifier auto &&name) const -> auto;
|
558
|
+
void instance_variable_set(
|
559
|
+
concepts::Identifier auto &&name, concepts::ConvertibleIntoValue auto &&value) const;
|
560
|
+
|
561
|
+
static Value const qnil;
|
562
|
+
static Value const qtrue;
|
563
|
+
static Value const qfalse;
|
564
|
+
static Value const qundef;
|
565
|
+
};
|
566
|
+
|
567
|
+
/// Represents a Ruby module or a class.
|
568
|
+
///
|
569
|
+
class Module: public ValueT<Module, Value> {
|
570
|
+
public:
|
571
|
+
using ValueT<Module, Value>::ValueT;
|
572
|
+
|
573
|
+
/// Returns the name path of this module.
|
574
|
+
///
|
575
|
+
/// @return The name path of this module.
|
576
|
+
String name() const;
|
577
|
+
|
578
|
+
/// Defines a module under this module.
|
579
|
+
///
|
580
|
+
/// @warning Modules defined this way will be never garbage-collected.
|
581
|
+
///
|
582
|
+
/// @param name Name of the module.
|
583
|
+
/// @return The newly defined module.
|
584
|
+
Module define_module(concepts::Identifier auto &&name) const;
|
585
|
+
|
586
|
+
/// Defines a class under this module.
|
587
|
+
///
|
588
|
+
/// @warning Classes defined this way will be never garbage-collected.
|
589
|
+
///
|
590
|
+
/// @param name Name of the class.
|
591
|
+
/// @param superclass The new class will be a subclass of this class.
|
592
|
+
/// @return The newly defined class.
|
593
|
+
template <typename T = Value, typename S>
|
594
|
+
ClassT<T> define_class(concepts::Identifier auto &&name, ClassT<S> superclass) const;
|
595
|
+
|
596
|
+
/// Defines a subclass of Object under this module.
|
597
|
+
///
|
598
|
+
/// @warning Classes defined this way will be never garbage-collected.
|
599
|
+
///
|
600
|
+
/// @param name Name of the class.
|
601
|
+
/// @return The newly created class.
|
602
|
+
template <typename T = Value> ClassT<T> define_class(concepts::Identifier auto &&name) const;
|
603
|
+
|
604
|
+
/// Defines an instance method.
|
605
|
+
///
|
606
|
+
/// @warning Defining method this way allocates a resource that will never be
|
607
|
+
/// garbage-collected.
|
608
|
+
///
|
609
|
+
/// @tparam Self The type of self.
|
610
|
+
/// @param mid The name of the method.
|
611
|
+
/// @param function The function to be called.
|
612
|
+
/// @param argspec List of argument specifications.
|
613
|
+
/// @return Self.
|
614
|
+
template <concepts::ConvertibleFromValue Self = Value, concepts::ArgSpec... ArgSpec>
|
615
|
+
Module define_method(concepts::Identifier auto &&mid,
|
616
|
+
std::invocable<Self, typename ArgSpec::ResultType...> auto &&function,
|
617
|
+
ArgSpec... argspec) const;
|
618
|
+
|
619
|
+
/// Checks if a constant is defined under this module.
|
620
|
+
///
|
621
|
+
/// @param name Name of the constant.
|
622
|
+
/// @returns Whether the constant is defined.
|
623
|
+
bool const_defined(concepts::Identifier auto &&name) const;
|
624
|
+
|
625
|
+
/// Gets the value of a constant under this module.
|
626
|
+
///
|
627
|
+
/// @tparam T The type the constant value should be converted into.
|
628
|
+
/// @param name Name of the constant.
|
629
|
+
/// @return The value converted into T.
|
630
|
+
template <concepts::ConvertibleFromValue T = Value>
|
631
|
+
T const_get(concepts::Identifier auto &&name) const;
|
632
|
+
|
633
|
+
/// Defines a constant with a value under this module.
|
634
|
+
///
|
635
|
+
/// @param name The name of the constant.
|
636
|
+
/// @param value The value to be set.
|
637
|
+
void const_set(
|
638
|
+
concepts::Identifier auto &&name, concepts::ConvertibleIntoValue auto &&value) const;
|
639
|
+
|
640
|
+
/// Creates an anonymous module.
|
641
|
+
///
|
642
|
+
/// @return The newly created module.
|
643
|
+
static Module new_module();
|
644
|
+
};
|
645
|
+
|
646
|
+
template <typename T>
|
647
|
+
class [[clang::preferred_name(Class)]] ClassT: public ValueT<ClassT<T>, Module> {
|
648
|
+
public:
|
649
|
+
using ValueT<ClassT<T>, Module>::ValueT;
|
650
|
+
|
651
|
+
/// Allocates and initializes an instance of this class.
|
652
|
+
///
|
653
|
+
/// @param args The arguments to be passed to `initialize.
|
654
|
+
/// @return The new instance.
|
655
|
+
T new_instance(concepts::ConvertibleIntoValue auto &&...args) const
|
656
|
+
requires std::derived_from<T, ValueBase>;
|
657
|
+
|
658
|
+
/// Allocates an uninitialized instance of this class.
|
659
|
+
///
|
660
|
+
/// @returns The newly allocated uninitialized object.
|
661
|
+
Value allocate() const;
|
662
|
+
|
663
|
+
/// Checks if this class is a subclass of another class.
|
664
|
+
///
|
665
|
+
/// @param klass The class to check against.
|
666
|
+
/// @return Whether this class is a subclass of the given class.
|
667
|
+
template <typename S> bool is_subclass_of(ClassT<S> klass) const;
|
668
|
+
/// Checks if this class is a superclass of another class.
|
669
|
+
///
|
670
|
+
/// @param klass The class to check against.
|
671
|
+
/// @return Whether this class is a superclass of the given class.
|
672
|
+
template <typename S> bool is_superclass_of(ClassT<S> klass) const;
|
673
|
+
|
674
|
+
/// Defines a mutating instance method.
|
675
|
+
///
|
676
|
+
/// The method will raise a `FrozenError` if the object is frozen.
|
677
|
+
/// @param mid The name of the method.
|
678
|
+
/// @param function The function to be called.
|
679
|
+
/// @param argspec List of argument specifications.
|
680
|
+
/// @return Self.
|
681
|
+
/// @warning Defining method this way allocates a resource that will never be
|
682
|
+
/// garbage-collected.
|
683
|
+
template <concepts::ArgSpec... ArgSpec>
|
684
|
+
ClassT<T> define_method(concepts::Identifier auto &&mid,
|
685
|
+
std::invocable<T &, typename ArgSpec::ResultType...> auto &&function,
|
686
|
+
ArgSpec... argspec) const;
|
687
|
+
|
688
|
+
/// Defines a non-mutating instance method.
|
689
|
+
///
|
690
|
+
/// The method can be called even when the object is frozen.
|
691
|
+
/// @param mid The name of the method.
|
692
|
+
/// @param function The function to be called.
|
693
|
+
/// @param argspec List of argument specifications.
|
694
|
+
/// @return Self.
|
695
|
+
/// @warning Defining method this way allocates a resource that will never be
|
696
|
+
/// garbage-collected.
|
697
|
+
template <concepts::ArgSpec... ArgSpec>
|
698
|
+
ClassT<T> define_method_const(concepts::Identifier auto &&mid,
|
699
|
+
std::invocable<T const &, typename ArgSpec::ResultType...> auto &&function,
|
700
|
+
ArgSpec... argspec) const;
|
701
|
+
|
702
|
+
/// Defines `initialize` method using a C++ constructor.
|
703
|
+
///
|
704
|
+
/// @param argspec List of argument specifications.
|
705
|
+
/// @return Self.
|
706
|
+
template <concepts::ArgSpec... ArgSpec>
|
707
|
+
requires std::constructible_from<T, typename ArgSpec::ResultType...>
|
708
|
+
ClassT<T> define_constructor(ArgSpec... argspec) const;
|
709
|
+
|
710
|
+
/// Defines `initialize_copy` method using the C++ copy constructor.
|
711
|
+
///
|
712
|
+
/// @return Self.
|
713
|
+
ClassT<T> define_copy_constructor() const
|
714
|
+
requires std::copy_constructible<T>;
|
715
|
+
|
716
|
+
/// Creates a new class.
|
717
|
+
///
|
718
|
+
/// @return The newly created class.
|
719
|
+
static Class new_class();
|
720
|
+
|
721
|
+
/// Creates a new class with a superclass.
|
722
|
+
///
|
723
|
+
/// @param superclass The new class will be a subclass of this class.
|
724
|
+
/// @return The newly created class.
|
725
|
+
template <typename S> static ClassT<S> new_class(ClassT<S> superclass);
|
726
|
+
};
|
727
|
+
|
728
|
+
class Symbol: public ValueT<Symbol, Value> {
|
729
|
+
public:
|
730
|
+
using ValueT<Symbol, Value>::ValueT;
|
731
|
+
template <size_t N> explicit Symbol(char const (&)[N]) noexcept;
|
732
|
+
explicit Symbol(std::string_view sv) noexcept;
|
733
|
+
|
734
|
+
/**
|
735
|
+
* Returns Ruby-internal ID.
|
736
|
+
*
|
737
|
+
* The ID returned by this method may be dynamic and subject to garbage collection.
|
738
|
+
* So do not store, whether on stack or in heap.
|
739
|
+
*/
|
740
|
+
ID as_ID() const noexcept;
|
741
|
+
};
|
742
|
+
|
743
|
+
class String: public ValueT<String, Value> {
|
744
|
+
public:
|
745
|
+
using ValueT<String, Value>::ValueT;
|
746
|
+
|
747
|
+
template <concepts::StringLike S> static String intern_from(S &&s);
|
748
|
+
template <concepts::CharLike CharT> static String intern_from(CharT const *RCX_Nonnull s);
|
749
|
+
template <concepts::StringLike S> static String copy_from(S &&s);
|
750
|
+
template <concepts::CharLike CharT> static String copy_from(CharT const *RCX_Nonnull s);
|
751
|
+
|
752
|
+
size_t size() const noexcept;
|
753
|
+
char *RCX_Nonnull data() const;
|
754
|
+
char const *RCX_Nonnull cdata() const noexcept;
|
755
|
+
explicit operator std::string_view() const noexcept;
|
756
|
+
|
757
|
+
String lock() const;
|
758
|
+
String unlock() const;
|
759
|
+
};
|
760
|
+
|
761
|
+
class Array: public ValueT<Array, Value> {
|
762
|
+
public:
|
763
|
+
using ValueT<Array, Value>::ValueT;
|
764
|
+
|
765
|
+
size_t size() const noexcept;
|
766
|
+
template <concepts::ConvertibleFromValue T = Value> decltype(auto) at(size_t i) const;
|
767
|
+
Value operator[](size_t i) const;
|
768
|
+
|
769
|
+
template <std::ranges::contiguous_range R>
|
770
|
+
#ifdef HAVE_STD_IS_LAYOUT_COMPATIBLE
|
771
|
+
requires std::is_layout_compatible_v<std::ranges::range_value_t<R>, ValueBase>
|
772
|
+
#else
|
773
|
+
requires(std::derived_from<std::ranges::range_value_t<R>, ValueBase> &&
|
774
|
+
sizeof(std::ranges::range_value_t<R>) == sizeof(ValueBase))
|
775
|
+
#endif
|
776
|
+
static Array new_from(R const &elements);
|
777
|
+
|
778
|
+
static Array new_from(std::initializer_list<ValueBase> elements);
|
779
|
+
|
780
|
+
template <std::derived_from<ValueBase>... T>
|
781
|
+
static Array new_from(std::tuple<T...> const &elements);
|
782
|
+
|
783
|
+
static Array new_array();
|
784
|
+
static Array new_array(long capacity);
|
785
|
+
|
786
|
+
template <concepts::ConvertibleIntoValue T = Value> Array push_back(T value) const;
|
787
|
+
template <concepts::ConvertibleFromValue T = Value> T pop_back() const;
|
788
|
+
template <concepts::ConvertibleIntoValue T = Value> Array push_front(T value) const;
|
789
|
+
template <concepts::ConvertibleFromValue T = Value> T pop_front() const;
|
790
|
+
};
|
791
|
+
|
792
|
+
class Proc: public ValueT<Proc, Value> {
|
793
|
+
public:
|
794
|
+
using ValueT<Proc, Value>::ValueT;
|
795
|
+
|
796
|
+
bool is_lambda() const;
|
797
|
+
Value call(Array args) const;
|
798
|
+
};
|
799
|
+
|
800
|
+
class Exception: public ValueT<Exception, Value> {
|
801
|
+
public:
|
802
|
+
using ValueT<Exception, Value>::ValueT;
|
803
|
+
|
804
|
+
template <std::derived_from<Exception> E, typename... Args>
|
805
|
+
static E format(ClassT<E> cls, std::format_string<Args...> fmt, Args &&...args);
|
806
|
+
};
|
807
|
+
|
808
|
+
#ifdef RCX_IO_BUFFER
|
809
|
+
/// Represents an `IO::Buffer` object.
|
810
|
+
class IOBuffer: public ValueT<IOBuffer, Value> {
|
811
|
+
public:
|
812
|
+
using ValueT<IOBuffer, Value>::ValueT;
|
813
|
+
|
814
|
+
/// Creates an `IO::Buffer` with internal storage.
|
815
|
+
///
|
816
|
+
/// @param size The size of the buffer.
|
817
|
+
/// @return The newly created buffer.
|
818
|
+
static IOBuffer new_internal(size_t size);
|
819
|
+
/// Creates an `IO::Buffer` with mapped storage.
|
820
|
+
///
|
821
|
+
/// @param size The size of the buffer.
|
822
|
+
/// @return The newly created buffer.
|
823
|
+
static IOBuffer new_mapped(size_t size);
|
824
|
+
/// Creates an `IO::Buffer` with externally managed storage. The returned `IO::Buffer` should
|
825
|
+
/// be `free`d when the underlying storage is longer valid.
|
826
|
+
///
|
827
|
+
/// @param bytes The contiguous memory region to be used as the storage.
|
828
|
+
/// @return The newly created buffer.
|
829
|
+
template <size_t N = std::dynamic_extent>
|
830
|
+
static IOBuffer new_external(std::span<std::byte, N> bytes [[clang::lifetimebound]]);
|
831
|
+
/// Creates an `IO::Buffer` with externally managed read-only storage. The returned
|
832
|
+
/// `IO::Buffer` should be `free`d when the underlying storage is longer valid.
|
833
|
+
///
|
834
|
+
/// @param bytes The contiguous memory region to be used as the storage.
|
835
|
+
/// @return The newly created buffer.
|
836
|
+
template <size_t N = std::dynamic_extent>
|
837
|
+
static IOBuffer new_external(std::span<std::byte const, N> bytes [[clang::lifetimebound]]);
|
838
|
+
|
839
|
+
/// Frees the internal storage or disassociates the external storage.
|
840
|
+
///
|
841
|
+
/// See the Ruby documentation for `IO::Buffer#free`.
|
842
|
+
void free() const;
|
843
|
+
/// Resizes the `IO::Buffer` to the given size.
|
844
|
+
///
|
845
|
+
/// @param size The new size of the buffer.
|
846
|
+
void resize(size_t size) const;
|
847
|
+
|
848
|
+
/// Returns the bytes of the `IO::Buffer`. This will raise if the `IO::Buffer` is not
|
849
|
+
/// writable.
|
850
|
+
///
|
851
|
+
/// @return The bytes of the `IO::Buffer`.
|
852
|
+
std::span<std::byte> bytes() const;
|
853
|
+
/// Returns the bytes of the `IO::Buffer` as a read-only span.
|
854
|
+
///
|
855
|
+
/// @return The bytes of the `IO::Buffer`.
|
856
|
+
std::span<std::byte const> cbytes() const;
|
857
|
+
|
858
|
+
// BasicLockable
|
859
|
+
/// Locks the `IO::Buffer`.
|
860
|
+
///
|
861
|
+
void lock() const;
|
862
|
+
/// Unlocks the `IO::Buffer`.
|
863
|
+
///
|
864
|
+
void unlock() const;
|
865
|
+
|
866
|
+
// Lockable
|
867
|
+
/// Tries to lock the `IO::Buffer`.
|
868
|
+
///
|
869
|
+
/// @return Whether the lock is successful.
|
870
|
+
bool try_lock() const;
|
871
|
+
};
|
872
|
+
#endif
|
873
|
+
}
|
874
|
+
/// Built-in classes.
|
875
|
+
///
|
876
|
+
namespace builtin {
|
877
|
+
/// `NilClass` class
|
878
|
+
///
|
879
|
+
inline value::Class const NilClass = detail::unsafe_coerce<value::Class>(::rb_cNilClass);
|
880
|
+
/// `TrueClass` class
|
881
|
+
///
|
882
|
+
inline value::Class const TrueClass = detail::unsafe_coerce<value::Class>(::rb_cTrueClass);
|
883
|
+
/// `FalseClass` class
|
884
|
+
///
|
885
|
+
inline value::Class const FalseClass = detail::unsafe_coerce<value::Class>(::rb_cFalseClass);
|
886
|
+
/// `Class` class
|
887
|
+
///
|
888
|
+
inline ClassT<value::Class> const Class =
|
889
|
+
detail::unsafe_coerce<ClassT<value::Class>>(::rb_cClass);
|
890
|
+
/// `Module` class
|
891
|
+
///
|
892
|
+
inline ClassT<value::Module> const Module =
|
893
|
+
detail::unsafe_coerce<ClassT<value::Module>>(::rb_cModule);
|
894
|
+
/// `BasicObject` class
|
895
|
+
///
|
896
|
+
inline value::Class const BasicObject = detail::unsafe_coerce<value::Class>(::rb_cBasicObject);
|
897
|
+
/// `Object` class
|
898
|
+
///
|
899
|
+
inline value::Class const Object = detail::unsafe_coerce<value::Class>(::rb_cObject);
|
900
|
+
/// `String` class
|
901
|
+
///
|
902
|
+
inline ClassT<value::String> const String =
|
903
|
+
detail::unsafe_coerce<ClassT<value::String>>(::rb_cString);
|
904
|
+
/// `Encoding` class
|
905
|
+
///
|
906
|
+
inline value::Class const Encoding = detail::unsafe_coerce<value::Class>(::rb_cEncoding);
|
907
|
+
/// `Symbol` class
|
908
|
+
///
|
909
|
+
inline value::Class const Symbol = detail::unsafe_coerce<value::Class>(::rb_cSymbol);
|
910
|
+
/// `Regexp` class
|
911
|
+
///
|
912
|
+
inline value::Class const Regexp = detail::unsafe_coerce<value::Class>(::rb_cRegexp);
|
913
|
+
/// `MatchData` class
|
914
|
+
///
|
915
|
+
inline value::Class const MatchData = detail::unsafe_coerce<value::Class>(::rb_cMatch);
|
916
|
+
/// `Array` class
|
917
|
+
///
|
918
|
+
inline ClassT<value::Array> const Array =
|
919
|
+
detail::unsafe_coerce<ClassT<value::Array>>(::rb_cArray);
|
920
|
+
/// `Hash` class
|
921
|
+
///
|
922
|
+
inline value::Class const Hash = detail::unsafe_coerce<value::Class>(::rb_cHash);
|
923
|
+
/// `Proc` class
|
924
|
+
///
|
925
|
+
inline ClassT<value::Proc> const Proc = detail::unsafe_coerce<ClassT<value::Proc>>(::rb_cProc);
|
926
|
+
/// `Method` class
|
927
|
+
///
|
928
|
+
inline value::Class const Method = detail::unsafe_coerce<value::Class>(::rb_cMethod);
|
929
|
+
/// `Numeric` class
|
930
|
+
///
|
931
|
+
inline value::Class const Numeric = detail::unsafe_coerce<value::Class>(::rb_cNumeric);
|
932
|
+
/// `Integer` class
|
933
|
+
///
|
934
|
+
inline value::Class const Integer = detail::unsafe_coerce<value::Class>(::rb_cInteger);
|
935
|
+
/// `Float` class
|
936
|
+
///
|
937
|
+
inline value::Class const Float = detail::unsafe_coerce<value::Class>(::rb_cFloat);
|
938
|
+
/// `Rational` class
|
939
|
+
///
|
940
|
+
inline value::Class const Rational = detail::unsafe_coerce<value::Class>(::rb_cRational);
|
941
|
+
/// `Complex` class
|
942
|
+
///
|
943
|
+
inline value::Class const Complex = detail::unsafe_coerce<value::Class>(::rb_cComplex);
|
944
|
+
/// `Range` class
|
945
|
+
///
|
946
|
+
inline value::Class const Range = detail::unsafe_coerce<value::Class>(::rb_cRange);
|
947
|
+
/// `IO` class
|
948
|
+
///
|
949
|
+
inline value::Class const IO = detail::unsafe_coerce<value::Class>(::rb_cIO);
|
950
|
+
/// `File` class
|
951
|
+
///
|
952
|
+
inline value::Class const File = detail::unsafe_coerce<value::Class>(::rb_cFile);
|
953
|
+
/// `Thread` class
|
954
|
+
///
|
955
|
+
inline value::Class const Thread = detail::unsafe_coerce<value::Class>(::rb_cThread);
|
956
|
+
|
957
|
+
/// `Exception` class
|
958
|
+
///
|
959
|
+
inline value::ClassT<value::Exception> const Exception =
|
960
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eException);
|
961
|
+
/// `SystemExit` class
|
962
|
+
///
|
963
|
+
inline value::ClassT<value::Exception> const SystemExit =
|
964
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eSystemExit);
|
965
|
+
/// `Interrupt` class
|
966
|
+
///
|
967
|
+
inline value::ClassT<value::Exception> const Interrupt =
|
968
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eInterrupt);
|
969
|
+
/// `SignalException` class
|
970
|
+
///
|
971
|
+
inline value::ClassT<value::Exception> const SignalException =
|
972
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eSignal);
|
973
|
+
/// `StandardError` class
|
974
|
+
///
|
975
|
+
inline value::ClassT<value::Exception> const StandardError =
|
976
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eStandardError);
|
977
|
+
/// `RuntimeError` class
|
978
|
+
///
|
979
|
+
inline value::ClassT<value::Exception> const RuntimeError =
|
980
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eRuntimeError);
|
981
|
+
/// `FrozenError` class
|
982
|
+
///
|
983
|
+
inline value::ClassT<value::Exception> const FrozenError =
|
984
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eFrozenError);
|
985
|
+
/// `TypeError` class
|
986
|
+
///
|
987
|
+
inline value::ClassT<value::Exception> const TypeError =
|
988
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eTypeError);
|
989
|
+
/// `ArgumentError` class
|
990
|
+
///
|
991
|
+
inline value::ClassT<value::Exception> const ArgumentError =
|
992
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eArgError);
|
993
|
+
/// `IndexError` class
|
994
|
+
///
|
995
|
+
inline value::ClassT<value::Exception> const IndexError =
|
996
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eIndexError);
|
997
|
+
/// `KeyError` class
|
998
|
+
///
|
999
|
+
inline value::ClassT<value::Exception> const KeyError =
|
1000
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eKeyError);
|
1001
|
+
/// `RangeError` class
|
1002
|
+
///
|
1003
|
+
inline value::ClassT<value::Exception> const RangeError =
|
1004
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eRangeError);
|
1005
|
+
/// `NameError` class
|
1006
|
+
///
|
1007
|
+
inline value::ClassT<value::Exception> const NameError =
|
1008
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eNameError);
|
1009
|
+
/// `EncodingError` class
|
1010
|
+
///
|
1011
|
+
inline value::ClassT<value::Exception> const EncodingError =
|
1012
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eEncodingError);
|
1013
|
+
/// `Encoding::CompatibilityError` class
|
1014
|
+
///
|
1015
|
+
inline value::ClassT<value::Exception> const EncodingCompatibilityError =
|
1016
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eEncCompatError);
|
1017
|
+
/// `NoMethodError` class
|
1018
|
+
///
|
1019
|
+
inline value::ClassT<value::Exception> const NoMethodError =
|
1020
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eNoMethodError);
|
1021
|
+
/// `SecurityError` class
|
1022
|
+
///
|
1023
|
+
inline value::ClassT<value::Exception> const SecurityError =
|
1024
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eSecurityError);
|
1025
|
+
/// `NotImplementedError` class
|
1026
|
+
///
|
1027
|
+
inline value::ClassT<value::Exception> const NotImplementedError =
|
1028
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eNotImpError);
|
1029
|
+
/// `NoMemoryError` class
|
1030
|
+
///
|
1031
|
+
inline value::ClassT<value::Exception> const NoMemoryError =
|
1032
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eNoMemError);
|
1033
|
+
/// `NoMatchingPatternError` class
|
1034
|
+
///
|
1035
|
+
inline value::ClassT<value::Exception> const NoMatchingPatternError =
|
1036
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eNoMatchingPatternError);
|
1037
|
+
/// `NoMatchingPatternKeyError` class
|
1038
|
+
///
|
1039
|
+
inline value::ClassT<value::Exception> const NoMatchingPatternKeyError =
|
1040
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eNoMatchingPatternKeyError);
|
1041
|
+
/// `ScriptError` class
|
1042
|
+
///
|
1043
|
+
inline value::ClassT<value::Exception> const ScriptError =
|
1044
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eScriptError);
|
1045
|
+
/// `SyntaxError` class
|
1046
|
+
///
|
1047
|
+
inline value::ClassT<value::Exception> const SyntaxError =
|
1048
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eSyntaxError);
|
1049
|
+
/// `LoadError` class
|
1050
|
+
///
|
1051
|
+
inline value::ClassT<value::Exception> const LoadError =
|
1052
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eLoadError);
|
1053
|
+
/// `SystemCallError` class
|
1054
|
+
///
|
1055
|
+
inline value::ClassT<value::Exception> const SystemCallError =
|
1056
|
+
detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eSystemCallError);
|
1057
|
+
|
1058
|
+
#ifdef RCX_IO_BUFFER
|
1059
|
+
/// `IO::Buffer` class
|
1060
|
+
///
|
1061
|
+
inline value::Class const IOBuffer = detail::unsafe_coerce<value::Class>(::rb_cIOBuffer);
|
1062
|
+
#endif
|
1063
|
+
};
|
1064
|
+
|
1065
|
+
namespace typed_data {
|
1066
|
+
template <typename> class DataTypeStorage;
|
1067
|
+
}
|
1068
|
+
|
1069
|
+
/// Garbage collection.
|
1070
|
+
namespace gc {
|
1071
|
+
/// Phases of garbage collection.
|
1072
|
+
///
|
1073
|
+
enum Phase {
|
1074
|
+
Marking,
|
1075
|
+
Compaction,
|
1076
|
+
};
|
1077
|
+
|
1078
|
+
struct Gc {
|
1079
|
+
/// Marks an object as movable.
|
1080
|
+
///
|
1081
|
+
/// The object will be marked as movable. In the compaction phase, the object may be moved.
|
1082
|
+
///
|
1083
|
+
/// @tparam T The type of the object to be marked.
|
1084
|
+
/// @param value The object to be marked.
|
1085
|
+
template <std::derived_from<ValueBase> T> void mark_movable(T &value) const noexcept;
|
1086
|
+
/// Marks an object as pinned.
|
1087
|
+
///
|
1088
|
+
/// The object will be marked as pinned. The object will not be moved while this reference
|
1089
|
+
/// is alive.
|
1090
|
+
///
|
1091
|
+
/// @param value The object to be marked.
|
1092
|
+
void mark_pinned(ValueBase value) const noexcept;
|
1093
|
+
|
1094
|
+
private:
|
1095
|
+
Phase phase_;
|
1096
|
+
|
1097
|
+
Gc(Phase phase);
|
1098
|
+
template <typename> friend class typed_data::DataTypeStorage;
|
1099
|
+
};
|
1100
|
+
}
|
1101
|
+
|
1102
|
+
namespace typed_data {
|
1103
|
+
template <typename T> void dmark(gc::Gc, T *RCX_Nonnull) noexcept;
|
1104
|
+
template <typename T> void dfree(T *RCX_Nonnull) noexcept;
|
1105
|
+
template <typename T> size_t dsize(T const *RCX_Nonnull) noexcept;
|
1106
|
+
|
1107
|
+
struct AssociatedValue {
|
1108
|
+
std::optional<Value> value_;
|
1109
|
+
|
1110
|
+
public:
|
1111
|
+
AssociatedValue() = default;
|
1112
|
+
AssociatedValue(AssociatedValue const &) noexcept: value_{} {
|
1113
|
+
}
|
1114
|
+
AssociatedValue &operator=(AssociatedValue const &) noexcept {
|
1115
|
+
return *this;
|
1116
|
+
}
|
1117
|
+
|
1118
|
+
void associate_value(Value v) {
|
1119
|
+
if(value_) {
|
1120
|
+
throw std::runtime_error("Already associcated");
|
1121
|
+
}
|
1122
|
+
value_ = v;
|
1123
|
+
}
|
1124
|
+
|
1125
|
+
std::optional<Value> get_associated_value() const {
|
1126
|
+
return value_;
|
1127
|
+
};
|
1128
|
+
|
1129
|
+
void mark_associated_value(gc::Gc gc) noexcept {
|
1130
|
+
if(value_) {
|
1131
|
+
gc.mark_movable(*value_);
|
1132
|
+
}
|
1133
|
+
}
|
1134
|
+
};
|
1135
|
+
|
1136
|
+
struct OneWayAssociation {};
|
1137
|
+
struct TwoWayAssociation: public AssociatedValue {};
|
1138
|
+
|
1139
|
+
template <std::derived_from<TwoWayAssociation> T>
|
1140
|
+
void dmark(gc::Gc gc, T *RCX_Nonnull p) noexcept;
|
1141
|
+
|
1142
|
+
struct WrappedStructBase {};
|
1143
|
+
|
1144
|
+
template <typename AssociationPolicy = OneWayAssociation>
|
1145
|
+
struct WrappedStruct: public WrappedStructBase, public AssociationPolicy {};
|
1146
|
+
|
1147
|
+
template <typename T, typename S>
|
1148
|
+
ClassT<T> bind_data_type(ClassT<T> klass, ClassT<S> superclass);
|
1149
|
+
|
1150
|
+
template <typename T> class DataTypeStorage {
|
1151
|
+
inline static std::optional<rb_data_type_t> data_type_;
|
1152
|
+
|
1153
|
+
public:
|
1154
|
+
static void bind(ClassT<T> klass, rb_data_type_t const *RCX_Nullable parent = nullptr);
|
1155
|
+
static ClassT<T> bound_class();
|
1156
|
+
static rb_data_type_t const *RCX_Nonnull get();
|
1157
|
+
|
1158
|
+
template <typename... A>
|
1159
|
+
requires std::constructible_from<T, A...>
|
1160
|
+
static Value initialize(Value value, A &&...args);
|
1161
|
+
|
1162
|
+
static Value initialize_copy(Value value, T const &obj)
|
1163
|
+
requires std::copy_constructible<T>;
|
1164
|
+
|
1165
|
+
// TODO: allocator support
|
1166
|
+
};
|
1167
|
+
|
1168
|
+
template <typename T> using DataType = DataTypeStorage<std::remove_cvref_t<T>>;
|
1169
|
+
}
|
1170
|
+
|
1171
|
+
namespace convert {
|
1172
|
+
template <std::derived_from<typed_data::WrappedStructBase> T> struct FromValue<T> {
|
1173
|
+
std::reference_wrapper<T> convert(Value value);
|
1174
|
+
};
|
1175
|
+
template <std::derived_from<typed_data::TwoWayAssociation> T> struct IntoValue<T> {
|
1176
|
+
Value convert(T &value);
|
1177
|
+
};
|
1178
|
+
template <std::derived_from<typed_data::WrappedStructBase> T> struct FromValue<ClassT<T>> {
|
1179
|
+
ClassT<T> convert(Value value);
|
1180
|
+
};
|
1181
|
+
}
|
1182
|
+
|
1183
|
+
/// Leaking object container.
|
1184
|
+
///
|
1185
|
+
/// The contained Ruby object will not be garbage-collected or moved.
|
1186
|
+
/// Use this container if you want to store Ruby objects in global variables or static block
|
1187
|
+
/// variables.
|
1188
|
+
template <std::derived_from<ValueBase> T> class Leak {
|
1189
|
+
union {
|
1190
|
+
// T has a VALUE as its first field.
|
1191
|
+
T value_;
|
1192
|
+
VALUE raw_value_;
|
1193
|
+
};
|
1194
|
+
bool init_;
|
1195
|
+
|
1196
|
+
public:
|
1197
|
+
/// Initializes the container with no value.
|
1198
|
+
///
|
1199
|
+
Leak() noexcept;
|
1200
|
+
Leak(Leak<T> const &) = rcx_delete("Leak<T> cannot be copied");
|
1201
|
+
/// Initializes the container with the given value.
|
1202
|
+
///
|
1203
|
+
Leak(T value) noexcept(noexcept(T(value)));
|
1204
|
+
Leak<T> &operator=(Leak<T> const &) = rcx_delete("Leak<T> cannot be copied");
|
1205
|
+
/// Copies the given value into the container, destroying the existing value if any.
|
1206
|
+
///
|
1207
|
+
Leak<T> &operator=(T value) noexcept(noexcept(T(value)));
|
1208
|
+
/// Gets the value in the container.
|
1209
|
+
///
|
1210
|
+
/// @throw std::runtime_error When the container has no value.
|
1211
|
+
T get() const;
|
1212
|
+
/// Copies the given value into the container, destroying the existing value if any.
|
1213
|
+
///
|
1214
|
+
void set(T value) noexcept(noexcept(T(value)));
|
1215
|
+
/// Gets the value in the container.
|
1216
|
+
///
|
1217
|
+
/// @throw std::runtime_error When the container has no value.
|
1218
|
+
T operator*() const;
|
1219
|
+
T const *RCX_Nonnull operator->() const;
|
1220
|
+
/// Clears the container.
|
1221
|
+
///
|
1222
|
+
/// The value originally in the container will be no longer pinned.
|
1223
|
+
void clear() noexcept;
|
1224
|
+
};
|
1225
|
+
template <std::derived_from<ValueBase> T> Leak(T) -> Leak<T>;
|
1226
|
+
|
1227
|
+
class Ruby {
|
1228
|
+
public:
|
1229
|
+
Module define_module(concepts::Identifier auto &&name);
|
1230
|
+
|
1231
|
+
template <typename T = Value, typename S>
|
1232
|
+
ClassT<T> define_class(concepts::Identifier auto &&name, ClassT<S> superclass);
|
1233
|
+
|
1234
|
+
template <typename T = Value> ClassT<T> define_class(concepts::Identifier auto &&name);
|
1235
|
+
};
|
1236
|
+
|
1237
|
+
namespace detail {
|
1238
|
+
inline Ruby &unsafe_ruby() {
|
1239
|
+
static Ruby ruby = {};
|
1240
|
+
return ruby;
|
1241
|
+
}
|
1242
|
+
|
1243
|
+
inline Ruby &ruby() {
|
1244
|
+
// TODO: check gvl
|
1245
|
+
return unsafe_ruby();
|
1246
|
+
}
|
1247
|
+
}
|
1248
|
+
}
|
1249
|
+
|
1250
|
+
namespace std {
|
1251
|
+
template <std::derived_from<rcx::Value> T> struct formatter<T, char> {
|
1252
|
+
bool inspect = false;
|
1253
|
+
|
1254
|
+
template <typename ParseContext> constexpr ParseContext::iterator parse(ParseContext &ctx);
|
1255
|
+
template <typename FormatContext>
|
1256
|
+
FormatContext::iterator format(T value, FormatContext &ctx) const;
|
1257
|
+
};
|
1258
|
+
}
|