rice 4.11.4 → 4.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/CMakePresets.json +3 -3
  4. data/bin/rice-doc.rb +2 -0
  5. data/include/rice/rice.hpp +162 -113
  6. data/include/rice/stl.hpp +229 -69
  7. data/lib/mkmf-rice.rb +2 -2
  8. data/lib/rice/version.rb +1 -1
  9. data/rice/Constructor.ipp +3 -3
  10. data/rice/Data_Object.ipp +0 -42
  11. data/rice/JumpException.ipp +1 -0
  12. data/rice/Reference.hpp +2 -2
  13. data/rice/Reference.ipp +1 -1
  14. data/rice/cpp_api/Encoding.ipp +0 -48
  15. data/rice/cpp_api/Hash.ipp +19 -0
  16. data/rice/cpp_api/Object.hpp +2 -2
  17. data/rice/cpp_api/Object.ipp +3 -3
  18. data/rice/cpp_api/String.ipp +19 -0
  19. data/rice/detail/Anchor.hpp +1 -1
  20. data/rice/detail/Anchor.ipp +16 -10
  21. data/rice/detail/Parameter.ipp +11 -0
  22. data/rice/detail/Proc.ipp +19 -0
  23. data/rice/detail/Wrapper.ipp +3 -3
  24. data/rice/detail/from_ruby.hpp +1 -0
  25. data/rice/detail/to_ruby.ipp +58 -1
  26. data/rice/stl/function.ipp +142 -2
  27. data/rice/stl/map.ipp +2 -10
  28. data/rice/stl/multimap.ipp +2 -10
  29. data/rice/stl/optional.ipp +18 -0
  30. data/rice/stl/reference_wrapper.ipp +18 -0
  31. data/rice/stl/set.ipp +24 -25
  32. data/rice/stl/unique_ptr.ipp +3 -0
  33. data/rice/stl/unordered_map.ipp +2 -10
  34. data/rice/stl/vector.ipp +18 -15
  35. data/rice/traits/rice_traits.hpp +3 -0
  36. data/test/test_Attribute.cpp +6 -6
  37. data/test/test_Constructor.cpp +140 -1
  38. data/test/test_File.cpp +2 -3
  39. data/test/test_From_Ruby.cpp +3 -3
  40. data/test/test_Hash.cpp +8 -0
  41. data/test/test_Object.cpp +76 -0
  42. data/test/test_Overloads.cpp +74 -1
  43. data/test/test_Proc.cpp +11 -1
  44. data/test/test_Reference.cpp +20 -1
  45. data/test/test_Stl_Exception.cpp +2 -5
  46. data/test/test_Stl_Function.cpp +72 -7
  47. data/test/test_Stl_Map.cpp +10 -1
  48. data/test/test_Stl_Multimap.cpp +11 -2
  49. data/test/test_Stl_Optional.cpp +9 -0
  50. data/test/test_Stl_Reference_Wrapper.cpp +11 -0
  51. data/test/test_Stl_Set.cpp +12 -0
  52. data/test/test_Stl_Unordered_Map.cpp +10 -1
  53. data/test/test_Stl_Vector.cpp +130 -11
  54. data/test/test_String.cpp +7 -0
  55. data/test/test_To_Ruby.cpp +24 -1
  56. metadata +1 -1
@@ -28,6 +28,38 @@ namespace
28
28
  };
29
29
  }
30
30
 
31
+ namespace
32
+ {
33
+ class TakesPointerParameter
34
+ {
35
+ public:
36
+ TakesPointerParameter(int argc, const char *const* argv, bool enabled)
37
+ : argc_(argc), argv_(argv), enabled_(enabled)
38
+ {
39
+ }
40
+
41
+ int argc() const
42
+ {
43
+ return argc_;
44
+ }
45
+
46
+ const char *firstArg() const
47
+ {
48
+ return argv_[0];
49
+ }
50
+
51
+ bool enabled() const
52
+ {
53
+ return enabled_;
54
+ }
55
+
56
+ private:
57
+ int argc_;
58
+ const char *const *argv_;
59
+ bool enabled_;
60
+ };
61
+ }
62
+
31
63
  TESTCASE(default_constructor)
32
64
  {
33
65
  Data_Type<Default_Constructible> rb_cDefault_Constructible(anonymous_class());
@@ -36,6 +68,15 @@ TESTCASE(default_constructor)
36
68
  ASSERT_EQUAL(rb_cDefault_Constructible, o.class_of());
37
69
  }
38
70
 
71
+ TESTCASE(constructor_accepts_pointer_parameters)
72
+ {
73
+ Data_Type<TakesPointerParameter> rb_cTakesPointerParameter(anonymous_class());
74
+ rb_cTakesPointerParameter.define_constructor(
75
+ Constructor<TakesPointerParameter, int, const char *const*, bool>(),
76
+ Arg("argc"), Arg("argv"), Arg("enabled"));
77
+ ASSERT_EQUAL(rb_cTakesPointerParameter, rb_cTakesPointerParameter);
78
+ }
79
+
39
80
  namespace
40
81
  {
41
82
  class Non_Default_Constructible
@@ -187,4 +228,102 @@ TESTCASE(constructor_move)
187
228
 
188
229
  // This intentionally will not compile due to a static_assert
189
230
  //c.define_constructor(Constructor<MyClass, MyClass&&>());
190
- }
231
+ }
232
+
233
+ namespace
234
+ {
235
+ // Polymorphic (has virtual methods) but non-virtual destructor.
236
+ // Deleting through a pointer is UB in this case. Rice's
237
+ // Wrapper<T*>::~Wrapper should not delete such objects.
238
+ class PolymorphicNonVirtualDtor
239
+ {
240
+ public:
241
+ PolymorphicNonVirtualDtor() : value_(42) {}
242
+ ~PolymorphicNonVirtualDtor() = default;
243
+
244
+ virtual int value()
245
+ {
246
+ return value_;
247
+ }
248
+
249
+ private:
250
+ int value_;
251
+ };
252
+ }
253
+
254
+ TESTCASE(constructor_polymorphic_non_virtual_dtor)
255
+ {
256
+ Data_Type<PolymorphicNonVirtualDtor> rb_cPolymorphicNonVirtualDtor(anonymous_class());
257
+ rb_cPolymorphicNonVirtualDtor
258
+ .define_constructor(Constructor<PolymorphicNonVirtualDtor>())
259
+ .define_method("value", &PolymorphicNonVirtualDtor::value);
260
+
261
+ Object o = rb_cPolymorphicNonVirtualDtor.call("new");
262
+ ASSERT_EQUAL(rb_cPolymorphicNonVirtualDtor, o.class_of());
263
+
264
+ Object value = o.call("value");
265
+ ASSERT_EQUAL(42, detail::From_Ruby<int>().convert(value));
266
+ }
267
+
268
+ namespace
269
+ {
270
+ class MoveOnlyValue
271
+ {
272
+ public:
273
+ MoveOnlyValue() = default;
274
+ MoveOnlyValue(const MoveOnlyValue&) = delete;
275
+
276
+ MoveOnlyValue(MoveOnlyValue&& other) noexcept
277
+ : moved_from_(other.moved_from_)
278
+ {
279
+ other.moved_from_ = true;
280
+ }
281
+
282
+ bool movedFrom() const
283
+ {
284
+ return moved_from_;
285
+ }
286
+
287
+ private:
288
+ bool moved_from_ = false;
289
+ };
290
+
291
+ class TakesMoveOnlyValue
292
+ {
293
+ public:
294
+ explicit TakesMoveOnlyValue(MoveOnlyValue&& value)
295
+ : value_(std::move(value))
296
+ {
297
+ }
298
+
299
+ bool hasValue() const
300
+ {
301
+ return !value_.movedFrom();
302
+ }
303
+
304
+ private:
305
+ MoveOnlyValue value_;
306
+ };
307
+ }
308
+
309
+ TESTCASE(constructor_forwards_rvalue_arguments)
310
+ {
311
+ define_class<MoveOnlyValue>("MoveOnlyValue")
312
+ .define_constructor(Constructor<MoveOnlyValue>())
313
+ .define_method("moved_from?", &MoveOnlyValue::movedFrom);
314
+
315
+ define_class<TakesMoveOnlyValue>("TakesMoveOnlyValue")
316
+ .define_constructor(Constructor<TakesMoveOnlyValue, MoveOnlyValue&&>())
317
+ .define_method("has_value?", &TakesMoveOnlyValue::hasValue);
318
+
319
+ Module m(anonymous_module());
320
+ Array result = m.module_eval(R"(
321
+ value = MoveOnlyValue.new
322
+ consumer = TakesMoveOnlyValue.new(value)
323
+ [value.moved_from?, consumer.has_value?, consumer.class.name]
324
+ )");
325
+
326
+ ASSERT_EQUAL(Qtrue, result[0].value());
327
+ ASSERT_EQUAL(Qtrue, result[1].value());
328
+ ASSERT_EQUAL("TakesMoveOnlyValue", String(result[2].value()).str());
329
+ }
data/test/test_File.cpp CHANGED
@@ -62,10 +62,9 @@ TESTCASE(File)
62
62
  define_module_function("read_file", readFile).
63
63
  define_module_function("close_file", closeFile);
64
64
 
65
- Data_Object<FILE> file = m.call("open_file");
65
+ Object file = m.call("open_file");
66
66
  ASSERT((file.value() != Qnil));
67
- ASSERT((file.get() != nullptr));
68
-
67
+
69
68
  String string = m.call("read_file", file);
70
69
  ASSERT((string.length() > 1300));
71
70
 
@@ -230,7 +230,7 @@ TESTCASE(char_pointer_const)
230
230
 
231
231
  TESTCASE(unsigned_char)
232
232
  {
233
- unsigned char expected = -1;
233
+ unsigned char expected = static_cast<unsigned char>(-1);
234
234
  ASSERT_EQUAL(expected, detail::From_Ruby<unsigned char>().convert(INT2NUM(-1)));
235
235
 
236
236
  expected = 1;
@@ -518,8 +518,8 @@ TESTCASE(unsigned_long)
518
518
  {
519
519
  ASSERT_EQUAL(0u, detail::From_Ruby<unsigned long>().convert(ULONG2NUM(0)));
520
520
  ASSERT_EQUAL(1u, detail::From_Ruby<unsigned long>().convert(ULONG2NUM(1)));
521
- ASSERT_EQUAL((unsigned long)(FIXNUM_MIN),
522
- detail::From_Ruby<unsigned long>().convert(ULONG2NUM(FIXNUM_MIN)));
521
+ ASSERT_EQUAL(static_cast<unsigned long>(FIXNUM_MIN),
522
+ detail::From_Ruby<unsigned long>().convert(ULONG2NUM(static_cast<unsigned long>(FIXNUM_MIN))));
523
523
  ASSERT_EQUAL(std::numeric_limits<unsigned long>::min(),
524
524
  detail::From_Ruby<unsigned long>().convert(ULONG2NUM(std::numeric_limits<unsigned long>::min())));
525
525
  ASSERT_EQUAL(std::numeric_limits<unsigned long>::max(),
data/test/test_Hash.cpp CHANGED
@@ -227,3 +227,11 @@ namespace {
227
227
  TESTCASE(use_hash_in_wrapped_function) {
228
228
  define_global_function("test_hash_arg", &testHashArg);
229
229
  }
230
+
231
+ TESTCASE(hash_lvalue_to_ruby)
232
+ {
233
+ Hash value;
234
+ value["foo"] = 42;
235
+
236
+ ASSERT_EQUAL(value.value(), detail::to_ruby(value));
237
+ }
data/test/test_Object.cpp CHANGED
@@ -187,6 +187,38 @@ TESTCASE(call_return_rice_object)
187
187
  ASSERT_EQUAL(Object(detail::to_ruby(3)), three);
188
188
  }
189
189
 
190
+ TESTCASE(call_with_c_string_argument)
191
+ {
192
+ Module m(anonymous_module());
193
+
194
+ m.module_eval(R"(
195
+ def self.echo(value)
196
+ value
197
+ end
198
+ )");
199
+
200
+ const char* value = "charlie";
201
+ Object result = m.call("echo", value);
202
+
203
+ ASSERT_EQUAL("charlie", detail::From_Ruby<char*>().convert(result.value()));
204
+ }
205
+
206
+ TESTCASE(call_with_string_argument)
207
+ {
208
+ Module m(anonymous_module());
209
+
210
+ m.module_eval(R"(
211
+ def self.echo(value)
212
+ value
213
+ end
214
+ )");
215
+
216
+ String value("charlie");
217
+ Object result = m.call("echo", value);
218
+
219
+ ASSERT_EQUAL("charlie", detail::From_Ruby<char*>().convert(result.value()));
220
+ }
221
+
190
222
  TESTCASE(call_with_keywords)
191
223
  {
192
224
  Module m(anonymous_module());
@@ -215,6 +247,50 @@ TESTCASE(call_with_keywords)
215
247
  );
216
248
  }
217
249
 
250
+ namespace
251
+ {
252
+ class NonCopyableObjectCallArg
253
+ {
254
+ public:
255
+ NonCopyableObjectCallArg() = default;
256
+ NonCopyableObjectCallArg(const NonCopyableObjectCallArg&) = delete;
257
+ NonCopyableObjectCallArg& operator=(const NonCopyableObjectCallArg&) = delete;
258
+
259
+ void setValue(int value)
260
+ {
261
+ value_ = value;
262
+ }
263
+
264
+ int value() const
265
+ {
266
+ return value_;
267
+ }
268
+
269
+ private:
270
+ int value_ = 0;
271
+ };
272
+ }
273
+
274
+ TESTCASE(call_with_noncopyable_reference_argument)
275
+ {
276
+ define_class<NonCopyableObjectCallArg>("NonCopyableObjectCallArg")
277
+ .define_constructor(Constructor<NonCopyableObjectCallArg>())
278
+ .define_method("set_value", &NonCopyableObjectCallArg::setValue)
279
+ .define_method("value", &NonCopyableObjectCallArg::value);
280
+
281
+ Module m(anonymous_module());
282
+ m.module_eval(R"(
283
+ def self.set_value(target)
284
+ target.set_value(41)
285
+ end
286
+ )");
287
+
288
+ NonCopyableObjectCallArg value;
289
+ m.call("set_value", value);
290
+
291
+ ASSERT_EQUAL(41, value.value());
292
+ }
293
+
218
294
  TESTCASE(const_set_get_by_id)
219
295
  {
220
296
  Class c(anonymous_class());
@@ -17,6 +17,8 @@ namespace
17
17
  class MyClass6;
18
18
  class MyClass7;
19
19
  class MyClass8;
20
+ class MyClass9;
21
+ class MyClass10;
20
22
  }
21
23
 
22
24
  SETUP(Overloads)
@@ -31,6 +33,8 @@ SETUP(Overloads)
31
33
  Data_Type<MyClass6>::unbind();
32
34
  Data_Type<MyClass7>::unbind();
33
35
  Data_Type<MyClass8>::unbind();
36
+ Data_Type<MyClass9>::unbind();
37
+ Data_Type<MyClass10>::unbind();
34
38
  Rice::detail::Registries::instance.types.remove<MyClass>();
35
39
  Rice::detail::Registries::instance.types.remove<MyClass2>();
36
40
  Rice::detail::Registries::instance.types.remove<MyClass3>();
@@ -39,6 +43,8 @@ SETUP(Overloads)
39
43
  Rice::detail::Registries::instance.types.remove<MyClass6>();
40
44
  Rice::detail::Registries::instance.types.remove<MyClass7>();
41
45
  Rice::detail::Registries::instance.types.remove<MyClass8>();
46
+ Rice::detail::Registries::instance.types.remove<MyClass9>();
47
+ Rice::detail::Registries::instance.types.remove<MyClass10>();
42
48
  Rice::detail::Registries::instance.natives.reset(Module(rb_mKernel));
43
49
  }
44
50
 
@@ -826,6 +832,73 @@ TESTCASE(ConstRef)
826
832
  ASSERT_EQUAL("const ref", result.str());
827
833
  }
828
834
 
835
+ namespace
836
+ {
837
+ class MyClass9
838
+ {
839
+ public:
840
+ MyClass9() = default;
841
+ MyClass9(const MyClass9&) = default;
842
+
843
+ MyClass9(MyClass9&& other) noexcept
844
+ : moved_from_(other.moved_from_)
845
+ {
846
+ other.moved_from_ = true;
847
+ }
848
+
849
+ bool movedFrom() const
850
+ {
851
+ return moved_from_;
852
+ }
853
+
854
+ private:
855
+ bool moved_from_ = false;
856
+ };
857
+
858
+ class MyClass10
859
+ {
860
+ public:
861
+ MyClass10(const MyClass9&)
862
+ {
863
+ this->result = "const ref";
864
+ }
865
+
866
+ MyClass10(MyClass9&& value)
867
+ : value_(std::move(value))
868
+ {
869
+ this->result = "rvalue ref";
870
+ }
871
+
872
+ std::string result = "";
873
+
874
+ private:
875
+ MyClass9 value_;
876
+ };
877
+ }
878
+
879
+ TESTCASE(ConstructorRValueRefShouldNotBeatConstRefForWrappedObjects)
880
+ {
881
+ Class c9 = define_class<MyClass9>("MyClass9")
882
+ .define_constructor(Constructor<MyClass9>())
883
+ .define_method("moved_from?", &MyClass9::movedFrom);
884
+
885
+ Class c10 = define_class<MyClass10>("MyClass10")
886
+ .define_constructor(Constructor<MyClass10, const MyClass9&>())
887
+ .define_constructor(Constructor<MyClass10, MyClass9&&>())
888
+ .define_attr("result", &MyClass10::result);
889
+
890
+ Module m = define_module("Testing");
891
+
892
+ Array result = m.module_eval(R"(
893
+ source = MyClass9.new
894
+ consumer = MyClass10.new(source)
895
+ [consumer.result, source.moved_from?]
896
+ )");
897
+
898
+ ASSERT_EQUAL("const ref", String(result[0].value()).str());
899
+ ASSERT_EQUAL(Qfalse, result[1].value());
900
+ }
901
+
829
902
  namespace
830
903
  {
831
904
  class MyClass6
@@ -1255,4 +1328,4 @@ TESTCASE(MethodBlock)
1255
1328
 
1256
1329
  String result = m.module_eval(code);
1257
1330
  ASSERT_EQUAL("Method Block", result.c_str());
1258
- }
1331
+ }
data/test/test_Proc.cpp CHANGED
@@ -59,6 +59,16 @@ TESTCASE(SquareProc)
59
59
  ASSERT_EQUAL(81, detail::From_Ruby<int>().convert(result));
60
60
  }
61
61
 
62
+ TESTCASE(SquareProcLvalueToRuby)
63
+ {
64
+ auto proc = square;
65
+ using To_Ruby_T = detail::remove_cv_recursive_t<decltype((proc))>;
66
+
67
+ VALUE value = detail::To_Ruby<To_Ruby_T>().convert(proc);
68
+ Object result(value);
69
+ ASSERT_EQUAL(81, detail::From_Ruby<int>().convert(result.call("call", 9)));
70
+ }
71
+
62
72
  TESTCASE(SquareWithBlock)
63
73
  {
64
74
  Module m = define_module("TestingModuleMakeBlock");
@@ -97,4 +107,4 @@ TESTCASE(SquareWithProc)
97
107
 
98
108
  Object result = m.module_eval(code);
99
109
  ASSERT_EQUAL(16, detail::From_Ruby<int>().convert(result));
100
- }
110
+ }
@@ -104,6 +104,26 @@ TESTCASE(double_reference)
104
104
  ASSERT_EQUAL(2.5, detail::From_Ruby<double>().convert(result));
105
105
  }
106
106
 
107
+ TESTCASE(double_reference_object)
108
+ {
109
+ Module m = define_module("Testing");
110
+
111
+ std::string code = R"(ref = Rice::Reference≺double≻.new(2.5)
112
+ ref.value)";
113
+ Object result = m.module_eval(code);
114
+ ASSERT_EQUAL(2.5, detail::From_Ruby<double>().convert(result));
115
+ }
116
+
117
+ TESTCASE(float_reference_object)
118
+ {
119
+ Module m = define_module("Testing");
120
+ define_reference<float>();
121
+ std::string code = R"(ref = Rice::Reference≺float≻.new(1.25)
122
+ ref.value)";
123
+ Object result = m.module_eval(code);
124
+ ASSERT_EQUAL(1.25f, detail::From_Ruby<float>().convert(result));
125
+ }
126
+
107
127
  TESTCASE(bool_reference)
108
128
  {
109
129
  Module m = define_module("Testing");
@@ -178,4 +198,3 @@ TESTCASE(unsigned_long_long_reference)
178
198
  Object result = m.module_eval(code);
179
199
  ASSERT_EQUAL(73ULL, detail::From_Ruby<unsigned long long>().convert(result));
180
200
  }
181
-
@@ -95,11 +95,8 @@ TESTCASE(StlExceptionPtr)
95
95
  m.define_module_function("create_exception_ptr", createExceptionPtr).
96
96
  define_module_function("handle_exception_ptr", handleExceptionPtr);
97
97
 
98
- Data_Object<std::exception_ptr> exception = m.call("create_exception_ptr");
99
- VALUE value = exception.value();
100
- std::exception_ptr* ptr = exception.get();
101
- ASSERT((value != Qnil));
102
- ASSERT((ptr != nullptr));
98
+ Object exception = m.call("create_exception_ptr");
99
+ ASSERT((exception.value() != Qnil));
103
100
 
104
101
  ASSERT_EXCEPTION_CHECK(
105
102
  Exception,
@@ -180,12 +180,8 @@ TESTCASE(RawProc)
180
180
  invoke(proc, 5, 1)
181
181
  )";
182
182
 
183
- ASSERT_EXCEPTION_CHECK(
184
- Exception,
185
- m.module_eval(code),
186
- ASSERT_EQUAL("The Ruby object is a proc or lambda and does not wrap a C++ object",
187
- ex.what())
188
- );
183
+ Object result = m.module_eval(code);
184
+ ASSERT_EQUAL(5, detail::From_Ruby<int>().convert(result));
189
185
  }
190
186
 
191
187
  TESTCASE(ReturnVoid)
@@ -258,4 +254,73 @@ TESTCASE(ConstFunctionReference)
258
254
 
259
255
  Object result = m.module_eval(code);
260
256
  ASSERT_EQUAL(9, detail::From_Ruby<int>().convert(result));
261
- }
257
+ }
258
+
259
+ TESTCASE(RawProcConstFunctionReference)
260
+ {
261
+ Module m = define_module("TestConstFunctionRaw");
262
+ m.define_module_function("invoke_const", invokeConst);
263
+
264
+ std::string code = R"(
265
+ proc = Proc.new do |value|
266
+ value * 2
267
+ end
268
+ invoke_const(proc, 9)
269
+ )";
270
+
271
+ Object result = m.module_eval(code);
272
+ ASSERT_EQUAL(18, detail::From_Ruby<int>().convert(result));
273
+ }
274
+
275
+ namespace
276
+ {
277
+ class NonCopyableReferenceArg
278
+ {
279
+ public:
280
+ NonCopyableReferenceArg() = default;
281
+ NonCopyableReferenceArg(const NonCopyableReferenceArg&) = delete;
282
+ NonCopyableReferenceArg& operator=(const NonCopyableReferenceArg&) = delete;
283
+
284
+ void setValue(int value)
285
+ {
286
+ value_ = value;
287
+ }
288
+
289
+ int value() const
290
+ {
291
+ return value_;
292
+ }
293
+
294
+ private:
295
+ int value_ = 0;
296
+ };
297
+
298
+ void invokeReferenceFunction(std::function<void(NonCopyableReferenceArg&)> func,
299
+ NonCopyableReferenceArg& value)
300
+ {
301
+ func(value);
302
+ }
303
+ }
304
+
305
+ TESTCASE(ReferenceParameter)
306
+ {
307
+ define_stl_function<void(NonCopyableReferenceArg&)>("FunctionRef");
308
+
309
+ define_class<NonCopyableReferenceArg>("NonCopyableReferenceArg")
310
+ .define_constructor(Constructor<NonCopyableReferenceArg>())
311
+ .define_method("set_value", &NonCopyableReferenceArg::setValue)
312
+ .define_method("value", &NonCopyableReferenceArg::value);
313
+
314
+ Module m = define_module("TestFunctionReference");
315
+ m.define_module_function("invoke_reference_function", invokeReferenceFunction);
316
+
317
+ std::string code = R"(
318
+ value = NonCopyableReferenceArg.new
319
+ func = Std::FunctionRef.new(->(target) { target.set_value(37) })
320
+ invoke_reference_function(func, value)
321
+ value.value
322
+ )";
323
+
324
+ Object result = m.module_eval(code);
325
+ ASSERT_EQUAL(37, detail::From_Ruby<int>().convert(result));
326
+ }
@@ -345,7 +345,16 @@ TESTCASE(NotComparable)
345
345
  Object result = map.call("include?", "two");
346
346
  ASSERT_EQUAL(Qtrue, result.value());
347
347
 
348
- result = map.call("value?", NotComparable(3));
348
+ result = map.instance_eval("respond_to?(:value?)");
349
+ ASSERT_EQUAL(Qfalse, result.value());
350
+
351
+ result = map.instance_eval("respond_to?(:has_value)");
352
+ ASSERT_EQUAL(Qfalse, result.value());
353
+
354
+ result = map.instance_eval("method(:==).owner == self.class");
355
+ ASSERT_EQUAL(Qfalse, result.value());
356
+
357
+ result = map.instance_eval("method(:eql?).owner == self.class");
349
358
  ASSERT_EQUAL(Qfalse, result.value());
350
359
  }
351
360
 
@@ -339,7 +339,16 @@ TESTCASE(NotComparable)
339
339
  Object result = multimap.call("include?", "two");
340
340
  ASSERT_EQUAL(Qtrue, result.value());
341
341
 
342
- result = multimap.call("value?", NotComparable(3));
342
+ result = multimap.instance_eval("respond_to?(:value?)");
343
+ ASSERT_EQUAL(Qfalse, result.value());
344
+
345
+ result = multimap.instance_eval("respond_to?(:has_value)");
346
+ ASSERT_EQUAL(Qfalse, result.value());
347
+
348
+ result = multimap.instance_eval("method(:==).owner == self.class");
349
+ ASSERT_EQUAL(Qfalse, result.value());
350
+
351
+ result = multimap.instance_eval("method(:eql?).owner == self.class");
343
352
  ASSERT_EQUAL(Qfalse, result.value());
344
353
  }
345
354
 
@@ -769,4 +778,4 @@ TESTCASE(KeepAliveCopyConstructor)
769
778
  ASSERT_EQUAL(2, result.size());
770
779
  ASSERT_EQUAL("instance1", detail::From_Ruby<std::string>().convert(result[0].value()));
771
780
  ASSERT_EQUAL("instance2", detail::From_Ruby<std::string>().convert(result[1].value()));
772
- }
781
+ }
@@ -67,6 +67,15 @@ TESTCASE(OptionalReturn)
67
67
  ASSERT(result.is_nil());
68
68
  }
69
69
 
70
+ TESTCASE(NulloptToRubyReference)
71
+ {
72
+ const std::nullopt_t& nullopt = std::nullopt;
73
+ using To_Ruby_T = detail::remove_cv_recursive_t<decltype((nullopt))>;
74
+
75
+ VALUE result = detail::To_Ruby<To_Ruby_T>().convert(nullopt);
76
+ ASSERT_EQUAL(Qnil, result);
77
+ }
78
+
70
79
  TESTCASE(OptionalArgument)
71
80
  {
72
81
  Module m = define_module("Testing");
@@ -75,6 +75,17 @@ TESTCASE(Return)
75
75
  ASSERT_EQUAL("A ref wrapped string", detail::From_Ruby<std::string>().convert(result));
76
76
  }
77
77
 
78
+ TESTCASE(LvalueToRuby)
79
+ {
80
+ MyClass instance;
81
+ std::reference_wrapper<MyClass> wrapper = std::ref(instance);
82
+ const std::reference_wrapper<MyClass>& ref = wrapper;
83
+ using To_Ruby_T = detail::remove_cv_recursive_t<decltype((ref))>;
84
+
85
+ VALUE result = detail::To_Ruby<To_Ruby_T>().convert(ref);
86
+ ASSERT_EQUAL(&instance, detail::unwrap<MyClass>(result, Data_Type<MyClass>::ruby_data_type(), false));
87
+ }
88
+
78
89
  TESTCASE(Argument)
79
90
  {
80
91
  Module m = define_module("Testing");
@@ -333,6 +333,18 @@ TESTCASE(Equal)
333
333
 
334
334
  result = m.instance_eval(code);
335
335
  ASSERT_EQUAL(Qfalse, result.value());
336
+
337
+ code = R"(set = Std::NotPrintableSet.new
338
+ set.method(:==).owner == set.class)";
339
+
340
+ result = m.instance_eval(code);
341
+ ASSERT_EQUAL(Qfalse, result.value());
342
+
343
+ code = R"(set = Std::NotPrintableSet.new
344
+ set.method(:eql?).owner == set.class)";
345
+
346
+ result = m.instance_eval(code);
347
+ ASSERT_EQUAL(Qfalse, result.value());
336
348
  }
337
349
 
338
350
  TESTCASE(ToArray)
@@ -337,7 +337,16 @@ TESTCASE(NotComparable)
337
337
  Object result = unordered_map.call("include?", "two");
338
338
  ASSERT_EQUAL(Qtrue, result.value());
339
339
 
340
- result = unordered_map.call("value?", NotComparable(3));
340
+ result = unordered_map.instance_eval("respond_to?(:value?)");
341
+ ASSERT_EQUAL(Qfalse, result.value());
342
+
343
+ result = unordered_map.instance_eval("respond_to?(:has_value)");
344
+ ASSERT_EQUAL(Qfalse, result.value());
345
+
346
+ result = unordered_map.instance_eval("method(:==).owner == self.class");
347
+ ASSERT_EQUAL(Qfalse, result.value());
348
+
349
+ result = unordered_map.instance_eval("method(:eql?).owner == self.class");
341
350
  ASSERT_EQUAL(Qfalse, result.value());
342
351
  }
343
352