rcx 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }