rice 4.0.4 → 4.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.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/Rakefile +1 -1
  4. data/include/rice/rice.hpp +2596 -1771
  5. data/include/rice/stl.hpp +1580 -271
  6. data/lib/mkmf-rice.rb +5 -2
  7. data/lib/version.rb +1 -1
  8. data/rice/Arg.hpp +6 -6
  9. data/rice/Arg.ipp +8 -9
  10. data/rice/Constructor.hpp +2 -2
  11. data/rice/Data_Object.ipp +69 -15
  12. data/rice/Data_Object_defn.hpp +1 -15
  13. data/rice/Data_Type.ipp +56 -86
  14. data/rice/Data_Type_defn.hpp +14 -17
  15. data/rice/Director.hpp +0 -1
  16. data/rice/Enum.ipp +31 -22
  17. data/rice/Exception.ipp +2 -3
  18. data/rice/Exception_defn.hpp +5 -5
  19. data/rice/HandlerRegistration.hpp +15 -0
  20. data/rice/Return.hpp +5 -4
  21. data/rice/Return.ipp +8 -3
  22. data/rice/detail/ExceptionHandler.hpp +8 -0
  23. data/rice/detail/ExceptionHandler.ipp +28 -0
  24. data/rice/detail/{Exception_Handler_defn.hpp → ExceptionHandler_defn.hpp} +17 -21
  25. data/rice/detail/HandlerRegistry.hpp +51 -0
  26. data/rice/detail/HandlerRegistry.ipp +20 -0
  27. data/rice/detail/InstanceRegistry.hpp +34 -0
  28. data/rice/detail/InstanceRegistry.ipp +50 -0
  29. data/rice/detail/MethodInfo.ipp +1 -1
  30. data/rice/detail/NativeAttribute.hpp +26 -15
  31. data/rice/detail/NativeAttribute.ipp +76 -47
  32. data/rice/detail/NativeFunction.hpp +60 -13
  33. data/rice/detail/NativeFunction.ipp +103 -85
  34. data/rice/detail/NativeIterator.hpp +49 -0
  35. data/rice/detail/NativeIterator.ipp +102 -0
  36. data/rice/detail/NativeRegistry.hpp +31 -0
  37. data/rice/detail/{method_data.ipp → NativeRegistry.ipp} +20 -16
  38. data/rice/detail/Registries.hpp +26 -0
  39. data/rice/detail/Registries.ipp +23 -0
  40. data/rice/detail/RubyFunction.hpp +6 -11
  41. data/rice/detail/RubyFunction.ipp +10 -22
  42. data/rice/detail/Type.hpp +1 -1
  43. data/rice/detail/Type.ipp +2 -2
  44. data/rice/detail/TypeRegistry.hpp +8 -11
  45. data/rice/detail/TypeRegistry.ipp +3 -28
  46. data/rice/detail/Wrapper.hpp +0 -2
  47. data/rice/detail/Wrapper.ipp +73 -23
  48. data/rice/detail/cpp_protect.hpp +93 -0
  49. data/rice/detail/default_allocation_func.ipp +1 -1
  50. data/rice/detail/from_ruby.ipp +206 -2
  51. data/rice/detail/to_ruby.ipp +39 -5
  52. data/rice/detail/to_ruby_defn.hpp +1 -1
  53. data/rice/forward_declares.ipp +6 -0
  54. data/rice/global_function.hpp +0 -4
  55. data/rice/global_function.ipp +0 -6
  56. data/rice/rice.hpp +29 -24
  57. data/rice/stl.hpp +6 -1
  58. data/test/embed_ruby.cpp +0 -15
  59. data/test/test_Array.cpp +20 -24
  60. data/test/test_Class.cpp +8 -47
  61. data/test/test_Constructor.cpp +0 -2
  62. data/test/test_Data_Object.cpp +25 -11
  63. data/test/test_Data_Type.cpp +124 -28
  64. data/test/test_Director.cpp +12 -13
  65. data/test/test_Enum.cpp +65 -26
  66. data/test/test_Inheritance.cpp +9 -9
  67. data/test/test_Iterator.cpp +134 -5
  68. data/test/test_Keep_Alive.cpp +7 -7
  69. data/test/test_Memory_Management.cpp +1 -1
  70. data/test/test_Module.cpp +25 -62
  71. data/test/test_Object.cpp +66 -3
  72. data/test/test_Ownership.cpp +12 -13
  73. data/test/test_Self.cpp +12 -13
  74. data/test/test_Stl_Map.cpp +696 -0
  75. data/test/test_Stl_Optional.cpp +3 -3
  76. data/test/test_Stl_Pair.cpp +38 -2
  77. data/test/test_Stl_Reference_Wrapper.cpp +102 -0
  78. data/test/test_Stl_SmartPointer.cpp +5 -5
  79. data/test/test_Stl_Unordered_Map.cpp +697 -0
  80. data/test/test_Stl_Variant.cpp +301 -0
  81. data/test/test_Stl_Vector.cpp +200 -41
  82. data/test/test_Struct.cpp +3 -3
  83. data/test/test_To_From_Ruby.cpp +6 -0
  84. data/test/test_Tracking.cpp +239 -0
  85. data/test/unittest.hpp +13 -4
  86. metadata +23 -13
  87. data/rice/detail/Exception_Handler.hpp +0 -8
  88. data/rice/detail/Exception_Handler.ipp +0 -28
  89. data/rice/detail/Iterator.hpp +0 -23
  90. data/rice/detail/Iterator.ipp +0 -47
  91. data/rice/detail/function_traits.hpp +0 -124
  92. data/rice/detail/method_data.hpp +0 -29
  93. data/rice/detail/rice_traits.hpp +0 -116
  94. data/rice/ruby_try_catch.hpp +0 -86
data/include/rice/stl.hpp CHANGED
@@ -49,6 +49,11 @@ namespace Rice::detail
49
49
  {
50
50
  }
51
51
 
52
+ bool is_convertible(VALUE value)
53
+ {
54
+ return rb_type(value) == RUBY_T_STRING;
55
+ }
56
+
52
57
  std::string convert(VALUE value)
53
58
  {
54
59
  if (value == Qnil && this->arg_ && this->arg_->hasDefaultValue())
@@ -70,6 +75,11 @@ namespace Rice::detail
70
75
  class From_Ruby<std::string*>
71
76
  {
72
77
  public:
78
+ bool is_convertible(VALUE value)
79
+ {
80
+ return rb_type(value) == RUBY_T_STRING;
81
+ }
82
+
73
83
  std::string* convert(VALUE value)
74
84
  {
75
85
  detail::protect(rb_check_type, value, (int)T_STRING);
@@ -91,6 +101,11 @@ namespace Rice::detail
91
101
  {
92
102
  }
93
103
 
104
+ bool is_convertible(VALUE value)
105
+ {
106
+ return rb_type(value) == RUBY_T_STRING;
107
+ }
108
+
94
109
  std::string& convert(VALUE value)
95
110
  {
96
111
  if (value == Qnil && this->arg_ && this->arg_->hasDefaultValue())
@@ -138,7 +153,7 @@ namespace Rice::detail
138
153
  std::vector<VALUE> args(2);
139
154
  args[0] = To_Ruby<T>().convert(data.real());
140
155
  args[1] = To_Ruby<T>().convert(data.imag());
141
- return protect(rb_funcall2, rb_mKernel, rb_intern("Complex"), (int)args.size(), (const VALUE*)args.data());
156
+ return protect(rb_funcallv, rb_mKernel, rb_intern("Complex"), (int)args.size(), (const VALUE*)args.data());
142
157
  }
143
158
  };
144
159
 
@@ -148,8 +163,8 @@ namespace Rice::detail
148
163
  public:
149
164
  std::complex<T> convert(VALUE value)
150
165
  {
151
- VALUE real = protect(rb_funcall2, value, rb_intern("real"), 0, (const VALUE*)nullptr);
152
- VALUE imaginary = protect(rb_funcall2, value, rb_intern("imaginary"), 0, (const VALUE*)nullptr);
166
+ VALUE real = protect(rb_funcallv, value, rb_intern("real"), 0, (const VALUE*)nullptr);
167
+ VALUE imaginary = protect(rb_funcallv, value, rb_intern("imaginary"), 0, (const VALUE*)nullptr);
153
168
 
154
169
  return std::complex<T>(From_Ruby<T>().convert(real), From_Ruby<T>().convert(imaginary));
155
170
  }
@@ -161,8 +176,8 @@ namespace Rice::detail
161
176
  public:
162
177
  std::complex<T>& convert(VALUE value)
163
178
  {
164
- VALUE real = protect(rb_funcall2, value, rb_intern("real"), 0, (const VALUE*)nullptr);
165
- VALUE imaginary = protect(rb_funcall2, value, rb_intern("imaginary"), 0, (const VALUE*)nullptr);
179
+ VALUE real = protect(rb_funcallv, value, rb_intern("real"), 0, (const VALUE*)nullptr);
180
+ VALUE imaginary = protect(rb_funcallv, value, rb_intern("imaginary"), 0, (const VALUE*)nullptr);
166
181
  this->converted_ = std::complex<T>(From_Ruby<T>().convert(real), From_Ruby<T>().convert(imaginary));
167
182
 
168
183
  return this->converted_;
@@ -273,178 +288,52 @@ namespace Rice::detail
273
288
  };
274
289
  }
275
290
 
276
- // ========= pair.hpp =========
277
-
278
-
279
- namespace Rice
280
- {
281
- template<typename T>
282
- Data_Type<T> define_pair(std::string name);
283
-
284
- template<typename T>
285
- Data_Type<T> define_pair_under(Object module, std::string name);
286
- }
287
-
291
+ // ========= reference_wrapper.hpp =========
288
292
 
289
- // --------- pair.ipp ---------
290
293
 
291
- #include <sstream>
292
- #include <stdexcept>
293
- #include <utility>
294
+ // --------- reference_wrapper.ipp ---------
295
+ #include <functional>
294
296
 
295
- namespace Rice
297
+ namespace Rice::detail
296
298
  {
297
- namespace stl
298
- {
299
- template<typename T>
300
- class PairHelper
301
- {
302
- public:
303
- PairHelper(Data_Type<T> klass) : klass_(klass)
304
- {
305
- this->define_constructor();
306
- this->define_copyable_methods();
307
- this->define_access_methods();
308
- this->define_modify_methods();
309
- this->define_to_s();
310
- }
311
-
312
- private:
313
- void define_constructor()
314
- {
315
- klass_.define_constructor(Constructor<T, typename T::first_type&, typename T::second_type&>());
316
- }
317
-
318
- void define_copyable_methods()
319
- {
320
- if constexpr (std::is_copy_constructible_v<typename T::first_type> && std::is_copy_constructible_v<typename T::second_type>)
321
- {
322
- klass_.define_method("copy", [](T& self) -> T
323
- {
324
- return self;
325
- });
326
- }
327
- else
328
- {
329
- klass_.define_method("copy", [](T& self) -> T
330
- {
331
- throw std::runtime_error("Cannot copy pair with non-copy constructible types");
332
- return self;
333
- });
334
- }
335
- }
336
-
337
- void define_access_methods()
338
- {
339
- // Access methods
340
- klass_.define_method("first", [](T& self) -> typename T::first_type&
341
- {
342
- return self.first;
343
- })
344
- .define_method("second", [](T& self) -> typename T::second_type&
345
- {
346
- return self.second;
347
- });
348
- }
349
-
350
- void define_modify_methods()
351
- {
352
- // Access methods
353
- klass_.define_method("first=", [](T& self, typename T::first_type& value) -> typename T::first_type&
354
- {
355
- self.first = value;
356
- return self.first;
357
- })
358
- .define_method("second=", [](T& self, typename T::second_type& value) -> typename T::second_type&
359
- {
360
- self.second = value;
361
- return self.second;
362
- });
363
- }
364
-
365
- void define_to_s()
366
- {
367
- if constexpr (detail::is_ostreamable_v<typename T::first_type> && detail::is_ostreamable_v<typename T::second_type>)
368
- {
369
- klass_.define_method("to_s", [](const T& self)
370
- {
371
- std::stringstream stream;
372
- stream << "[" << self.first << ", " << self.second << "]";
373
- return stream.str();
374
- });
375
- }
376
- else
377
- {
378
- klass_.define_method("to_s", [](const T& self)
379
- {
380
- return "[Not printable]";
381
- });
382
- }
383
- }
384
-
385
- private:
386
- Data_Type<T> klass_;
387
- };
388
- } // namespace
389
-
390
299
  template<typename T>
391
- Data_Type<T> define_pair_under(Object module, std::string name)
300
+ struct Type<std::reference_wrapper<T>>
392
301
  {
393
- if (detail::TypeRegistry::isDefined<T>())
302
+ constexpr static bool verify()
394
303
  {
395
- return Data_Type<T>(Data_Type<T>());
304
+ return Type<T>::verify();
396
305
  }
397
-
398
- Data_Type<T> result = define_class_under<detail::intrinsic_type<T>>(module, name.c_str());
399
- stl::PairHelper helper(result);
400
- return result;
401
- }
306
+ };
402
307
 
403
308
  template<typename T>
404
- Data_Type<T> define_pair(std::string name)
309
+ class To_Ruby<std::reference_wrapper<T>>
405
310
  {
406
- if (detail::TypeRegistry::isDefined<T>())
311
+ public:
312
+ VALUE convert(std::reference_wrapper<T>& data, bool takeOwnership = false)
407
313
  {
408
- return Data_Type<T>(Data_Type<T>());
314
+ return To_Ruby<T&>().convert(data.get());
409
315
  }
410
-
411
- Data_Type<T> result = define_class<detail::intrinsic_type<T>>(name.c_str());
412
- stl::PairHelper<T> helper(result);
413
- return result;
414
- }
316
+ };
415
317
 
416
318
  template<typename T>
417
- Data_Type<T> define_pair_auto()
418
- {
419
- std::string klassName = detail::makeClassName(typeid(T));
420
- Module rb_mRice = define_module("Rice");
421
- Module rb_mpair = define_module_under(rb_mRice, "Std");
422
- return define_pair_under<T>(rb_mpair, klassName);
423
- }
424
-
425
- namespace detail
319
+ class From_Ruby<std::reference_wrapper<T>>
426
320
  {
427
- template<typename T1, typename T2>
428
- struct Type<std::pair<T1, T2>>
321
+ public:
322
+ bool is_convertible(VALUE value)
429
323
  {
430
- static bool verify()
431
- {
432
- Type<T1>::verify();
433
- Type<T2>::verify();
324
+ return true;
325
+ }
434
326
 
435
- if (!detail::TypeRegistry::isDefined<std::pair<T1, T2>>())
436
- {
437
- define_pair_auto<std::pair<T1, T2>>();
438
- }
327
+ std::reference_wrapper<T> convert(VALUE value)
328
+ {
329
+ return this->converter_.convert(value);
330
+ }
439
331
 
440
- return true;
441
- }
442
- };
443
- }
332
+ private:
333
+ From_Ruby<T&> converter_;
334
+ };
444
335
  }
445
336
 
446
-
447
-
448
337
  // ========= smart_ptr.hpp =========
449
338
 
450
339
 
@@ -455,6 +344,7 @@ namespace Rice::detail
455
344
  {
456
345
  public:
457
346
  WrapperSmartPointer(SmartPointer_T<Arg_Ts...>& data);
347
+ ~WrapperSmartPointer();
458
348
  void* get() override;
459
349
  SmartPointer_T<Arg_Ts...>& data();
460
350
 
@@ -478,6 +368,12 @@ namespace Rice::detail
478
368
  {
479
369
  }
480
370
 
371
+ template <template <typename, typename...> typename SmartPointer_T, typename...Arg_Ts>
372
+ inline WrapperSmartPointer<SmartPointer_T, Arg_Ts...>::~WrapperSmartPointer()
373
+ {
374
+ Registries::instance.instances.remove(this->get());
375
+ }
376
+
481
377
  template <template <typename, typename...> typename SmartPointer_T, typename...Arg_Ts>
482
378
  inline void* WrapperSmartPointer<SmartPointer_T, Arg_Ts...>::get()
483
379
  {
@@ -497,7 +393,7 @@ namespace Rice::detail
497
393
  public:
498
394
  VALUE convert(std::unique_ptr<T>& data)
499
395
  {
500
- std::pair<VALUE, rb_data_type_t*> rubyTypeInfo = detail::TypeRegistry::figureType<T>(*data);
396
+ std::pair<VALUE, rb_data_type_t*> rubyTypeInfo = detail::Registries::instance.types.figureType<T>(*data);
501
397
 
502
398
  // Use custom wrapper type
503
399
  using Wrapper_T = WrapperSmartPointer<std::unique_ptr, T>;
@@ -511,7 +407,7 @@ namespace Rice::detail
511
407
  public:
512
408
  std::unique_ptr<T>& convert(VALUE value)
513
409
  {
514
- Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::rb_type());
410
+ Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::ruby_data_type());
515
411
 
516
412
  using Wrapper_T = WrapperSmartPointer<std::unique_ptr, T>;
517
413
  Wrapper_T* smartWrapper = dynamic_cast<Wrapper_T*>(wrapper);
@@ -540,7 +436,7 @@ namespace Rice::detail
540
436
  public:
541
437
  VALUE convert(std::shared_ptr<T>& data)
542
438
  {
543
- std::pair<VALUE, rb_data_type_t*> rubyTypeInfo = detail::TypeRegistry::figureType<T>(*data);
439
+ std::pair<VALUE, rb_data_type_t*> rubyTypeInfo = detail::Registries::instance.types.figureType<T>(*data);
544
440
 
545
441
  // Use custom wrapper type
546
442
  using Wrapper_T = WrapperSmartPointer<std::shared_ptr, T>;
@@ -554,7 +450,7 @@ namespace Rice::detail
554
450
  public:
555
451
  std::shared_ptr<T> convert(VALUE value)
556
452
  {
557
- Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::rb_type());
453
+ Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::ruby_data_type());
558
454
 
559
455
  using Wrapper_T = WrapperSmartPointer<std::shared_ptr, T>;
560
456
  Wrapper_T* smartWrapper = dynamic_cast<Wrapper_T*>(wrapper);
@@ -573,7 +469,7 @@ namespace Rice::detail
573
469
  public:
574
470
  std::shared_ptr<T>& convert(VALUE value)
575
471
  {
576
- Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::rb_type());
472
+ Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::ruby_data_type());
577
473
 
578
474
  using Wrapper_T = WrapperSmartPointer<std::shared_ptr, T>;
579
475
  Wrapper_T* smartWrapper = dynamic_cast<Wrapper_T*>(wrapper);
@@ -596,66 +492,1464 @@ namespace Rice::detail
596
492
  };
597
493
  }
598
494
 
599
- // ========= vector.hpp =========
495
+ // ========= monostate.hpp =========
600
496
 
601
497
 
602
- namespace Rice
498
+ // --------- monostate.ipp ---------
499
+ #include <variant>
500
+
501
+ namespace Rice::detail
603
502
  {
604
- template<typename T>
605
- Data_Type<T> define_vector(std::string name);
503
+ template<>
504
+ struct Type<std::monostate>
505
+ {
506
+ constexpr static bool verify()
507
+ {
508
+ return true;
509
+ }
510
+ };
606
511
 
607
- template<typename T>
608
- Data_Type<T> define_vector_under(Object module, std::string name);
512
+ template<>
513
+ class To_Ruby<std::monostate>
514
+ {
515
+ public:
516
+ VALUE convert(std::monostate& _)
517
+ {
518
+ return Qnil;
519
+ }
520
+ };
521
+
522
+ template<>
523
+ class To_Ruby<std::monostate&>
524
+ {
525
+ public:
526
+ static VALUE convert(std::monostate& data, bool takeOwnership = false)
527
+ {
528
+ return Qnil;
529
+ }
530
+ };
531
+
532
+ template<>
533
+ class From_Ruby<std::monostate>
534
+ {
535
+ public:
536
+ bool is_convertible(VALUE value)
537
+ {
538
+ return false;
539
+ }
540
+
541
+ std::monostate convert(VALUE value)
542
+ {
543
+ return std::monostate();
544
+ }
545
+ };
546
+
547
+ template<>
548
+ class From_Ruby<std::monostate&>
549
+ {
550
+ public:
551
+ bool is_convertible(VALUE value)
552
+ {
553
+ return false;
554
+ }
555
+
556
+ std::monostate& convert(VALUE value)
557
+ {
558
+ return this->converted_;
559
+ }
560
+
561
+ private:
562
+ std::monostate converted_ = std::monostate();
563
+ };
609
564
  }
610
565
 
566
+ // ========= variant.hpp =========
611
567
 
612
- // --------- vector.ipp ---------
613
568
 
614
- #include <sstream>
615
- #include <stdexcept>
616
- #include <vector>
569
+ // --------- variant.ipp ---------
570
+ #include <variant>
617
571
 
618
- namespace Rice
572
+ namespace Rice::detail
619
573
  {
620
- namespace stl
574
+ template<typename...Types>
575
+ struct Type<std::variant<Types...>>
621
576
  {
622
- template<typename T>
623
- class VectorHelper
577
+ using Tuple_T = std::tuple<Types...>;
578
+
579
+ template<std::size_t... I>
580
+ constexpr static bool verifyTypes(std::index_sequence<I...>& indices)
624
581
  {
625
- using Value_T = typename T::value_type;
626
- using Size_T = typename T::size_type;
627
- using Difference_T = typename T::difference_type;
582
+ return (Type<std::tuple_element_t<I, Tuple_T>>::verify() && ...);
583
+ }
628
584
 
629
- public:
630
- VectorHelper(Data_Type<T> klass) : klass_(klass)
631
- {
632
- this->define_constructor();
633
- this->define_copyable_methods();
634
- this->define_constructable_methods();
635
- this->define_capacity_methods();
636
- this->define_access_methods();
637
- this->define_comparable_methods();
638
- this->define_modify_methods();
639
- this->define_to_s();
640
- this->define_enumerable();
641
- }
585
+ template<std::size_t... I>
586
+ constexpr static bool verify()
587
+ {
588
+ auto indices = std::make_index_sequence<std::variant_size_v<std::variant<Types...>>>{};
589
+ return verifyTypes(indices);
590
+ }
591
+ };
642
592
 
643
- private:
593
+ template<typename...Types>
594
+ class To_Ruby<std::variant<Types...>>
595
+ {
596
+ public:
644
597
 
645
- // Helper method to translate Ruby indices to vector indices
646
- Difference_T normalizeIndex(Size_T size, Difference_T index, bool enforceBounds = false)
647
- {
648
- // Negative indices mean count from the right. Note that negative indices
649
- // wrap around!
650
- if (index < 0)
651
- {
652
- index = ((-index) % size);
653
- index = index > 0 ? size - index : index;
654
- }
598
+ template<typename T>
599
+ static VALUE convertElement(std::variant<Types...>& data, bool takeOwnership)
600
+ {
601
+ return To_Ruby<T>().convert(std::get<T>(data));
602
+ }
655
603
 
656
- if (enforceBounds && (index < 0 || index >= (Difference_T)size))
604
+ template<std::size_t... I>
605
+ static VALUE convertIterator(std::variant<Types...>& data, bool takeOwnership, std::index_sequence<I...>& indices)
606
+ {
607
+ // Create a tuple of the variant types so we can look over the tuple's types
608
+ using Tuple_T = std::tuple<Types...>;
609
+
610
+ /* This is a fold expression. In pseudo code:
611
+
612
+ for (type in variant.types)
657
613
  {
658
- throw std::out_of_range("Invalid index: " + std::to_string(index));
614
+ if (variant.has_value<type>())
615
+ return ToRuby<type>().convert(variant.getValue<type>)
616
+ }
617
+
618
+ The list of variant types is stored in Tuple_T. The number of types is stored in I.
619
+ Starting with index 0, get the variant type using td::tuple_element_t<I, Tuple_T>>.
620
+ Next check if the variant has a value for that type using std::holds_alternative<T>.
621
+ If yes, then call convertElement and save the return value to result. Then use the
622
+ comma operator to return true to the fold expression. If the variant does not have
623
+ a value for the type then return false.
624
+
625
+ The fold operator is or (||). If an index returns false, then the next index is evaulated
626
+ up until I.
627
+
628
+ Code inspired by https://www.foonathan.net/2020/05/fold-tricks/ */
629
+
630
+ VALUE result = Qnil;
631
+ ((std::holds_alternative<std::tuple_element_t<I, Tuple_T>>(data) ?
632
+ (result = convertElement<std::tuple_element_t<I, Tuple_T>>(data, takeOwnership), true) : false) || ...);
633
+
634
+ return result;
635
+ }
636
+
637
+ static VALUE convert(std::variant<Types...>& data, bool takeOwnership = false)
638
+ {
639
+ auto indices = std::make_index_sequence<std::variant_size_v<std::variant<Types...>>>{};
640
+ return convertIterator(data, takeOwnership, indices);
641
+ }
642
+ };
643
+
644
+ template<typename...Types>
645
+ class To_Ruby<std::variant<Types...>&>
646
+ {
647
+ public:
648
+ template<typename T>
649
+ static VALUE convertElement(std::variant<Types...>& data, bool takeOwnership)
650
+ {
651
+ return To_Ruby<T>().convert(std::get<T>(data));
652
+ }
653
+
654
+ template<std::size_t... I>
655
+ static VALUE convertIterator(std::variant<Types...>& data, bool takeOwnership, std::index_sequence<I...>& indices)
656
+ {
657
+ // Create a tuple of the variant types so we can look over the tuple's types
658
+ using Tuple_T = std::tuple<Types...>;
659
+
660
+ // See comments above for explanation of this code
661
+ VALUE result = Qnil;
662
+ ((std::holds_alternative<std::tuple_element_t<I, Tuple_T>>(data) ?
663
+ (result = convertElement<std::tuple_element_t<I, Tuple_T>>(data, takeOwnership), true) : false) || ...);
664
+
665
+ return result;
666
+ }
667
+
668
+ static VALUE convert(std::variant<Types...>& data, bool takeOwnership = false)
669
+ {
670
+ auto indices = std::make_index_sequence<std::variant_size_v<std::variant<Types...>>>{};
671
+ return convertIterator(data, takeOwnership, indices);
672
+ }
673
+ };
674
+
675
+ template<typename...Types>
676
+ class From_Ruby<std::variant<Types...>>
677
+ {
678
+ private:
679
+ // Possible converters we could use for this variant
680
+ using From_Ruby_Ts = std::tuple<From_Ruby<Types>...>;
681
+
682
+ public:
683
+ /* This method loops over each type in the variant, creates a From_Ruby converter,
684
+ and then check if the converter can work with the provided Rby value (it checks
685
+ the type of the Ruby object to see if it matches the variant type).
686
+ If yes, then the converter runs. If no, then the method recursively calls itself
687
+ increasing the index.
688
+
689
+ We use recursion, with a constexpr, to avoid having to instantiate an instance
690
+ of the variant to store results from a fold expression like the To_Ruby code
691
+ does above. That allows us to process variants with non default constructible
692
+ arguments like std::reference_wrapper. */
693
+ template <std::size_t I = 0>
694
+ std::variant<Types...> convertInternal(VALUE value)
695
+ {
696
+ // Loop over each possible type in the variant.
697
+ if constexpr (I < std::variant_size_v<std::variant<Types...>>)
698
+ {
699
+ // Get the converter for the current index
700
+ typename std::tuple_element_t<I, From_Ruby_Ts> converter;
701
+
702
+ // See if it will work
703
+ if (converter.is_convertible(value))
704
+ {
705
+ return converter.convert(value);
706
+ }
707
+ else
708
+ {
709
+ return convertInternal<I + 1>(value);
710
+ }
711
+ }
712
+ throw std::runtime_error("Could not find converter for variant");
713
+ }
714
+
715
+ std::variant<Types...> convert(VALUE value)
716
+ {
717
+ return convertInternal(value);
718
+ }
719
+ };
720
+
721
+ template<typename...Types>
722
+ class From_Ruby<std::variant<Types...>&>
723
+ {
724
+ private:
725
+ // Possible converters we could use for this variant
726
+ using From_Ruby_Ts = std::tuple<From_Ruby<Types>...>;
727
+
728
+ public:
729
+ template <std::size_t I = 0>
730
+ std::variant<Types...> convertInternal(VALUE value)
731
+ {
732
+ // Loop over each possible type in the variant
733
+ if constexpr (I < std::variant_size_v<std::variant<Types...>>)
734
+ {
735
+ // Get the converter for the current index
736
+ typename std::tuple_element_t<I, From_Ruby_Ts> converter;
737
+
738
+ // See if it will work
739
+ if (converter.is_convertible(value))
740
+ {
741
+ return converter.convert(value);
742
+ }
743
+ else
744
+ {
745
+ return convertInternal<I + 1>(value);
746
+ }
747
+ }
748
+ throw std::runtime_error("Could not find converter for variant");
749
+ }
750
+
751
+ std::variant<Types...> convert(VALUE value)
752
+ {
753
+ return convertInternal(value);
754
+ }
755
+ };
756
+ }
757
+
758
+ // ========= pair.hpp =========
759
+
760
+
761
+ namespace Rice
762
+ {
763
+ template<typename T>
764
+ Data_Type<T> define_pair(std::string name);
765
+
766
+ template<typename T>
767
+ Data_Type<T> define_pair_under(Object module, std::string name);
768
+ }
769
+
770
+
771
+ // --------- pair.ipp ---------
772
+
773
+ #include <sstream>
774
+ #include <stdexcept>
775
+ #include <utility>
776
+
777
+ namespace Rice
778
+ {
779
+ namespace stl
780
+ {
781
+ template<typename T>
782
+ class PairHelper
783
+ {
784
+ public:
785
+ PairHelper(Data_Type<T> klass) : klass_(klass)
786
+ {
787
+ this->define_constructor();
788
+ this->define_copyable_methods();
789
+ this->define_access_methods();
790
+ this->define_modify_methods();
791
+ this->define_to_s();
792
+ }
793
+
794
+ private:
795
+ void define_constructor()
796
+ {
797
+ klass_.define_constructor(Constructor<T, typename T::first_type&, typename T::second_type&>());
798
+ }
799
+
800
+ void define_copyable_methods()
801
+ {
802
+ if constexpr (std::is_copy_constructible_v<typename T::first_type> && std::is_copy_constructible_v<typename T::second_type>)
803
+ {
804
+ klass_.define_method("copy", [](T& pair) -> T
805
+ {
806
+ return pair;
807
+ });
808
+ }
809
+ else
810
+ {
811
+ klass_.define_method("copy", [](T& pair) -> T
812
+ {
813
+ throw std::runtime_error("Cannot copy pair with non-copy constructible types");
814
+ return pair;
815
+ });
816
+ }
817
+ }
818
+
819
+ void define_access_methods()
820
+ {
821
+ // Access methods
822
+ klass_.define_method("first", [](T& pair) -> typename T::first_type&
823
+ {
824
+ return pair.first;
825
+ })
826
+ .define_method("second", [](T& pair) -> typename T::second_type&
827
+ {
828
+ return pair.second;
829
+ });
830
+ }
831
+
832
+ void define_modify_methods()
833
+ {
834
+ // Access methods
835
+ klass_.define_method("first=", [](T& pair, typename T::first_type& value) -> typename T::first_type&
836
+ {
837
+ if constexpr (std::is_const_v<std::remove_reference_t<std::remove_pointer_t<typename T::first_type>>>)
838
+ {
839
+ throw std::runtime_error("Cannot set pair.first since it is a constant");
840
+ }
841
+ else
842
+ {
843
+ pair.first = value;
844
+ return pair.first;
845
+ }
846
+ })
847
+ .define_method("second=", [](T& pair, typename T::second_type& value) -> typename T::second_type&
848
+ {
849
+ if constexpr (std::is_const_v<std::remove_reference_t<std::remove_pointer_t<typename T::second_type>>>)
850
+ {
851
+ throw std::runtime_error("Cannot set pair.second since it is a constant");
852
+ }
853
+ else
854
+ {
855
+ pair.second = value;
856
+ return pair.second;
857
+ }
858
+ });
859
+ }
860
+
861
+ void define_to_s()
862
+ {
863
+ if constexpr (detail::is_ostreamable_v<typename T::first_type> && detail::is_ostreamable_v<typename T::second_type>)
864
+ {
865
+ klass_.define_method("to_s", [](const T& pair)
866
+ {
867
+ std::stringstream stream;
868
+ stream << "[" << pair.first << ", " << pair.second << "]";
869
+ return stream.str();
870
+ });
871
+ }
872
+ else
873
+ {
874
+ klass_.define_method("to_s", [](const T& pair)
875
+ {
876
+ return "[Not printable]";
877
+ });
878
+ }
879
+ }
880
+
881
+ private:
882
+ Data_Type<T> klass_;
883
+ };
884
+ } // namespace
885
+
886
+ template<typename T>
887
+ Data_Type<T> define_pair_under(Object module, std::string name)
888
+ {
889
+ if (detail::Registries::instance.types.isDefined<T>())
890
+ {
891
+ // If the pair has been previously seen it will be registered but may
892
+ // not be associated with the constant Module::<name>
893
+ module.const_set_maybe(name, Data_Type<T>().klass());
894
+ return Data_Type<T>();
895
+ }
896
+
897
+ Data_Type<T> result = define_class_under<detail::intrinsic_type<T>>(module, name.c_str());
898
+ stl::PairHelper helper(result);
899
+ return result;
900
+ }
901
+
902
+ template<typename T>
903
+ Data_Type<T> define_pair(std::string name)
904
+ {
905
+ if (detail::Registries::instance.types.isDefined<T>())
906
+ {
907
+ // If the pair has been previously seen it will be registered but may
908
+ // not be associated with the constant Object::<name>
909
+ Object(rb_cObject).const_set_maybe(name, Data_Type<T>().klass());
910
+ return Data_Type<T>();
911
+ }
912
+
913
+ Data_Type<T> result = define_class<detail::intrinsic_type<T>>(name.c_str());
914
+ stl::PairHelper<T> helper(result);
915
+ return result;
916
+ }
917
+
918
+ template<typename T>
919
+ Data_Type<T> define_pair_auto()
920
+ {
921
+ std::string klassName = detail::makeClassName(typeid(T));
922
+ Module rb_mRice = define_module("Rice");
923
+ Module rb_mpair = define_module_under(rb_mRice, "Std");
924
+ return define_pair_under<T>(rb_mpair, klassName);
925
+ }
926
+
927
+ namespace detail
928
+ {
929
+ template<typename T1, typename T2>
930
+ struct Type<std::pair<T1, T2>>
931
+ {
932
+ static bool verify()
933
+ {
934
+ detail::verifyType<T1>();
935
+ detail::verifyType<T2>();
936
+
937
+ if (!detail::Registries::instance.types.isDefined<std::pair<T1, T2>>())
938
+ {
939
+ define_pair_auto<std::pair<T1, T2>>();
940
+ }
941
+
942
+ return true;
943
+ }
944
+ };
945
+ }
946
+ }
947
+
948
+
949
+
950
+ // ========= map.hpp =========
951
+
952
+
953
+ namespace Rice
954
+ {
955
+ template<typename U>
956
+ Data_Type<U> define_map(std::string name);
957
+
958
+ template<typename U>
959
+ Data_Type<U> define_map_under(Object module, std::string name);
960
+ }
961
+
962
+
963
+ // --------- map.ipp ---------
964
+
965
+ #include <sstream>
966
+ #include <stdexcept>
967
+ #include <map>
968
+ #include <type_traits>
969
+ #include <variant>
970
+
971
+ namespace Rice
972
+ {
973
+ namespace stl
974
+ {
975
+ template<typename T>
976
+ class MapHelper
977
+ {
978
+ using Key_T = typename T::key_type;
979
+ using Mapped_T = typename T::mapped_type;
980
+ using Value_T = typename T::value_type;
981
+ using Size_T = typename T::size_type;
982
+ using Difference_T = typename T::difference_type;
983
+
984
+ public:
985
+ MapHelper(Data_Type<T> klass) : klass_(klass)
986
+ {
987
+ this->register_pair();
988
+ this->define_constructor();
989
+ this->define_copyable_methods();
990
+ this->define_capacity_methods();
991
+ this->define_access_methods();
992
+ this->define_comparable_methods();
993
+ this->define_modify_methods();
994
+ this->define_enumerable();
995
+ this->define_to_s();
996
+ this->define_to_hash();
997
+ }
998
+
999
+ private:
1000
+
1001
+ void register_pair()
1002
+ {
1003
+ define_pair_auto<Value_T>();
1004
+ }
1005
+
1006
+ void define_constructor()
1007
+ {
1008
+ klass_.define_constructor(Constructor<T>());
1009
+ }
1010
+
1011
+ void define_copyable_methods()
1012
+ {
1013
+ if constexpr (std::is_copy_constructible_v<Value_T>)
1014
+ {
1015
+ klass_.define_method("copy", [](T& map) -> T
1016
+ {
1017
+ return map;
1018
+ });
1019
+ }
1020
+ else
1021
+ {
1022
+ klass_.define_method("copy", [](T& map) -> T
1023
+ {
1024
+ throw std::runtime_error("Cannot copy maps with non-copy constructible types");
1025
+ return map;
1026
+ });
1027
+ }
1028
+ }
1029
+
1030
+ void define_capacity_methods()
1031
+ {
1032
+ klass_.define_method("empty?", &T::empty)
1033
+ .define_method("max_size", &T::max_size)
1034
+ .define_method("size", &T::size);
1035
+
1036
+ rb_define_alias(klass_, "count", "size");
1037
+ rb_define_alias(klass_, "length", "size");
1038
+ }
1039
+
1040
+ void define_access_methods()
1041
+ {
1042
+ // Access methods
1043
+ klass_.define_method("[]", [](const T& map, const Key_T& key) -> std::optional<Mapped_T>
1044
+ {
1045
+ auto iter = map.find(key);
1046
+
1047
+ if (iter != map.end())
1048
+ {
1049
+ return iter->second;
1050
+ }
1051
+ else
1052
+ {
1053
+ return std::nullopt;
1054
+ }
1055
+ })
1056
+ .define_method("include?", [](T& map, Key_T& key) -> bool
1057
+ {
1058
+ return map.find(key) != map.end();
1059
+ })
1060
+ .define_method("keys", [](T& map) -> std::vector<Key_T>
1061
+ {
1062
+ std::vector<Key_T> result;
1063
+ std::transform(map.begin(), map.end(), std::back_inserter(result),
1064
+ [](const auto& pair)
1065
+ {
1066
+ return pair.first;
1067
+ });
1068
+
1069
+ return result;
1070
+ })
1071
+ .define_method("values", [](T& map) -> std::vector<Mapped_T>
1072
+ {
1073
+ std::vector<Mapped_T> result;
1074
+ std::transform(map.begin(), map.end(), std::back_inserter(result),
1075
+ [](const auto& pair)
1076
+ {
1077
+ return pair.second;
1078
+ });
1079
+
1080
+ return result;
1081
+ });
1082
+
1083
+ rb_define_alias(klass_, "has_key", "include?");
1084
+ }
1085
+
1086
+ // Methods that require Value_T to support operator==
1087
+ void define_comparable_methods()
1088
+ {
1089
+ if constexpr (detail::is_comparable_v<Mapped_T>)
1090
+ {
1091
+ klass_.define_method("value?", [](T& map, Mapped_T& value) -> bool
1092
+ {
1093
+ auto it = std::find_if(map.begin(), map.end(),
1094
+ [&value](auto& pair)
1095
+ {
1096
+ return pair.second == value;
1097
+ });
1098
+
1099
+ return it != map.end();
1100
+ });
1101
+ }
1102
+ else
1103
+ {
1104
+ klass_.define_method("value?", [](T& map, Mapped_T& value) -> bool
1105
+ {
1106
+ return false;
1107
+ });
1108
+ }
1109
+
1110
+ rb_define_alias(klass_, "has_value", "value?");
1111
+ }
1112
+
1113
+ void define_modify_methods()
1114
+ {
1115
+ klass_.define_method("clear", &T::clear)
1116
+ .define_method("delete", [](T& map, Key_T& key) -> std::optional<Mapped_T>
1117
+ {
1118
+ auto iter = map.find(key);
1119
+
1120
+ if (iter != map.end())
1121
+ {
1122
+ Mapped_T result = iter->second;
1123
+ map.erase(iter);
1124
+ return result;
1125
+ }
1126
+ else
1127
+ {
1128
+ return std::nullopt;
1129
+ }
1130
+ })
1131
+ .define_method("[]=", [](T& map, Key_T key, Mapped_T value) -> Mapped_T
1132
+ {
1133
+ map[key] = value;
1134
+ return value;
1135
+ });
1136
+
1137
+ rb_define_alias(klass_, "store", "[]=");
1138
+ }
1139
+
1140
+ void define_enumerable()
1141
+ {
1142
+ // Add enumerable support
1143
+ klass_.template define_iterator<typename T::iterator (T::*)()>(&T::begin, &T::end);
1144
+ }
1145
+
1146
+ void define_to_hash()
1147
+ {
1148
+ // Add enumerable support
1149
+ klass_.define_method("to_h", [](T& map)
1150
+ {
1151
+ VALUE result = rb_hash_new();
1152
+ std::for_each(map.begin(), map.end(), [&result](const typename T::reference pair)
1153
+ {
1154
+ VALUE key = detail::To_Ruby<Key_T&>().convert(pair.first);
1155
+ VALUE value = detail::To_Ruby<Mapped_T&>().convert(pair.second);
1156
+ rb_hash_aset(result, key, value);
1157
+ });
1158
+
1159
+ return result;
1160
+ }, Return().setValue());
1161
+ }
1162
+
1163
+ void define_to_s()
1164
+ {
1165
+ if constexpr (detail::is_ostreamable_v<Key_T> && detail::is_ostreamable_v<Mapped_T>)
1166
+ {
1167
+ klass_.define_method("to_s", [](const T& map)
1168
+ {
1169
+ auto iter = map.begin();
1170
+
1171
+ std::stringstream stream;
1172
+ stream << "{";
1173
+
1174
+ for (; iter != map.end(); iter++)
1175
+ {
1176
+ if (iter != map.begin())
1177
+ {
1178
+ stream << ", ";
1179
+ }
1180
+ stream << iter->first << " => " << iter->second;
1181
+ }
1182
+
1183
+ stream << "}";
1184
+ return stream.str();
1185
+ });
1186
+ }
1187
+ else
1188
+ {
1189
+ klass_.define_method("to_s", [](const T& map)
1190
+ {
1191
+ return "[Not printable]";
1192
+ });
1193
+ }
1194
+ }
1195
+
1196
+ private:
1197
+ Data_Type<T> klass_;
1198
+ };
1199
+ } // namespace
1200
+
1201
+ template<typename T>
1202
+ Data_Type<T> define_map_under(Object module, std::string name)
1203
+ {
1204
+ if (detail::Registries::instance.types.isDefined<T>())
1205
+ {
1206
+ // If the map has been previously seen it will be registered but may
1207
+ // not be associated with the constant Module::<name>
1208
+ module.const_set_maybe(name, Data_Type<T>().klass());
1209
+ return Data_Type<T>();
1210
+ }
1211
+
1212
+ Data_Type<T> result = define_class_under<detail::intrinsic_type<T>>(module, name.c_str());
1213
+ stl::MapHelper helper(result);
1214
+ return result;
1215
+ }
1216
+
1217
+ template<typename T>
1218
+ Data_Type<T> define_map(std::string name)
1219
+ {
1220
+ if (detail::Registries::instance.types.isDefined<T>())
1221
+ {
1222
+ // If the map has been previously seen it will be registered but may
1223
+ // not be associated with the constant Object::<name>
1224
+ Object(rb_cObject).const_set_maybe(name, Data_Type<T>().klass());
1225
+ return Data_Type<T>();
1226
+ }
1227
+
1228
+ Data_Type<T> result = define_class<detail::intrinsic_type<T>>(name.c_str());
1229
+ stl::MapHelper<T> helper(result);
1230
+ return result;
1231
+ }
1232
+
1233
+ template<typename T>
1234
+ Data_Type<T> define_map_auto()
1235
+ {
1236
+ std::string klassName = detail::makeClassName(typeid(T));
1237
+ Module rb_mRice = define_module("Rice");
1238
+ Module rb_mmap = define_module_under(rb_mRice, "Std");
1239
+ return define_map_under<T>(rb_mmap, klassName);
1240
+ }
1241
+
1242
+ namespace detail
1243
+ {
1244
+ template<typename T, typename U>
1245
+ struct Type<std::map<T, U>>
1246
+ {
1247
+ static bool verify()
1248
+ {
1249
+ Type<T>::verify();
1250
+ Type<U>::verify();
1251
+
1252
+ if (!detail::Registries::instance.types.isDefined<std::map<T, U>>())
1253
+ {
1254
+ define_map_auto<std::map<T, U>>();
1255
+ }
1256
+
1257
+ return true;
1258
+ }
1259
+ };
1260
+
1261
+ template<typename T, typename U>
1262
+ struct MapFromHash
1263
+ {
1264
+ static int convertPair(VALUE key, VALUE value, VALUE user_data)
1265
+ {
1266
+ std::map<T, U>* result = (std::map<T, U>*)(user_data);
1267
+
1268
+ // This method is being called from Ruby so we cannot let any C++
1269
+ // exceptions propogate back to Ruby
1270
+ return cpp_protect([&]
1271
+ {
1272
+ result->operator[](From_Ruby<T>().convert(key)) = From_Ruby<U>().convert(value);
1273
+ return ST_CONTINUE;
1274
+ });
1275
+ }
1276
+
1277
+ static std::map<T, U> convert(VALUE value)
1278
+ {
1279
+ std::map<T, U> result;
1280
+ VALUE user_data = (VALUE)(&result);
1281
+
1282
+ // MSVC needs help here, but g++ does not
1283
+ using Rb_Hash_ForEach_T = void(*)(VALUE, int(*)(VALUE, VALUE, VALUE), VALUE);
1284
+ detail::protect<Rb_Hash_ForEach_T>(rb_hash_foreach, value, convertPair, user_data);
1285
+
1286
+ return result;
1287
+ }
1288
+ };
1289
+
1290
+ template<typename T, typename U>
1291
+ class From_Ruby<std::map<T, U>>
1292
+ {
1293
+ public:
1294
+ From_Ruby() = default;
1295
+
1296
+ explicit From_Ruby(Arg * arg) : arg_(arg)
1297
+ {
1298
+ }
1299
+
1300
+ std::map<T, U> convert(VALUE value)
1301
+ {
1302
+ switch (rb_type(value))
1303
+ {
1304
+ case T_DATA:
1305
+ {
1306
+ // This is a wrapped map (hopefully!)
1307
+ return *Data_Object<std::map<T, U>>::from_ruby(value);
1308
+ }
1309
+ case T_HASH:
1310
+ {
1311
+ // If this an Ruby hash and the mapped type is copyable
1312
+ if constexpr (std::is_default_constructible_v<U>)
1313
+ {
1314
+ return MapFromHash<T, U>::convert(value);
1315
+ }
1316
+ }
1317
+ case T_NIL:
1318
+ {
1319
+ if (this->arg_ && this->arg_->hasDefaultValue())
1320
+ {
1321
+ return this->arg_->template defaultValue<std::map<T, U>>();
1322
+ }
1323
+ }
1324
+ default:
1325
+ {
1326
+ throw Exception(rb_eTypeError, "wrong argument type %s (expected % s)",
1327
+ detail::protect(rb_obj_classname, value), "std::map");
1328
+ }
1329
+ }
1330
+ }
1331
+
1332
+ private:
1333
+ Arg* arg_ = nullptr;
1334
+ };
1335
+
1336
+ template<typename T, typename U>
1337
+ class From_Ruby<std::map<T, U>&>
1338
+ {
1339
+ public:
1340
+ From_Ruby() = default;
1341
+
1342
+ explicit From_Ruby(Arg * arg) : arg_(arg)
1343
+ {
1344
+ }
1345
+
1346
+ std::map<T, U>& convert(VALUE value)
1347
+ {
1348
+ switch (rb_type(value))
1349
+ {
1350
+ case T_DATA:
1351
+ {
1352
+ // This is a wrapped map (hopefully!)
1353
+ return *Data_Object<std::map<T, U>>::from_ruby(value);
1354
+ }
1355
+ case T_HASH:
1356
+ {
1357
+ // If this an Ruby array and the map type is copyable
1358
+ if constexpr (std::is_default_constructible_v<std::map<T, U>>)
1359
+ {
1360
+ this->converted_ = MapFromHash<T, U>::convert(value);
1361
+ return this->converted_;
1362
+ }
1363
+ }
1364
+ case T_NIL:
1365
+ {
1366
+ if (this->arg_ && this->arg_->hasDefaultValue())
1367
+ {
1368
+ return this->arg_->template defaultValue<std::map<T, U>>();
1369
+ }
1370
+ }
1371
+ default:
1372
+ {
1373
+ throw Exception(rb_eTypeError, "wrong argument type %s (expected % s)",
1374
+ detail::protect(rb_obj_classname, value), "std::map");
1375
+ }
1376
+ }
1377
+ }
1378
+
1379
+ private:
1380
+ Arg* arg_ = nullptr;
1381
+ std::map<T, U> converted_;
1382
+ };
1383
+
1384
+ template<typename T, typename U>
1385
+ class From_Ruby<std::map<T, U>*>
1386
+ {
1387
+ public:
1388
+ std::map<T, U>* convert(VALUE value)
1389
+ {
1390
+ switch (rb_type(value))
1391
+ {
1392
+ case T_DATA:
1393
+ {
1394
+ // This is a wrapped map (hopefully!)
1395
+ return Data_Object<std::map<T, U>>::from_ruby(value);
1396
+ }
1397
+ case T_HASH:
1398
+ {
1399
+ // If this an Ruby array and the map type is copyable
1400
+ if constexpr (std::is_default_constructible_v<U>)
1401
+ {
1402
+ this->converted_ = MapFromHash<T, U>::convert(value);
1403
+ return &this->converted_;
1404
+ }
1405
+ }
1406
+ default:
1407
+ {
1408
+ throw Exception(rb_eTypeError, "wrong argument type %s (expected % s)",
1409
+ detail::protect(rb_obj_classname, value), "std::map");
1410
+ }
1411
+ }
1412
+ }
1413
+
1414
+ private:
1415
+ std::map<T, U> converted_;
1416
+ };
1417
+ }
1418
+ }
1419
+
1420
+ // ========= unordered_map.hpp =========
1421
+
1422
+
1423
+ namespace Rice
1424
+ {
1425
+ template<typename U>
1426
+ Data_Type<U> define_unordered_map(std::string name);
1427
+
1428
+ template<typename U>
1429
+ Data_Type<U> define_unordered_map_under(Object module, std::string name);
1430
+ }
1431
+
1432
+
1433
+ // --------- unordered_map.ipp ---------
1434
+
1435
+ #include <sstream>
1436
+ #include <stdexcept>
1437
+ #include <type_traits>
1438
+ #include <unordered_map>
1439
+ #include <variant>
1440
+
1441
+ namespace Rice
1442
+ {
1443
+ namespace stl
1444
+ {
1445
+ template<typename T>
1446
+ class UnorderedMapHelper
1447
+ {
1448
+ using Key_T = typename T::key_type;
1449
+ using Mapped_T = typename T::mapped_type;
1450
+ using Value_T = typename T::value_type;
1451
+ using Size_T = typename T::size_type;
1452
+ using Difference_T = typename T::difference_type;
1453
+
1454
+ public:
1455
+ UnorderedMapHelper(Data_Type<T> klass) : klass_(klass)
1456
+ {
1457
+ this->register_pair();
1458
+ this->define_constructor();
1459
+ this->define_copyable_methods();
1460
+ this->define_capacity_methods();
1461
+ this->define_access_methods();
1462
+ this->define_comparable_methods();
1463
+ this->define_modify_methods();
1464
+ this->define_enumerable();
1465
+ this->define_to_s();
1466
+ this->define_to_hash();
1467
+ }
1468
+
1469
+ private:
1470
+
1471
+ void register_pair()
1472
+ {
1473
+ define_pair_auto<Value_T>();
1474
+ }
1475
+
1476
+ void define_constructor()
1477
+ {
1478
+ klass_.define_constructor(Constructor<T>());
1479
+ }
1480
+
1481
+ void define_copyable_methods()
1482
+ {
1483
+ if constexpr (std::is_copy_constructible_v<Value_T>)
1484
+ {
1485
+ klass_.define_method("copy", [](T& unordered_map) -> T
1486
+ {
1487
+ return unordered_map;
1488
+ });
1489
+ }
1490
+ else
1491
+ {
1492
+ klass_.define_method("copy", [](T& unordered_map) -> T
1493
+ {
1494
+ throw std::runtime_error("Cannot copy unordered_maps with non-copy constructible types");
1495
+ return unordered_map;
1496
+ });
1497
+ }
1498
+ }
1499
+
1500
+ void define_capacity_methods()
1501
+ {
1502
+ klass_.define_method("empty?", &T::empty)
1503
+ .define_method("max_size", &T::max_size)
1504
+ .define_method("size", &T::size);
1505
+
1506
+ rb_define_alias(klass_, "count", "size");
1507
+ rb_define_alias(klass_, "length", "size");
1508
+ }
1509
+
1510
+ void define_access_methods()
1511
+ {
1512
+ // Access methods
1513
+ klass_.define_method("[]", [](const T& unordered_map, const Key_T& key) -> std::optional<Mapped_T>
1514
+ {
1515
+ auto iter = unordered_map.find(key);
1516
+
1517
+ if (iter != unordered_map.end())
1518
+ {
1519
+ return iter->second;
1520
+ }
1521
+ else
1522
+ {
1523
+ return std::nullopt;
1524
+ }
1525
+ })
1526
+ .define_method("include?", [](T& unordered_map, Key_T& key) -> bool
1527
+ {
1528
+ return unordered_map.find(key) != unordered_map.end();
1529
+ })
1530
+ .define_method("keys", [](T& unordered_map) -> std::vector<Key_T>
1531
+ {
1532
+ std::vector<Key_T> result;
1533
+ std::transform(unordered_map.begin(), unordered_map.end(), std::back_inserter(result),
1534
+ [](const auto& pair)
1535
+ {
1536
+ return pair.first;
1537
+ });
1538
+
1539
+ return result;
1540
+ })
1541
+ .define_method("values", [](T& unordered_map) -> std::vector<Mapped_T>
1542
+ {
1543
+ std::vector<Mapped_T> result;
1544
+ std::transform(unordered_map.begin(), unordered_map.end(), std::back_inserter(result),
1545
+ [](const auto& pair)
1546
+ {
1547
+ return pair.second;
1548
+ });
1549
+
1550
+ return result;
1551
+ });
1552
+
1553
+ rb_define_alias(klass_, "has_key", "include?");
1554
+ }
1555
+
1556
+ // Methods that require Value_T to support operator==
1557
+ void define_comparable_methods()
1558
+ {
1559
+ if constexpr (detail::is_comparable_v<Mapped_T>)
1560
+ {
1561
+ klass_.define_method("value?", [](T& unordered_map, Mapped_T& value) -> bool
1562
+ {
1563
+ auto it = std::find_if(unordered_map.begin(), unordered_map.end(),
1564
+ [&value](auto& pair)
1565
+ {
1566
+ return pair.second == value;
1567
+ });
1568
+
1569
+ return it != unordered_map.end();
1570
+ });
1571
+ }
1572
+ else
1573
+ {
1574
+ klass_.define_method("value?", [](T& unordered_map, Mapped_T& value) -> bool
1575
+ {
1576
+ return false;
1577
+ });
1578
+ }
1579
+
1580
+ rb_define_alias(klass_, "has_value", "value?");
1581
+ }
1582
+
1583
+ void define_modify_methods()
1584
+ {
1585
+ klass_.define_method("clear", &T::clear)
1586
+ .define_method("delete", [](T& unordered_map, Key_T& key) -> std::optional<Mapped_T>
1587
+ {
1588
+ auto iter = unordered_map.find(key);
1589
+
1590
+ if (iter != unordered_map.end())
1591
+ {
1592
+ Mapped_T result = iter->second;
1593
+ unordered_map.erase(iter);
1594
+ return result;
1595
+ }
1596
+ else
1597
+ {
1598
+ return std::nullopt;
1599
+ }
1600
+ })
1601
+ .define_method("[]=", [](T& unordered_map, Key_T key, Mapped_T value) -> Mapped_T
1602
+ {
1603
+ unordered_map[key] = value;
1604
+ return value;
1605
+ });
1606
+
1607
+ rb_define_alias(klass_, "store", "[]=");
1608
+ }
1609
+
1610
+ void define_enumerable()
1611
+ {
1612
+ // Add enumerable support
1613
+ klass_.template define_iterator<typename T::iterator (T::*)()>(&T::begin, &T::end);
1614
+ }
1615
+
1616
+ void define_to_hash()
1617
+ {
1618
+ // Add enumerable support
1619
+ klass_.define_method("to_h", [](T& unordered_map)
1620
+ {
1621
+ VALUE result = rb_hash_new();
1622
+ std::for_each(unordered_map.begin(), unordered_map.end(), [&result](const auto& pair)
1623
+ {
1624
+ VALUE key = detail::To_Ruby<Key_T>().convert(pair.first);
1625
+ VALUE value = detail::To_Ruby<Mapped_T>().convert(pair.second);
1626
+ rb_hash_aset(result, key, value);
1627
+ });
1628
+
1629
+ return result;
1630
+ }, Return().setValue());
1631
+ }
1632
+
1633
+ void define_to_s()
1634
+ {
1635
+ if constexpr (detail::is_ostreamable_v<Key_T> && detail::is_ostreamable_v<Mapped_T>)
1636
+ {
1637
+ klass_.define_method("to_s", [](const T& unordered_map)
1638
+ {
1639
+ auto iter = unordered_map.begin();
1640
+
1641
+ std::stringstream stream;
1642
+ stream << "{";
1643
+
1644
+ for (; iter != unordered_map.end(); iter++)
1645
+ {
1646
+ if (iter != unordered_map.begin())
1647
+ {
1648
+ stream << ", ";
1649
+ }
1650
+ stream << iter->first << " => " << iter->second;
1651
+ }
1652
+
1653
+ stream << "}";
1654
+ return stream.str();
1655
+ });
1656
+ }
1657
+ else
1658
+ {
1659
+ klass_.define_method("to_s", [](const T& unordered_map)
1660
+ {
1661
+ return "[Not printable]";
1662
+ });
1663
+ }
1664
+ }
1665
+
1666
+ private:
1667
+ Data_Type<T> klass_;
1668
+ };
1669
+ } // namespace
1670
+
1671
+ template<typename T>
1672
+ Data_Type<T> define_unordered_map_under(Object module, std::string name)
1673
+ {
1674
+ if (detail::Registries::instance.types.isDefined<T>())
1675
+ {
1676
+ // If the unordered_map has been previously seen it will be registered but may
1677
+ // not be associated with the constant Module::<name>
1678
+ module.const_set_maybe(name, Data_Type<T>().klass());
1679
+ return Data_Type<T>();
1680
+ }
1681
+
1682
+ Data_Type<T> result = define_class_under<detail::intrinsic_type<T>>(module, name.c_str());
1683
+ stl::UnorderedMapHelper helper(result);
1684
+ return result;
1685
+ }
1686
+
1687
+ template<typename T>
1688
+ Data_Type<T> define_unordered_map(std::string name)
1689
+ {
1690
+ if (detail::Registries::instance.types.isDefined<T>())
1691
+ {
1692
+ // If the unordered_map has been previously seen it will be registered but may
1693
+ // not be associated with the constant Object::<name>
1694
+ Object(rb_cObject).const_set_maybe(name, Data_Type<T>().klass());
1695
+ return Data_Type<T>();
1696
+ }
1697
+
1698
+ Data_Type<T> result = define_class<detail::intrinsic_type<T>>(name.c_str());
1699
+ stl::UnorderedMapHelper<T> helper(result);
1700
+ return result;
1701
+ }
1702
+
1703
+ template<typename T>
1704
+ Data_Type<T> define_unordered_map_auto()
1705
+ {
1706
+ std::string klassName = detail::makeClassName(typeid(T));
1707
+ Module rb_mRice = define_module("Rice");
1708
+ Module rb_munordered_map = define_module_under(rb_mRice, "Std");
1709
+ return define_unordered_map_under<T>(rb_munordered_map, klassName);
1710
+ }
1711
+
1712
+ namespace detail
1713
+ {
1714
+ template<typename T, typename U>
1715
+ struct Type<std::unordered_map<T, U>>
1716
+ {
1717
+ static bool verify()
1718
+ {
1719
+ Type<T>::verify();
1720
+ Type<U>::verify();
1721
+
1722
+ if (!detail::Registries::instance.types.isDefined<std::unordered_map<T, U>>())
1723
+ {
1724
+ define_unordered_map_auto<std::unordered_map<T, U>>();
1725
+ }
1726
+
1727
+ return true;
1728
+ }
1729
+ };
1730
+
1731
+ template<typename T, typename U>
1732
+ struct UnorderedMapFromHash
1733
+ {
1734
+ static int convertPair(VALUE key, VALUE value, VALUE user_data)
1735
+ {
1736
+ std::unordered_map<T, U>* result = (std::unordered_map<T, U>*)(user_data);
1737
+
1738
+ // This method is being called from Ruby so we cannot let any C++
1739
+ // exceptions propogate back to Ruby
1740
+ return cpp_protect([&]
1741
+ {
1742
+ result->operator[](From_Ruby<T>().convert(key)) = From_Ruby<U>().convert(value);
1743
+ return ST_CONTINUE;
1744
+ });
1745
+ }
1746
+
1747
+ static std::unordered_map<T, U> convert(VALUE value)
1748
+ {
1749
+ std::unordered_map<T, U> result;
1750
+ VALUE user_data = (VALUE)(&result);
1751
+
1752
+ // MSVC needs help here, but g++ does not
1753
+ using Rb_Hash_ForEach_T = void(*)(VALUE, int(*)(VALUE, VALUE, VALUE), VALUE);
1754
+ detail::protect<Rb_Hash_ForEach_T>(rb_hash_foreach, value, convertPair, user_data);
1755
+
1756
+ return result;
1757
+ }
1758
+ };
1759
+
1760
+ template<typename T, typename U>
1761
+ class From_Ruby<std::unordered_map<T, U>>
1762
+ {
1763
+ public:
1764
+ From_Ruby() = default;
1765
+
1766
+ explicit From_Ruby(Arg * arg) : arg_(arg)
1767
+ {
1768
+ }
1769
+
1770
+ std::unordered_map<T, U> convert(VALUE value)
1771
+ {
1772
+ switch (rb_type(value))
1773
+ {
1774
+ case T_DATA:
1775
+ {
1776
+ // This is a wrapped unordered_map (hopefully!)
1777
+ return *Data_Object<std::unordered_map<T, U>>::from_ruby(value);
1778
+ }
1779
+ case T_HASH:
1780
+ {
1781
+ // If this an Ruby hash and the unordered_mapped type is copyable
1782
+ if constexpr (std::is_default_constructible_v<U>)
1783
+ {
1784
+ return UnorderedMapFromHash<T, U>::convert(value);
1785
+ }
1786
+ }
1787
+ case T_NIL:
1788
+ {
1789
+ if (this->arg_ && this->arg_->hasDefaultValue())
1790
+ {
1791
+ return this->arg_->template defaultValue<std::unordered_map<T, U>>();
1792
+ }
1793
+ }
1794
+ default:
1795
+ {
1796
+ throw Exception(rb_eTypeError, "wrong argument type %s (expected % s)",
1797
+ detail::protect(rb_obj_classname, value), "std::unordered_map");
1798
+ }
1799
+ }
1800
+ }
1801
+
1802
+ private:
1803
+ Arg* arg_ = nullptr;
1804
+ };
1805
+
1806
+ template<typename T, typename U>
1807
+ class From_Ruby<std::unordered_map<T, U>&>
1808
+ {
1809
+ public:
1810
+ From_Ruby() = default;
1811
+
1812
+ explicit From_Ruby(Arg * arg) : arg_(arg)
1813
+ {
1814
+ }
1815
+
1816
+ std::unordered_map<T, U>& convert(VALUE value)
1817
+ {
1818
+ switch (rb_type(value))
1819
+ {
1820
+ case T_DATA:
1821
+ {
1822
+ // This is a wrapped unordered_map (hopefully!)
1823
+ return *Data_Object<std::unordered_map<T, U>>::from_ruby(value);
1824
+ }
1825
+ case T_HASH:
1826
+ {
1827
+ // If this an Ruby array and the unordered_map type is copyable
1828
+ if constexpr (std::is_default_constructible_v<std::unordered_map<T, U>>)
1829
+ {
1830
+ this->converted_ = UnorderedMapFromHash<T, U>::convert(value);
1831
+ return this->converted_;
1832
+ }
1833
+ }
1834
+ case T_NIL:
1835
+ {
1836
+ if (this->arg_ && this->arg_->hasDefaultValue())
1837
+ {
1838
+ return this->arg_->template defaultValue<std::unordered_map<T, U>>();
1839
+ }
1840
+ }
1841
+ default:
1842
+ {
1843
+ throw Exception(rb_eTypeError, "wrong argument type %s (expected % s)",
1844
+ detail::protect(rb_obj_classname, value), "std::unordered_map");
1845
+ }
1846
+ }
1847
+ }
1848
+
1849
+ private:
1850
+ Arg* arg_ = nullptr;
1851
+ std::unordered_map<T, U> converted_;
1852
+ };
1853
+
1854
+ template<typename T, typename U>
1855
+ class From_Ruby<std::unordered_map<T, U>*>
1856
+ {
1857
+ public:
1858
+ std::unordered_map<T, U>* convert(VALUE value)
1859
+ {
1860
+ switch (rb_type(value))
1861
+ {
1862
+ case T_DATA:
1863
+ {
1864
+ // This is a wrapped unordered_map (hopefully!)
1865
+ return Data_Object<std::unordered_map<T, U>>::from_ruby(value);
1866
+ }
1867
+ case T_HASH:
1868
+ {
1869
+ // If this an Ruby array and the unordered_map type is copyable
1870
+ if constexpr (std::is_default_constructible_v<U>)
1871
+ {
1872
+ this->converted_ = UnorderedMapFromHash<T, U>::convert(value);
1873
+ return &this->converted_;
1874
+ }
1875
+ }
1876
+ default:
1877
+ {
1878
+ throw Exception(rb_eTypeError, "wrong argument type %s (expected % s)",
1879
+ detail::protect(rb_obj_classname, value), "std::unordered_map");
1880
+ }
1881
+ }
1882
+ }
1883
+
1884
+ private:
1885
+ std::unordered_map<T, U> converted_;
1886
+ };
1887
+ }
1888
+ }
1889
+
1890
+ // ========= vector.hpp =========
1891
+
1892
+
1893
+ namespace Rice
1894
+ {
1895
+ template<typename T>
1896
+ Data_Type<T> define_vector(std::string name);
1897
+
1898
+ template<typename T>
1899
+ Data_Type<T> define_vector_under(Object module, std::string name);
1900
+ }
1901
+
1902
+
1903
+ // --------- vector.ipp ---------
1904
+
1905
+ #include <sstream>
1906
+ #include <stdexcept>
1907
+ #include <type_traits>
1908
+ #include <vector>
1909
+ #include <variant>
1910
+
1911
+ namespace Rice
1912
+ {
1913
+ namespace stl
1914
+ {
1915
+ template<typename T>
1916
+ class VectorHelper
1917
+ {
1918
+ using Value_T = typename T::value_type;
1919
+ using Size_T = typename T::size_type;
1920
+ using Difference_T = typename T::difference_type;
1921
+
1922
+ public:
1923
+ VectorHelper(Data_Type<T> klass) : klass_(klass)
1924
+ {
1925
+ this->define_constructor();
1926
+ this->define_copyable_methods();
1927
+ this->define_constructable_methods();
1928
+ this->define_capacity_methods();
1929
+ this->define_access_methods();
1930
+ this->define_comparable_methods();
1931
+ this->define_modify_methods();
1932
+ this->define_enumerable();
1933
+ this->define_to_array();
1934
+ this->define_to_s();
1935
+ }
1936
+
1937
+ private:
1938
+
1939
+ // Helper method to translate Ruby indices to vector indices
1940
+ Difference_T normalizeIndex(Size_T size, Difference_T index, bool enforceBounds = false)
1941
+ {
1942
+ // Negative indices mean count from the right. Note that negative indices
1943
+ // wrap around!
1944
+ if (index < 0)
1945
+ {
1946
+ index = ((-index) % size);
1947
+ index = index > 0 ? size - index : index;
1948
+ }
1949
+
1950
+ if (enforceBounds && (index < 0 || index >= (Difference_T)size))
1951
+ {
1952
+ throw std::out_of_range("Invalid index: " + std::to_string(index));
659
1953
  }
660
1954
 
661
1955
  return index;
@@ -670,17 +1964,17 @@ namespace Rice
670
1964
  {
671
1965
  if constexpr (std::is_copy_constructible_v<Value_T>)
672
1966
  {
673
- klass_.define_method("copy", [](T& self) -> T
1967
+ klass_.define_method("copy", [](T& vector) -> T
674
1968
  {
675
- return self;
1969
+ return vector;
676
1970
  });
677
1971
  }
678
1972
  else
679
1973
  {
680
- klass_.define_method("copy", [](T& self) -> T
1974
+ klass_.define_method("copy", [](T& vector) -> T
681
1975
  {
682
1976
  throw std::runtime_error("Cannot copy vectors with non-copy constructible types");
683
- return self;
1977
+ return vector;
684
1978
  });
685
1979
  }
686
1980
  }
@@ -693,7 +1987,7 @@ namespace Rice
693
1987
  }
694
1988
  else
695
1989
  {
696
- klass_.define_method("resize", [](const T& self, Size_T newSize)
1990
+ klass_.define_method("resize", [](const T& vector, Size_T newSize)
697
1991
  {
698
1992
  // Do nothing
699
1993
  });
@@ -717,38 +2011,38 @@ namespace Rice
717
2011
  void define_access_methods()
718
2012
  {
719
2013
  // Access methods
720
- klass_.define_method("first", [](const T& self) -> std::optional<Value_T>
2014
+ klass_.define_method("first", [](const T& vector) -> std::optional<Value_T>
721
2015
  {
722
- if (self.size() > 0)
2016
+ if (vector.size() > 0)
723
2017
  {
724
- return self.front();
2018
+ return vector.front();
725
2019
  }
726
2020
  else
727
2021
  {
728
2022
  return std::nullopt;
729
2023
  }
730
2024
  })
731
- .define_method("last", [](const T& self) -> std::optional<Value_T>
2025
+ .define_method("last", [](const T& vector) -> std::optional<Value_T>
732
2026
  {
733
- if (self.size() > 0)
2027
+ if (vector.size() > 0)
734
2028
  {
735
- return self.back();
2029
+ return vector.back();
736
2030
  }
737
2031
  else
738
2032
  {
739
2033
  return std::nullopt;
740
2034
  }
741
2035
  })
742
- .define_method("[]", [this](const T& self, Difference_T index) -> std::optional<Value_T>
2036
+ .define_method("[]", [this](const T& vector, Difference_T index) -> std::optional<Value_T>
743
2037
  {
744
- index = normalizeIndex(self.size(), index);
745
- if (index < 0 || index >= (Difference_T)self.size())
2038
+ index = normalizeIndex(vector.size(), index);
2039
+ if (index < 0 || index >= (Difference_T)vector.size())
746
2040
  {
747
2041
  return std::nullopt;
748
2042
  }
749
2043
  else
750
2044
  {
751
- return self[index];
2045
+ return vector[index];
752
2046
  }
753
2047
  });
754
2048
 
@@ -760,48 +2054,48 @@ namespace Rice
760
2054
  {
761
2055
  if constexpr (detail::is_comparable_v<Value_T>)
762
2056
  {
763
- klass_.define_method("delete", [](T& self, Value_T& element) -> std::optional<Value_T>
2057
+ klass_.define_method("delete", [](T& vector, Value_T& element) -> std::optional<Value_T>
764
2058
  {
765
- auto iter = std::find(self.begin(), self.end(), element);
766
- if (iter == self.end())
2059
+ auto iter = std::find(vector.begin(), vector.end(), element);
2060
+ if (iter == vector.end())
767
2061
  {
768
2062
  return std::nullopt;
769
2063
  }
770
2064
  else
771
2065
  {
772
2066
  Value_T result = *iter;
773
- self.erase(iter);
2067
+ vector.erase(iter);
774
2068
  return result;
775
2069
  }
776
2070
  })
777
- .define_method("include?", [](T& self, Value_T& element)
2071
+ .define_method("include?", [](T& vector, Value_T& element)
778
2072
  {
779
- return std::find(self.begin(), self.end(), element) != self.end();
2073
+ return std::find(vector.begin(), vector.end(), element) != vector.end();
780
2074
  })
781
- .define_method("index", [](T& self, Value_T& element) -> std::optional<Difference_T>
2075
+ .define_method("index", [](T& vector, Value_T& element) -> std::optional<Difference_T>
782
2076
  {
783
- auto iter = std::find(self.begin(), self.end(), element);
784
- if (iter == self.end())
2077
+ auto iter = std::find(vector.begin(), vector.end(), element);
2078
+ if (iter == vector.end())
785
2079
  {
786
2080
  return std::nullopt;
787
2081
  }
788
2082
  else
789
2083
  {
790
- return iter - self.begin();
2084
+ return iter - vector.begin();
791
2085
  }
792
2086
  });
793
2087
  }
794
2088
  else
795
2089
  {
796
- klass_.define_method("delete", [](T& self, Value_T& element) -> std::optional<Value_T>
2090
+ klass_.define_method("delete", [](T& vector, Value_T& element) -> std::optional<Value_T>
797
2091
  {
798
2092
  return std::nullopt;
799
2093
  })
800
- .define_method("include?", [](const T& self, Value_T& element)
2094
+ .define_method("include?", [](const T& vector, Value_T& element)
801
2095
  {
802
2096
  return false;
803
2097
  })
804
- .define_method("index", [](const T& self, Value_T& element) -> std::optional<Difference_T>
2098
+ .define_method("index", [](const T& vector, Value_T& element) -> std::optional<Difference_T>
805
2099
  {
806
2100
  return std::nullopt;
807
2101
  });
@@ -811,26 +2105,26 @@ namespace Rice
811
2105
  void define_modify_methods()
812
2106
  {
813
2107
  klass_.define_method("clear", &T::clear)
814
- .define_method("delete_at", [](T& self, const size_t& pos)
2108
+ .define_method("delete_at", [](T& vector, const size_t& pos)
815
2109
  {
816
- auto iter = self.begin() + pos;
2110
+ auto iter = vector.begin() + pos;
817
2111
  Value_T result = *iter;
818
- self.erase(iter);
2112
+ vector.erase(iter);
819
2113
  return result;
820
2114
  })
821
- .define_method("insert", [this](T& self, Difference_T index, Value_T& element) -> T&
2115
+ .define_method("insert", [this](T& vector, Difference_T index, Value_T& element) -> T&
822
2116
  {
823
- index = normalizeIndex(self.size(), index, true);
824
- auto iter = self.begin() + index;
825
- self.insert(iter, element);
826
- return self;
2117
+ index = normalizeIndex(vector.size(), index, true);
2118
+ auto iter = vector.begin() + index;
2119
+ vector.insert(iter, element);
2120
+ return vector;
827
2121
  })
828
- .define_method("pop", [](T& self) -> std::optional<Value_T>
2122
+ .define_method("pop", [](T& vector) -> std::optional<Value_T>
829
2123
  {
830
- if (self.size() > 0)
2124
+ if (vector.size() > 0)
831
2125
  {
832
- Value_T result = self.back();
833
- self.pop_back();
2126
+ Value_T result = vector.back();
2127
+ vector.pop_back();
834
2128
  return result;
835
2129
  }
836
2130
  else
@@ -838,16 +2132,16 @@ namespace Rice
838
2132
  return std::nullopt;
839
2133
  }
840
2134
  })
841
- .define_method("push", [](T& self, Value_T& element) -> T&
2135
+ .define_method("push", [](T& vector, Value_T& element) -> T&
842
2136
  {
843
- self.push_back(element);
844
- return self;
2137
+ vector.push_back(element);
2138
+ return vector;
845
2139
  })
846
2140
  .define_method("shrink_to_fit", &T::shrink_to_fit)
847
- .define_method("[]=", [this](T& self, Difference_T index, Value_T& element) -> Value_T&
2141
+ .define_method("[]=", [this](T& vector, Difference_T index, Value_T& element) -> Value_T&
848
2142
  {
849
- index = normalizeIndex(self.size(), index, true);
850
- self[index] = element;
2143
+ index = normalizeIndex(vector.size(), index, true);
2144
+ vector[index] = element;
851
2145
  return element;
852
2146
  });
853
2147
 
@@ -858,33 +2152,40 @@ namespace Rice
858
2152
  void define_enumerable()
859
2153
  {
860
2154
  // Add enumerable support
861
- klass_.include_module(rb_mEnumerable)
862
- .define_method("each", [](T& self) -> const T&
863
- {
864
- for (Value_T& item : self)
865
- {
866
- VALUE element = detail::To_Ruby<Value_T>().convert(item);
867
- rb_yield(element);
868
- }
869
- return self;
870
- });
2155
+ klass_.template define_iterator<typename T::iterator(T::*)()>(&T::begin, &T::end);
2156
+ }
2157
+
2158
+ void define_to_array()
2159
+ {
2160
+ // Add enumerable support
2161
+ klass_.define_method("to_a", [](T& vector)
2162
+ {
2163
+ VALUE result = rb_ary_new();
2164
+ std::for_each(vector.begin(), vector.end(), [&result](const Value_T& element)
2165
+ {
2166
+ VALUE value = detail::To_Ruby<Value_T&>().convert(element);
2167
+ rb_ary_push(result, value);
2168
+ });
2169
+
2170
+ return result;
2171
+ }, Return().setValue());
871
2172
  }
872
2173
 
873
2174
  void define_to_s()
874
2175
  {
875
2176
  if constexpr (detail::is_ostreamable_v<Value_T>)
876
2177
  {
877
- klass_.define_method("to_s", [](const T& self)
2178
+ klass_.define_method("to_s", [](const T& vector)
878
2179
  {
879
- auto iter = self.begin();
880
- auto finish = self.size() > 1000 ? self.begin() + 1000 : self.end();
2180
+ auto iter = vector.begin();
2181
+ auto finish = vector.size() > 1000 ? vector.begin() + 1000 : vector.end();
881
2182
 
882
2183
  std::stringstream stream;
883
2184
  stream << "[";
884
2185
 
885
2186
  for (; iter != finish; iter++)
886
2187
  {
887
- if (iter == self.begin())
2188
+ if (iter == vector.begin())
888
2189
  {
889
2190
  stream << *iter;
890
2191
  }
@@ -900,7 +2201,7 @@ namespace Rice
900
2201
  }
901
2202
  else
902
2203
  {
903
- klass_.define_method("to_s", [](const T& self)
2204
+ klass_.define_method("to_s", [](const T& vector)
904
2205
  {
905
2206
  return "[Not printable]";
906
2207
  });
@@ -915,9 +2216,13 @@ namespace Rice
915
2216
  template<typename T>
916
2217
  Data_Type<T> define_vector_under(Object module, std::string name)
917
2218
  {
918
- if (detail::TypeRegistry::isDefined<T>())
2219
+ if (detail::Registries::instance.types.isDefined<T>())
919
2220
  {
920
- return Data_Type<T>(Data_Type<T>());
2221
+ // If the vector has been previously seen it will be registered but may
2222
+ // not be associated with the constant Module::<name>
2223
+ module.const_set_maybe(name, Data_Type<T>().klass());
2224
+
2225
+ return Data_Type<T>();
921
2226
  }
922
2227
 
923
2228
  Data_Type<T> result = define_class_under<detail::intrinsic_type<T>>(module, name.c_str());
@@ -928,9 +2233,13 @@ namespace Rice
928
2233
  template<typename T>
929
2234
  Data_Type<T> define_vector(std::string name)
930
2235
  {
931
- if (detail::TypeRegistry::isDefined<T>())
2236
+ if (detail::Registries::instance.types.isDefined<T>())
932
2237
  {
933
- return Data_Type<T>(Data_Type<T>());
2238
+ // If the vector has been previously seen it will be registered but may
2239
+ // not be associated with the constant Module::<name>
2240
+ Object(rb_cObject).const_set_maybe(name, Data_Type<T>().klass());
2241
+
2242
+ return Data_Type<T>();
934
2243
  }
935
2244
 
936
2245
  Data_Type<T> result = define_class<detail::intrinsic_type<T>>(name.c_str());
@@ -956,7 +2265,7 @@ namespace Rice
956
2265
  {
957
2266
  Type<T>::verify();
958
2267
 
959
- if (!detail::TypeRegistry::isDefined<std::vector<T>>())
2268
+ if (!detail::Registries::instance.types.isDefined<std::vector<T>>())
960
2269
  {
961
2270
  define_vector_auto<std::vector<T>>();
962
2271
  }
@@ -968,7 +2277,7 @@ namespace Rice
968
2277
  template<typename T>
969
2278
  std::vector<T> vectorFromArray(VALUE value)
970
2279
  {
971
- size_t length = protect(rb_array_len, value);
2280
+ long length = protect(rb_array_len, value);
972
2281
  std::vector<T> result(length);
973
2282
 
974
2283
  for (long i = 0; i < length; i++)