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.
@@ -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
+ }