rice 4.0.4 → 4.2.0

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