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,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
|
+
}
|