rice 4.9.0 → 4.9.1

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -1
  3. data/CMakePresets.json +77 -50
  4. data/FindRuby.cmake +1 -1
  5. data/include/rice/rice.hpp +114 -81
  6. data/include/rice/stl.hpp +157 -95
  7. data/lib/rice/doc/config.rb +57 -57
  8. data/lib/rice/doc/cpp_reference.rb +158 -158
  9. data/lib/rice/doc/doxygen.rb +289 -289
  10. data/lib/rice/doc/mkdocs.rb +332 -332
  11. data/lib/rice/doc/rice.rb +48 -48
  12. data/lib/rice/doc/ruby.rb +26 -26
  13. data/lib/rice/native.rb +15 -15
  14. data/lib/rice/native_registry.rb +12 -12
  15. data/lib/rice/parameter.rb +5 -5
  16. data/lib/rice/rbs.rb +71 -71
  17. data/lib/rice/version.rb +1 -1
  18. data/lib/rubygems/builder.rb +9 -9
  19. data/lib/rubygems_plugin.rb +8 -8
  20. data/rice/Data_Type.ipp +1 -1
  21. data/rice/detail/Native.ipp +2 -4
  22. data/rice/detail/NativeAttributeGet.ipp +1 -1
  23. data/rice/detail/NativeAttributeSet.hpp +5 -3
  24. data/rice/detail/NativeAttributeSet.ipp +41 -33
  25. data/rice/detail/NativeMethod.ipp +25 -22
  26. data/rice/detail/NativeRegistry.hpp +3 -2
  27. data/rice/detail/NativeRegistry.ipp +13 -9
  28. data/rice/detail/Parameter.ipp +3 -4
  29. data/rice/detail/Wrapper.hpp +5 -1
  30. data/rice/detail/Wrapper.ipp +15 -1
  31. data/rice/stl/exception.ipp +1 -1
  32. data/rice/stl/filesystem.ipp +1 -1
  33. data/rice/stl/map.ipp +13 -11
  34. data/rice/stl/multimap.ipp +13 -11
  35. data/rice/stl/pair.ipp +14 -8
  36. data/rice/stl/set.ipp +16 -16
  37. data/rice/stl/shared_ptr.ipp +15 -1
  38. data/rice/stl/type_index.ipp +1 -1
  39. data/rice/stl/unique_ptr.ipp +2 -2
  40. data/rice/stl/unordered_map.ipp +14 -12
  41. data/rice/stl/vector.ipp +67 -31
  42. data/test/test_Attribute.cpp +72 -0
  43. data/test/test_Callback.cpp +3 -0
  44. data/test/test_Stl_Map.cpp +46 -0
  45. data/test/test_Stl_Multimap.cpp +46 -0
  46. data/test/test_Stl_Set.cpp +34 -0
  47. data/test/test_Stl_Unordered_Map.cpp +46 -0
  48. data/test/test_Stl_Variant.cpp +10 -14
  49. data/test/test_Stl_Vector.cpp +140 -13
  50. data/test/test_Tracking.cpp +3 -0
  51. metadata +1 -1
@@ -14,6 +14,8 @@ namespace Rice
14
14
  using Size_T = typename T::size_type;
15
15
  using Difference_T = typename T::difference_type;
16
16
  using To_Ruby_T = typename detail::remove_cv_recursive_t<Mapped_T>;
17
+ // For pointer types, use the pointer directly; for non-pointer types, use a reference
18
+ using Mapped_Parameter_T = std::conditional_t<std::is_pointer_v<Mapped_T>, Mapped_T, Mapped_T&>;
17
19
 
18
20
  public:
19
21
  UnorderedMapHelper(Data_Type<T> klass) : klass_(klass)
@@ -33,7 +35,7 @@ namespace Rice
33
35
 
34
36
  void register_pair()
35
37
  {
36
- define_pair<const Key_T, T>();
38
+ define_pair<const Key_T, Mapped_T>();
37
39
  }
38
40
 
39
41
  void define_constructors()
@@ -42,7 +44,7 @@ namespace Rice
42
44
 
43
45
  if constexpr (std::is_copy_constructible_v<Key_T> && std::is_copy_constructible_v<Value_T>)
44
46
  {
45
- klass_.define_constructor(Constructor<T, const T&>());
47
+ klass_.define_constructor(Constructor<T, const T&>(), Arg("other"));
46
48
  }
47
49
  }
48
50
 
@@ -71,11 +73,11 @@ namespace Rice
71
73
  {
72
74
  return std::nullopt;
73
75
  }
74
- })
76
+ }, Arg("key"))
75
77
  .define_method("include?", [](T& unordered_map, Key_T& key) -> bool
76
78
  {
77
79
  return unordered_map.find(key) != unordered_map.end();
78
- })
80
+ }, Arg("key"))
79
81
  .define_method("keys", [](T& unordered_map) -> std::vector<Key_T>
80
82
  {
81
83
  std::vector<Key_T> result;
@@ -110,8 +112,8 @@ namespace Rice
110
112
  klass_.define_method("==", [](T& unordered_map, T& other)->bool
111
113
  {
112
114
  return unordered_map == other;
113
- })
114
- .define_method("value?", [](T& unordered_map, Mapped_T& value) -> bool
115
+ }, Arg("other"))
116
+ .define_method("value?", [](T& unordered_map, Mapped_Parameter_T value) -> bool
115
117
  {
116
118
  auto it = std::find_if(unordered_map.begin(), unordered_map.end(),
117
119
  [&value](auto& pair)
@@ -120,15 +122,15 @@ namespace Rice
120
122
  });
121
123
 
122
124
  return it != unordered_map.end();
123
- });
125
+ }, Arg("value"));
124
126
  rb_define_alias(klass_, "eql?", "==");
125
127
  }
126
128
  else
127
129
  {
128
- klass_.define_method("value?", [](T&, Mapped_T&) -> bool
130
+ klass_.define_method("value?", [](T&, Mapped_Parameter_T) -> bool
129
131
  {
130
132
  return false;
131
- });
133
+ }, Arg("value"));
132
134
  }
133
135
 
134
136
  rb_define_alias(klass_, "has_value", "value?");
@@ -151,12 +153,12 @@ namespace Rice
151
153
  {
152
154
  return std::nullopt;
153
155
  }
154
- })
155
- .define_method("[]=", [](T& unordered_map, Key_T key, Mapped_T& value) -> Mapped_T
156
+ }, Arg("key"))
157
+ .define_method("[]=", [](T& unordered_map, Key_T key, Mapped_Parameter_T value) -> Mapped_T
156
158
  {
157
159
  unordered_map[key] = value;
158
160
  return value;
159
- });
161
+ }, Arg("key").keepAlive(), Arg("value").keepAlive());
160
162
 
161
163
  rb_define_alias(klass_, "store", "[]=");
162
164
  }
data/rice/stl/vector.ipp CHANGED
@@ -58,13 +58,13 @@ namespace Rice
58
58
 
59
59
  if constexpr (std::is_copy_constructible_v<Value_T>)
60
60
  {
61
- klass_.define_constructor(Constructor<T, const T&>())
62
- .define_constructor(Constructor<T, Size_T, const Parameter_T>());
61
+ klass_.define_constructor(Constructor<T, const T&>(), Arg("other"))
62
+ .define_constructor(Constructor<T, Size_T, const Parameter_T>(), Arg("count"), Arg("value"));
63
63
  }
64
64
 
65
65
  if constexpr (std::is_default_constructible_v<Value_T>)
66
66
  {
67
- klass_.define_constructor(Constructor<T, Size_T>());
67
+ klass_.define_constructor(Constructor<T, Size_T>(), Arg("count"));
68
68
  }
69
69
 
70
70
  // Allow creation of a vector from a Ruby Array
@@ -72,18 +72,20 @@ namespace Rice
72
72
  {
73
73
  // Create a new vector from the array
74
74
  T* data = new T();
75
- data->reserve(array.size());
76
75
 
76
+ // Wrap the vector
77
+ detail::Wrapper<T*>* wrapper = detail::wrapConstructed<T>(self, Data_Type<T>::ruby_data_type(), data);
78
+
79
+ // Now populate the vector
77
80
  detail::From_Ruby<Value_T> fromRuby;
81
+ data->reserve(array.size());
78
82
 
79
83
  for (long i = 0; i < array.size(); i++)
80
84
  {
81
85
  VALUE element = detail::protect(rb_ary_entry, array, i);
82
86
  data->push_back(fromRuby.convert(element));
87
+ wrapper->addKeepAlive(element);
83
88
  }
84
-
85
- // Wrap the vector
86
- detail::wrapConstructed<T>(self, Data_Type<T>::ruby_data_type(), data);
87
89
  });
88
90
  }
89
91
 
@@ -91,18 +93,18 @@ namespace Rice
91
93
  {
92
94
  if constexpr (std::is_default_constructible_v<Value_T> && std::is_same_v<Value_T, bool>)
93
95
  {
94
- klass_.define_method("resize", static_cast<void (T::*)(const size_t, bool)>(&T::resize));
96
+ klass_.define_method("resize", static_cast<void (T::*)(const size_t, bool)>(&T::resize), Arg("count"), Arg("value"));
95
97
  }
96
98
  else if constexpr (std::is_default_constructible_v<Value_T>)
97
99
  {
98
- klass_.define_method("resize", static_cast<void (T::*)(const size_t)>(&T::resize));
100
+ klass_.define_method("resize", static_cast<void (T::*)(const size_t)>(&T::resize), Arg("count"));
99
101
  }
100
102
  else
101
103
  {
102
104
  klass_.define_method("resize", [](const T&, Size_T)
103
105
  {
104
106
  // Do nothing
105
- });
107
+ }, Arg("count"));
106
108
  }
107
109
  }
108
110
 
@@ -111,13 +113,11 @@ namespace Rice
111
113
  klass_.define_method("empty?", &T::empty)
112
114
  .define_method("capacity", &T::capacity)
113
115
  .define_method("max_size", &T::max_size)
114
- .define_method("reserve", &T::reserve)
116
+ .define_method("reserve", &T::reserve, Arg("new_cap"))
115
117
  .define_method("size", &T::size);
116
-
118
+
117
119
  rb_define_alias(klass_, "count", "size");
118
120
  rb_define_alias(klass_, "length", "size");
119
- //detail::protect(rb_define_alias, klass_, "count", "size");
120
- //detail::protect(rb_define_alias, klass_, "length", "size");
121
121
  }
122
122
 
123
123
  void define_access_methods()
@@ -158,7 +158,7 @@ namespace Rice
158
158
  {
159
159
  return vector[index];
160
160
  }
161
- })
161
+ }, Arg("pos"))
162
162
  .template define_method<Value_T*(T::*)()>("data", &T::data, ReturnBuffer());
163
163
  }
164
164
  else
@@ -197,7 +197,7 @@ namespace Rice
197
197
  {
198
198
  return vector[index];
199
199
  }
200
- });
200
+ }, Arg("pos"));
201
201
  }
202
202
 
203
203
  klass_.define_method("[]", [this](T& vector, Difference_T start, Difference_T length) -> VALUE
@@ -230,7 +230,7 @@ namespace Rice
230
230
 
231
231
  return result;
232
232
  }
233
- }, Return().setValue());
233
+ }, Arg("start"), Arg("length"), Return().setValue());
234
234
 
235
235
  rb_define_alias(klass_, "at", "[]");
236
236
  }
@@ -243,7 +243,7 @@ namespace Rice
243
243
  klass_.define_method("==", [](T& vector, T& other)->bool
244
244
  {
245
245
  return vector == other;
246
- })
246
+ }, Arg("other"))
247
247
  .define_method("delete", [](T& vector, Parameter_T element) -> std::optional<Value_T>
248
248
  {
249
249
  auto iter = std::find(vector.begin(), vector.end(), element);
@@ -261,11 +261,11 @@ namespace Rice
261
261
  {
262
262
  return std::nullopt;
263
263
  }
264
- })
264
+ }, Arg("value"))
265
265
  .define_method("include?", [](T& vector, Parameter_T element)
266
266
  {
267
267
  return std::find(vector.begin(), vector.end(), element) != vector.end();
268
- })
268
+ }, Arg("value"))
269
269
  .define_method("index", [](T& vector, Parameter_T element) -> std::optional<Difference_T>
270
270
  {
271
271
  auto iter = std::find(vector.begin(), vector.end(), element);
@@ -277,7 +277,7 @@ namespace Rice
277
277
  {
278
278
  return iter - vector.begin();
279
279
  }
280
- });
280
+ }, Arg("value"));
281
281
  rb_define_alias(klass_, "eql?", "==");
282
282
  }
283
283
  else
@@ -285,15 +285,15 @@ namespace Rice
285
285
  klass_.define_method("delete", [](T&, Parameter_T) -> std::optional<Value_T>
286
286
  {
287
287
  return std::nullopt;
288
- })
288
+ }, Arg("value"))
289
289
  .define_method("include?", [](const T&, Parameter_T)
290
290
  {
291
291
  return false;
292
- })
292
+ }, Arg("value"))
293
293
  .define_method("index", [](const T&, Parameter_T) -> std::optional<Difference_T>
294
294
  {
295
295
  return std::nullopt;
296
- });
296
+ }, Arg("value"));
297
297
  }
298
298
  }
299
299
 
@@ -315,7 +315,7 @@ namespace Rice
315
315
  vector.erase(iter);
316
316
  return std::nullopt;
317
317
  }
318
- })
318
+ }, Arg("pos"))
319
319
  .define_method("insert", [this](T& vector, Difference_T index, Parameter_T element) -> T&
320
320
  {
321
321
  size_t normalized = normalizeIndex(vector.size(), index, true);
@@ -329,7 +329,7 @@ namespace Rice
329
329
  auto iter = vector.begin() + normalized;
330
330
  vector.insert(iter, std::move(element));
331
331
  return vector;
332
- })
332
+ }, Arg("pos"), Arg("value").keepAlive())
333
333
  .define_method("pop", [](T& vector) -> std::optional<Value_T>
334
334
  {
335
335
  if constexpr (!std::is_copy_assignable_v<Value_T>)
@@ -352,13 +352,13 @@ namespace Rice
352
352
  {
353
353
  vector.push_back(std::move(element));
354
354
  return vector;
355
- })
355
+ }, Arg("value").keepAlive())
356
356
  .define_method("shrink_to_fit", &T::shrink_to_fit)
357
357
  .define_method("[]=", [this](T& vector, Difference_T index, Parameter_T element) -> void
358
358
  {
359
359
  index = normalizeIndex(vector.size(), index, true);
360
360
  vector[index] = std::move(element);
361
- });
361
+ }, Arg("pos"), Arg("value").keepAlive());
362
362
 
363
363
  rb_define_alias(klass_, "push_back", "push");
364
364
  rb_define_alias(klass_, "<<", "push");
@@ -492,7 +492,19 @@ namespace Rice
492
492
  case RUBY_T_ARRAY:
493
493
  if constexpr (std::is_default_constructible_v<T>)
494
494
  {
495
- return Convertible::Exact;
495
+ // For proper overload resolution when a function has multiple vector type
496
+ // overloads (e.g., vector<A>& and vector<B>&), we must check if the array
497
+ // elements can actually be converted to T. Otherwise all vector overloads
498
+ // score equally and the wrong one may be selected.
499
+ long size = RARRAY_LEN(value);
500
+ if (size == 0)
501
+ {
502
+ return Convertible::Exact;
503
+ }
504
+
505
+ From_Ruby<remove_cv_recursive_t<T>> fromRuby;
506
+ VALUE first = rb_ary_entry(value, 0);
507
+ return fromRuby.is_convertible(first);
496
508
  }
497
509
  default:
498
510
  return Convertible::None;
@@ -548,7 +560,19 @@ namespace Rice
548
560
  case RUBY_T_ARRAY:
549
561
  if constexpr (std::is_default_constructible_v<T>)
550
562
  {
551
- return Convertible::Exact;
563
+ // For proper overload resolution when a function has multiple vector type
564
+ // overloads (e.g., vector<A>& and vector<B>&), we must check if the array
565
+ // elements can actually be converted to T. Otherwise all vector overloads
566
+ // score equally and the wrong one may be selected.
567
+ long size = RARRAY_LEN(value);
568
+ if (size == 0)
569
+ {
570
+ return Convertible::Exact;
571
+ }
572
+
573
+ From_Ruby<remove_cv_recursive_t<T>> fromRuby;
574
+ VALUE first = rb_ary_entry(value, 0);
575
+ return fromRuby.is_convertible(first);
552
576
  }
553
577
  default:
554
578
  return Convertible::None;
@@ -609,7 +633,19 @@ namespace Rice
609
633
  case RUBY_T_ARRAY:
610
634
  if constexpr (std::is_default_constructible_v<T>)
611
635
  {
612
- return Convertible::Exact;
636
+ // For proper overload resolution when a function has multiple vector type
637
+ // overloads (e.g., vector<A>& and vector<B>&), we must check if the array
638
+ // elements can actually be converted to T. Otherwise all vector overloads
639
+ // score equally and the wrong one may be selected.
640
+ long size = RARRAY_LEN(value);
641
+ if (size == 0)
642
+ {
643
+ return Convertible::Exact;
644
+ }
645
+
646
+ From_Ruby<remove_cv_recursive_t<T>> fromRuby;
647
+ VALUE first = rb_ary_entry(value, 0);
648
+ return fromRuby.is_convertible(first);
613
649
  }
614
650
  default:
615
651
  return Convertible::None;
@@ -22,6 +22,21 @@ TEARDOWN(Attribute)
22
22
 
23
23
  namespace
24
24
  {
25
+ class MyClass2
26
+ {
27
+ public:
28
+ MyClass2(int value) : value(value)
29
+ {
30
+ }
31
+
32
+ ~MyClass2()
33
+ {
34
+ value = -1;
35
+ }
36
+
37
+ int value = 0;
38
+ };
39
+
25
40
  class SomeClass
26
41
  {
27
42
  };
@@ -70,6 +85,7 @@ namespace
70
85
  char buf[2] = { '0', '1' };
71
86
  OldEnum oldEnum = OldValue1;
72
87
  NewEnum newEnum = NewEnum::NewValue1;
88
+ MyClass2* myClass2 = nullptr;
73
89
 
74
90
  std::string inspect()
75
91
  {
@@ -496,4 +512,60 @@ TESTCASE(CounterBufferAttribute)
496
512
  struct.counters_buffer.class)";
497
513
  Class klass = (Class)m.module_eval(code);
498
514
  ASSERT_EQUAL("Rice::Pointer≺AnonymousNamespace꞉꞉Counter≻", klass.name().c_str());
515
+ }
516
+
517
+ TESTCASE(TakeOwnership)
518
+ {
519
+ Module m = define_module("Testing");
520
+
521
+ define_class<MyClass2>("MyClass2")
522
+ .define_constructor(Constructor<MyClass2, int>());
523
+
524
+ define_class<DataStruct>("DataStruct")
525
+ .define_constructor(Constructor<DataStruct>())
526
+ .define_attr("my_class2", &DataStruct::myClass2, Rice::AttrAccess::ReadWrite, Arg("value").takeOwnership());
527
+
528
+ // Create DataStruct, create MyClass2, assign it, nil it, GC, then verify MyClass2 is still alive
529
+ std::string code = R"(
530
+ data_struct = DataStruct.new
531
+ my_class2 = MyClass2.new(42)
532
+ data_struct.my_class2 = my_class2
533
+ my_class2 = nil
534
+ GC.start
535
+ data_struct
536
+ )";
537
+
538
+ Data_Object<DataStruct> dataStruct = m.module_eval(code);
539
+
540
+ // This should work because ownership was transferred - MyClass2 is now owned by C++
541
+ ASSERT_NOT_EQUAL(nullptr, dataStruct->myClass2);
542
+ ASSERT_EQUAL(42, dataStruct->myClass2->value);
543
+ }
544
+
545
+ TESTCASE(KeepAlive)
546
+ {
547
+ Module m = define_module("Testing");
548
+
549
+ define_class<MyClass2>("MyClass2")
550
+ .define_constructor(Constructor<MyClass2, int>());
551
+
552
+ define_class<DataStruct>("DataStruct")
553
+ .define_constructor(Constructor<DataStruct>())
554
+ .define_attr("my_class2", &DataStruct::myClass2, Rice::AttrAccess::ReadWrite, Arg("value").keepAlive());
555
+
556
+ // Create DataStruct, create MyClass2, assign it, nil it, GC, then verify MyClass2 is still alive
557
+ std::string code = R"(
558
+ data_struct = DataStruct.new
559
+ my_class2 = MyClass2.new(43)
560
+ data_struct.my_class2 = my_class2
561
+ my_class2 = nil
562
+ GC.start
563
+ data_struct
564
+ )";
565
+
566
+ Data_Object<DataStruct> dataStruct = m.module_eval(code);
567
+
568
+ // This should work because keepAlive prevents MyClass2 from being GC'd
569
+ ASSERT_NOT_EQUAL(nullptr, dataStruct->myClass2);
570
+ ASSERT_EQUAL(43, dataStruct->myClass2->value);
499
571
  }
@@ -55,7 +55,10 @@ TESTCASE(LambdaCallBack)
55
55
  ASSERT((globalCallback != nullptr));
56
56
 
57
57
  int ref = 4;
58
+ #pragma GCC diagnostic push
59
+ #pragma GCC diagnostic ignored "-Wwrite-strings"
58
60
  char* result = triggerCallback(1, 2, true, "hello", ref);
61
+ #pragma GCC diagnostic pop
59
62
  ASSERT_EQUAL("1 - 2.0 - true - hello - 4", result);
60
63
  }
61
64
 
@@ -706,3 +706,49 @@ TESTCASE(MapToHash)
706
706
  ASSERT_EQUAL("2", detail::From_Ruby<std::string>().convert(hash["Two"].value()));
707
707
  ASSERT_EQUAL("3", detail::From_Ruby<std::string>().convert(hash["Three"].value()));
708
708
  }
709
+
710
+ namespace
711
+ {
712
+ class MyClass2
713
+ {
714
+ public:
715
+ MyClass2(std::string name): name(name)
716
+ {
717
+ }
718
+ std::string name;
719
+ };
720
+ }
721
+
722
+ TESTCASE(KeepAlive)
723
+ {
724
+ Class c = define_class<MyClass2>("MyClass2").
725
+ define_constructor(Constructor<MyClass2, std::string>()).
726
+ define_attr("name", &MyClass2::name, AttrAccess::Read);
727
+
728
+ define_map<std::string, MyClass2*>("MyClass2PointerMap");
729
+
730
+ Module m = define_module("Testing");
731
+
732
+ std::string code = R"(
733
+ map = Std::MyClass2PointerMap.new
734
+
735
+ # Test []=
736
+ map["one"] = MyClass2.new("instance1")
737
+ map["two"] = MyClass2.new("instance2")
738
+ map["three"] = MyClass2.new("instance3")
739
+
740
+ GC.start
741
+
742
+ names = []
743
+ map.each do |pair|
744
+ names << pair.second.name
745
+ end
746
+ names.sort
747
+ )";
748
+
749
+ Array result = m.module_eval(code);
750
+ ASSERT_EQUAL(3, result.size());
751
+ ASSERT_EQUAL("instance1", detail::From_Ruby<std::string>().convert(result[0].value()));
752
+ ASSERT_EQUAL("instance2", detail::From_Ruby<std::string>().convert(result[1].value()));
753
+ ASSERT_EQUAL("instance3", detail::From_Ruby<std::string>().convert(result[2].value()));
754
+ }
@@ -690,4 +690,50 @@ TESTCASE(HashToMultimapMixedTypes)
690
690
  m.module_eval(code),
691
691
  ASSERT_EQUAL("no implicit conversion of String into Integer", ex.what())
692
692
  );
693
+ }
694
+
695
+ namespace
696
+ {
697
+ class MyClass2
698
+ {
699
+ public:
700
+ MyClass2(std::string name): name(name)
701
+ {
702
+ }
703
+ std::string name;
704
+ };
705
+ }
706
+
707
+ TESTCASE(KeepAlive)
708
+ {
709
+ Class c = define_class<MyClass2>("MyClass2").
710
+ define_constructor(Constructor<MyClass2, std::string>()).
711
+ define_attr("name", &MyClass2::name, AttrAccess::Read);
712
+
713
+ define_multimap<std::string, MyClass2*>("MyClass2PointerMultimap");
714
+
715
+ Module m = define_module("Testing");
716
+
717
+ std::string code = R"(
718
+ map = Std::MyClass2PointerMultimap.new
719
+
720
+ # Test insert
721
+ map.insert("one", MyClass2.new("instance1"))
722
+ map.insert("two", MyClass2.new("instance2"))
723
+ map.insert("three", MyClass2.new("instance3"))
724
+
725
+ GC.start
726
+
727
+ names = []
728
+ map.each do |pair|
729
+ names << pair.second.name
730
+ end
731
+ names.sort
732
+ )";
733
+
734
+ Array result = m.module_eval(code);
735
+ ASSERT_EQUAL(3, result.size());
736
+ ASSERT_EQUAL("instance1", detail::From_Ruby<std::string>().convert(result[0].value()));
737
+ ASSERT_EQUAL("instance2", detail::From_Ruby<std::string>().convert(result[1].value()));
738
+ ASSERT_EQUAL("instance3", detail::From_Ruby<std::string>().convert(result[2].value()));
693
739
  }
@@ -788,3 +788,37 @@ TESTCASE(Superset)
788
788
  Object result = m.instance_eval(code);
789
789
  ASSERT_EQUAL(Qtrue, result.value());
790
790
  }
791
+
792
+ TESTCASE(KeepAlive)
793
+ {
794
+ Class c = define_class<MyClass2>("MyClass2").
795
+ define_constructor(Constructor<MyClass2, std::string>()).
796
+ define_attr("name", &MyClass2::name, AttrAccess::Read);
797
+
798
+ define_set<MyClass2*>("MyClass2PointerSet");
799
+
800
+ Module m = define_module("Testing");
801
+
802
+ std::string code = R"(
803
+ set = Std::MyClass2PointerSet.new
804
+
805
+ # Test insert
806
+ set.insert(MyClass2.new("instance1"))
807
+ set.insert(MyClass2.new("instance2"))
808
+ set.insert(MyClass2.new("instance3"))
809
+
810
+ GC.start
811
+
812
+ names = []
813
+ set.each do |instance|
814
+ names << instance.name
815
+ end
816
+ names.sort
817
+ )";
818
+
819
+ Array result = m.module_eval(code);
820
+ ASSERT_EQUAL(3, result.size());
821
+ ASSERT_EQUAL("instance1", detail::From_Ruby<std::string>().convert(result[0].value()));
822
+ ASSERT_EQUAL("instance2", detail::From_Ruby<std::string>().convert(result[1].value()));
823
+ ASSERT_EQUAL("instance3", detail::From_Ruby<std::string>().convert(result[2].value()));
824
+ }
@@ -704,3 +704,49 @@ TESTCASE(UnorderedMapToHash)
704
704
  ASSERT_EQUAL("2", detail::From_Ruby<std::string>().convert(hash["Two"].value()));
705
705
  ASSERT_EQUAL("3", detail::From_Ruby<std::string>().convert(hash["Three"].value()));
706
706
  }
707
+
708
+ namespace
709
+ {
710
+ class MyClass2
711
+ {
712
+ public:
713
+ MyClass2(std::string name): name(name)
714
+ {
715
+ }
716
+ std::string name;
717
+ };
718
+ }
719
+
720
+ TESTCASE(KeepAlive)
721
+ {
722
+ Class c = define_class<MyClass2>("MyClass2").
723
+ define_constructor(Constructor<MyClass2, std::string>()).
724
+ define_attr("name", &MyClass2::name, AttrAccess::Read);
725
+
726
+ define_unordered_map<std::string, MyClass2*>("MyClass2PointerUnorderedMap");
727
+
728
+ Module m = define_module("Testing");
729
+
730
+ std::string code = R"(
731
+ map = Std::MyClass2PointerUnorderedMap.new
732
+
733
+ # Test []=
734
+ map["one"] = MyClass2.new("instance1")
735
+ map["two"] = MyClass2.new("instance2")
736
+ map["three"] = MyClass2.new("instance3")
737
+
738
+ GC.start
739
+
740
+ names = []
741
+ map.each do |pair|
742
+ names << pair.second.name
743
+ end
744
+ names.sort
745
+ )";
746
+
747
+ Array result = m.module_eval(code);
748
+ ASSERT_EQUAL(3, result.size());
749
+ ASSERT_EQUAL("instance1", detail::From_Ruby<std::string>().convert(result[0].value()));
750
+ ASSERT_EQUAL("instance2", detail::From_Ruby<std::string>().convert(result[1].value()));
751
+ ASSERT_EQUAL("instance3", detail::From_Ruby<std::string>().convert(result[2].value()));
752
+ }
@@ -403,33 +403,29 @@ TESTCASE(VariantWithTwoVectors)
403
403
  Module m = define_module("Testing");
404
404
 
405
405
  std::string code = R"(vector = Std::Vector≺string≻.new
406
- vector << "a" << "b" << "c"
407
- my_class = MyClass4.new
408
- my_class.variant_index(vector))";
406
+ vector << "a" << "b" << "c"
407
+ my_class = MyClass4.new
408
+ my_class.variant_index(vector))";
409
409
 
410
410
  Object result = m.module_eval(code);
411
411
  ASSERT_EQUAL(0u, detail::From_Ruby<size_t>().convert(result));
412
412
 
413
413
  code = R"(vector = Std::Vector≺int≻.new
414
- vector.push_back(4)
415
- my_class = MyClass4.new
416
- my_class.variant_index(vector))";
414
+ vector.push_back(4)
415
+ my_class = MyClass4.new
416
+ my_class.variant_index(vector))";
417
417
  result = m.module_eval(code);
418
418
  ASSERT_EQUAL(1u, detail::From_Ruby<size_t>().convert(result));
419
419
 
420
420
  code = R"(my_class = MyClass4.new
421
- my_class.variant_index(["x", "y", "z"]))";
421
+ my_class.variant_index(["x", "y", "z"]))";
422
422
  result = m.module_eval(code);
423
423
  ASSERT_EQUAL(0u, detail::From_Ruby<size_t>().convert(result));
424
424
 
425
425
  code = R"(my_class = MyClass4.new
426
- my_class.variant_index([5, 6]))";
427
-
428
- ASSERT_EXCEPTION_CHECK(
429
- Exception,
430
- m.module_eval(code),
431
- ASSERT_EQUAL("wrong argument type Integer (expected String)", ex.what())
432
- );
426
+ my_class.variant_index([5, 6]))";
427
+ result = m.module_eval(code);
428
+ ASSERT_EQUAL(1u, detail::From_Ruby<size_t>().convert(result));
433
429
  }
434
430
 
435
431
  namespace