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