rice 4.3.2 → 4.5.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 (151) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -25
  3. data/README.md +7 -2
  4. data/Rakefile +7 -1
  5. data/include/rice/rice.hpp +7321 -4470
  6. data/include/rice/stl.hpp +769 -222
  7. data/lib/mkmf-rice.rb +37 -95
  8. data/rice/Address_Registration_Guard.hpp +72 -3
  9. data/rice/Arg.hpp +19 -5
  10. data/rice/Arg.ipp +24 -0
  11. data/rice/Callback.hpp +21 -0
  12. data/rice/Callback.ipp +13 -0
  13. data/rice/Constructor.hpp +4 -27
  14. data/rice/Constructor.ipp +79 -0
  15. data/rice/Data_Object.hpp +74 -3
  16. data/rice/Data_Object.ipp +324 -32
  17. data/rice/Data_Type.hpp +215 -3
  18. data/rice/Data_Type.ipp +125 -64
  19. data/rice/Director.hpp +0 -2
  20. data/rice/Enum.hpp +4 -6
  21. data/rice/Enum.ipp +101 -57
  22. data/rice/Exception.hpp +62 -2
  23. data/rice/Exception.ipp +7 -12
  24. data/rice/JumpException.hpp +44 -0
  25. data/rice/JumpException.ipp +48 -0
  26. data/rice/MemoryView.hpp +11 -0
  27. data/rice/MemoryView.ipp +43 -0
  28. data/rice/Return.hpp +6 -26
  29. data/rice/Return.ipp +10 -16
  30. data/rice/detail/DefaultHandler.hpp +12 -0
  31. data/rice/detail/DefaultHandler.ipp +8 -0
  32. data/rice/detail/HandlerRegistry.hpp +5 -35
  33. data/rice/detail/HandlerRegistry.ipp +7 -11
  34. data/rice/detail/InstanceRegistry.hpp +1 -4
  35. data/rice/detail/MethodInfo.hpp +15 -5
  36. data/rice/detail/MethodInfo.ipp +78 -6
  37. data/rice/detail/Native.hpp +32 -0
  38. data/rice/detail/Native.ipp +129 -0
  39. data/rice/detail/NativeAttributeGet.hpp +51 -0
  40. data/rice/detail/NativeAttributeGet.ipp +51 -0
  41. data/rice/detail/NativeAttributeSet.hpp +43 -0
  42. data/rice/detail/NativeAttributeSet.ipp +82 -0
  43. data/rice/detail/NativeCallbackFFI.hpp +55 -0
  44. data/rice/detail/NativeCallbackFFI.ipp +151 -0
  45. data/rice/detail/NativeCallbackSimple.hpp +30 -0
  46. data/rice/detail/NativeCallbackSimple.ipp +29 -0
  47. data/rice/detail/NativeFunction.hpp +20 -21
  48. data/rice/detail/NativeFunction.ipp +199 -64
  49. data/rice/detail/NativeIterator.hpp +8 -11
  50. data/rice/detail/NativeIterator.ipp +27 -31
  51. data/rice/detail/NativeRegistry.hpp +24 -17
  52. data/rice/detail/NativeRegistry.ipp +23 -56
  53. data/rice/detail/Proc.hpp +4 -0
  54. data/rice/detail/Proc.ipp +85 -0
  55. data/rice/detail/Registries.hpp +0 -7
  56. data/rice/detail/Registries.ipp +0 -18
  57. data/rice/detail/RubyFunction.hpp +0 -3
  58. data/rice/detail/RubyFunction.ipp +4 -8
  59. data/rice/detail/RubyType.hpp +19 -0
  60. data/rice/detail/RubyType.ipp +187 -0
  61. data/rice/detail/TupleIterator.hpp +14 -0
  62. data/rice/detail/Type.hpp +5 -6
  63. data/rice/detail/Type.ipp +150 -33
  64. data/rice/detail/TypeRegistry.hpp +15 -7
  65. data/rice/detail/TypeRegistry.ipp +105 -12
  66. data/rice/detail/Wrapper.hpp +6 -5
  67. data/rice/detail/Wrapper.ipp +45 -23
  68. data/rice/detail/cpp_protect.hpp +5 -6
  69. data/rice/detail/default_allocation_func.ipp +0 -2
  70. data/rice/detail/from_ruby.hpp +37 -3
  71. data/rice/detail/from_ruby.ipp +911 -454
  72. data/rice/detail/ruby.hpp +18 -0
  73. data/rice/detail/to_ruby.hpp +41 -3
  74. data/rice/detail/to_ruby.ipp +437 -113
  75. data/rice/global_function.hpp +0 -4
  76. data/rice/global_function.ipp +1 -2
  77. data/rice/rice.hpp +105 -22
  78. data/rice/ruby_mark.hpp +4 -3
  79. data/rice/stl.hpp +4 -0
  80. data/test/embed_ruby.cpp +4 -1
  81. data/test/extconf.rb +2 -0
  82. data/test/ruby/test_multiple_extensions_same_class.rb +14 -14
  83. data/test/test_Address_Registration_Guard.cpp +5 -0
  84. data/test/test_Array.cpp +12 -1
  85. data/test/test_Attribute.cpp +103 -21
  86. data/test/test_Builtin_Object.cpp +5 -0
  87. data/test/test_Callback.cpp +231 -0
  88. data/test/test_Class.cpp +5 -31
  89. data/test/test_Constructor.cpp +69 -6
  90. data/test/test_Data_Object.cpp +9 -4
  91. data/test/test_Data_Type.cpp +428 -64
  92. data/test/test_Director.cpp +10 -5
  93. data/test/test_Enum.cpp +152 -40
  94. data/test/test_Exception.cpp +235 -0
  95. data/test/test_File.cpp +70 -0
  96. data/test/test_From_Ruby.cpp +542 -0
  97. data/test/test_Hash.cpp +5 -0
  98. data/test/test_Identifier.cpp +5 -0
  99. data/test/test_Inheritance.cpp +6 -1
  100. data/test/test_Iterator.cpp +5 -0
  101. data/test/test_JumpException.cpp +22 -0
  102. data/test/test_Keep_Alive.cpp +6 -1
  103. data/test/test_Keep_Alive_No_Wrapper.cpp +5 -0
  104. data/test/test_Memory_Management.cpp +5 -0
  105. data/test/test_Module.cpp +118 -64
  106. data/test/test_Native_Registry.cpp +2 -33
  107. data/test/test_Object.cpp +5 -0
  108. data/test/test_Overloads.cpp +631 -0
  109. data/test/test_Ownership.cpp +67 -4
  110. data/test/test_Proc.cpp +45 -0
  111. data/test/test_Self.cpp +5 -0
  112. data/test/test_Stl_Exception.cpp +109 -0
  113. data/test/test_Stl_Map.cpp +22 -8
  114. data/test/test_Stl_Optional.cpp +5 -0
  115. data/test/test_Stl_Pair.cpp +7 -2
  116. data/test/test_Stl_Reference_Wrapper.cpp +5 -0
  117. data/test/test_Stl_SmartPointer.cpp +210 -5
  118. data/test/test_Stl_String.cpp +5 -0
  119. data/test/test_Stl_String_View.cpp +5 -0
  120. data/test/test_Stl_Type.cpp +147 -0
  121. data/test/test_Stl_Unordered_Map.cpp +18 -7
  122. data/test/test_Stl_Variant.cpp +5 -0
  123. data/test/test_Stl_Vector.cpp +130 -8
  124. data/test/test_String.cpp +5 -0
  125. data/test/test_Struct.cpp +5 -0
  126. data/test/test_Symbol.cpp +5 -0
  127. data/test/test_Template.cpp +192 -0
  128. data/test/test_To_Ruby.cpp +152 -0
  129. data/test/test_Tracking.cpp +1 -0
  130. data/test/test_Type.cpp +100 -0
  131. data/test/test_global_functions.cpp +53 -6
  132. data/test/unittest.cpp +8 -0
  133. metadata +37 -20
  134. data/lib/version.rb +0 -3
  135. data/rice/Address_Registration_Guard_defn.hpp +0 -79
  136. data/rice/Data_Object_defn.hpp +0 -84
  137. data/rice/Data_Type_defn.hpp +0 -190
  138. data/rice/Exception_defn.hpp +0 -68
  139. data/rice/HandlerRegistration.hpp +0 -15
  140. data/rice/Identifier.hpp +0 -50
  141. data/rice/Identifier.ipp +0 -29
  142. data/rice/detail/ExceptionHandler.hpp +0 -8
  143. data/rice/detail/ExceptionHandler.ipp +0 -28
  144. data/rice/detail/ExceptionHandler_defn.hpp +0 -77
  145. data/rice/detail/Jump_Tag.hpp +0 -21
  146. data/rice/detail/NativeAttribute.hpp +0 -64
  147. data/rice/detail/NativeAttribute.ipp +0 -112
  148. data/rice/detail/from_ruby_defn.hpp +0 -38
  149. data/rice/detail/to_ruby_defn.hpp +0 -48
  150. data/test/test_Jump_Tag.cpp +0 -17
  151. data/test/test_To_From_Ruby.cpp +0 -399
data/test/test_Enum.cpp CHANGED
@@ -9,19 +9,22 @@ using namespace Rice;
9
9
 
10
10
  TESTSUITE(Enum);
11
11
 
12
+ SETUP(Enum)
13
+ {
14
+ embed_ruby();
15
+ }
16
+
17
+ TEARDOWN(Enum)
18
+ {
19
+ Rice::detail::Registries::instance.types.clearUnverifiedTypes();
20
+ rb_gc_start();
21
+ }
22
+
23
+
12
24
  namespace
13
25
  {
14
26
  enum Color { RED, BLACK, GREEN };
15
27
 
16
- Enum<Color> define_color_enum()
17
- {
18
- static Enum<Color> colors = define_enum<Color>("Color")
19
- .define_value("RED", RED)
20
- .define_value("BLACK", BLACK)
21
- .define_value("GREEN", GREEN);
22
- return colors;
23
- }
24
-
25
28
  enum class Season { Spring, Summer, Fall, Winter };
26
29
 
27
30
  // This is needed to make unittest compile (it uses ostream to report errors)
@@ -30,22 +33,26 @@ namespace
30
33
  os << static_cast<std::underlying_type_t<Season>>(season);
31
34
  return os;
32
35
  }
36
+ }
33
37
 
34
- Enum<Season> define_season_enum()
35
- {
36
- static Enum<Season> seasons = define_enum<Season>("Season")
37
- .define_value("Spring", Season::Spring)
38
- .define_value("Summer", Season::Summer)
39
- .define_value("Fall", Season::Fall)
40
- .define_value("Winter", Season::Winter);
41
-
42
- return seasons;
43
- }
38
+ Enum<Color> define_color_enum()
39
+ {
40
+ static Enum<Color> colors = define_enum<Color>("Color")
41
+ .define_value("RED", RED)
42
+ .define_value("BLACK", BLACK)
43
+ .define_value("GREEN", GREEN);
44
+ return colors;
44
45
  }
45
46
 
46
- SETUP(Enum)
47
+ Enum<Season> define_season_enum()
47
48
  {
48
- embed_ruby();
49
+ static Enum<Season> seasons = define_enum<Season>("Season")
50
+ .define_value("Spring", Season::Spring)
51
+ .define_value("Summer", Season::Summer)
52
+ .define_value("Fall", Season::Fall)
53
+ .define_value("Winter", Season::Winter);
54
+
55
+ return seasons;
49
56
  }
50
57
 
51
58
  TESTCASE(copy_construct)
@@ -147,6 +154,44 @@ TESTCASE(each_seasons)
147
154
  ASSERT_EQUAL(Season::Winter, *enum_3);
148
155
  }
149
156
 
157
+ TESTCASE(bitset_operators)
158
+ {
159
+ Module m = define_module("TestingBitsetOperators");
160
+
161
+ Enum<Season> rb_cSeason = define_season_enum();
162
+
163
+ std::string code = R"(Season::Summer & Season::Winter)";
164
+ Object value = m.module_eval(code);
165
+ ASSERT_EQUAL(1, detail::From_Ruby<int>().convert(value));
166
+
167
+ code = R"(Season::Summer | Season::Fall)";
168
+ value = m.module_eval(code);
169
+ ASSERT_EQUAL(3, detail::From_Ruby<int>().convert(value));
170
+
171
+ code = R"(Season::Summer ^ Season::Winter)";
172
+ value = m.module_eval(code);
173
+ ASSERT_EQUAL(2, detail::From_Ruby<int>().convert(value));
174
+
175
+ code = R"(~Season::Winter)";
176
+ value = m.module_eval(code);
177
+ ASSERT_EQUAL(-4, detail::From_Ruby<int>().convert(value));
178
+ }
179
+
180
+ TESTCASE(shift_operators)
181
+ {
182
+ Module m = define_module("TestingShiftOperators");
183
+
184
+ Enum<Season> rb_cSeason = define_season_enum();
185
+
186
+ std::string code = R"(Season::Winter << 1)";
187
+ Object value = m.module_eval(code);
188
+ ASSERT_EQUAL(6, detail::From_Ruby<int>().convert(value));
189
+
190
+ code = R"(Season::Winter >> 1)";
191
+ value = m.module_eval(code);
192
+ ASSERT_EQUAL(1, detail::From_Ruby<int>().convert(value));
193
+ }
194
+
150
195
  TESTCASE(to_s)
151
196
  {
152
197
  Module m = define_module("Testing");
@@ -157,14 +202,14 @@ TESTCASE(to_s)
157
202
  ASSERT_EQUAL(String("GREEN"), String(m.module_eval("Color::GREEN.to_s")));
158
203
  }
159
204
 
160
- TESTCASE(to_i)
205
+ TESTCASE(to_int)
161
206
  {
162
207
  Module m = define_module("Testing");
163
208
 
164
209
  Enum<Color> colorEnum = define_color_enum();
165
- ASSERT_EQUAL(detail::to_ruby(int(RED)), m.module_eval("Color::RED.to_i").value());
166
- ASSERT_EQUAL(detail::to_ruby(int(BLACK)), m.module_eval("Color::BLACK.to_i").value());
167
- ASSERT_EQUAL(detail::to_ruby(int(GREEN)), m.module_eval("Color::GREEN.to_i").value());
210
+ ASSERT_EQUAL(detail::to_ruby(int(RED)), m.module_eval("Color::RED.to_int").value());
211
+ ASSERT_EQUAL(detail::to_ruby(int(BLACK)), m.module_eval("Color::BLACK.to_int").value());
212
+ ASSERT_EQUAL(detail::to_ruby(int(GREEN)), m.module_eval("Color::GREEN.to_int").value());
168
213
  }
169
214
 
170
215
  TESTCASE(inspect)
@@ -275,7 +320,7 @@ namespace
275
320
  TESTCASE(nested_enums)
276
321
  {
277
322
  Data_Type<Inner> inner = define_class<Inner>("Inner");
278
- define_enum<Inner::Props>("Props", inner)
323
+ define_enum_under<Inner::Props>("Props", inner)
279
324
  .define_value("VALUE1", Inner::VALUE1)
280
325
  .define_value("VALUE2", Inner::VALUE2)
281
326
  .define_value("VALUE3", Inner::VALUE3);
@@ -283,9 +328,9 @@ TESTCASE(nested_enums)
283
328
 
284
329
  Module m = define_module("Testing");
285
330
 
286
- ASSERT_EQUAL(detail::to_ruby(int(0)), m.module_eval("Inner::Props::VALUE1.to_i").value());
287
- ASSERT_EQUAL(detail::to_ruby(int(1)), m.module_eval("Inner::Props::VALUE2.to_i").value());
288
- ASSERT_EQUAL(detail::to_ruby(int(2)), m.module_eval("Inner::Props::VALUE3.to_i").value());
331
+ ASSERT_EQUAL(detail::to_ruby(int(0)), m.module_eval("Inner::Props::VALUE1.to_int").value());
332
+ ASSERT_EQUAL(detail::to_ruby(int(1)), m.module_eval("Inner::Props::VALUE2.to_int").value());
333
+ ASSERT_EQUAL(detail::to_ruby(int(2)), m.module_eval("Inner::Props::VALUE3.to_int").value());
289
334
  }
290
335
 
291
336
  namespace
@@ -295,9 +340,14 @@ namespace
295
340
  return RED;
296
341
  }
297
342
 
298
- bool isMyFavoriteColor(Color aColor)
343
+ bool isMyFavoriteColor(Color color)
299
344
  {
300
- return aColor == RED;
345
+ return color == RED;
346
+ }
347
+
348
+ bool myFavoriteColorAsInt(int color)
349
+ {
350
+ return color == RED;
301
351
  }
302
352
  }
303
353
 
@@ -305,9 +355,8 @@ TESTCASE(using_enums)
305
355
  {
306
356
  Enum<Color> colorEnum = define_color_enum();
307
357
  colorEnum.define_singleton_function("my_favorite_color", &myFavoriteColor)
308
- .define_singleton_function("is_my_favorite_color", &isMyFavoriteColor)
309
- .define_singleton_function("is_my_favorite_color", &isMyFavoriteColor)
310
- .define_method("is_my_favorite_color", &isMyFavoriteColor);
358
+ .define_singleton_function("is_my_favorite_color", &isMyFavoriteColor)
359
+ .define_method("is_my_favorite_color", &isMyFavoriteColor);
311
360
 
312
361
  Module m = define_module("Testing");
313
362
 
@@ -327,6 +376,46 @@ TESTCASE(using_enums)
327
376
  ASSERT_EQUAL(Qfalse, result.value());
328
377
  }
329
378
 
379
+ TESTCASE(enum_to_int)
380
+ {
381
+ Enum<Color> colorEnum = define_color_enum();
382
+
383
+ Module m = define_module("Testing");
384
+ m.define_module_function("my_favorite_color_as_int", &myFavoriteColorAsInt);
385
+
386
+ std::string code = R"(my_favorite_color_as_int(Color::RED))";
387
+ Object result = m.module_eval(code);
388
+ ASSERT_EQUAL(Qtrue, result.value());
389
+
390
+ code = R"(my_favorite_color_as_int(Color::GREEN))";
391
+ result = m.module_eval(code);
392
+ ASSERT_EQUAL(Qfalse, result.value());
393
+ }
394
+
395
+ namespace
396
+ {
397
+ bool isMyFavoriteSeasonAsInt(int season)
398
+ {
399
+ return ((Season)season == Season::Summer);
400
+ }
401
+ }
402
+
403
+ TESTCASE(enum_class_to_int)
404
+ {
405
+ define_season_enum();
406
+
407
+ Module m = define_module("Testing");
408
+ m.define_module_function("is_my_favorite_season_as_int", &isMyFavoriteSeasonAsInt);
409
+
410
+ std::string code = R"(is_my_favorite_season_as_int(Season::Spring))";
411
+ Object result = m.module_eval(code);
412
+ ASSERT_EQUAL(Qfalse, result.value());
413
+
414
+ code = R"(is_my_favorite_season_as_int(Season::Summer))";
415
+ result = m.module_eval(code);
416
+ ASSERT_EQUAL(Qtrue, result.value());
417
+ }
418
+
330
419
  namespace
331
420
  {
332
421
  Color defaultColor(Color aColor = BLACK)
@@ -360,21 +449,44 @@ namespace
360
449
 
361
450
  TESTCASE(not_defined)
362
451
  {
452
+ Module m = define_module("TestingEnumNotDefined");
453
+
363
454
  #ifdef _MSC_VER
364
- const char* message = "Type is not defined with Rice: enum `anonymous namespace'::Undefined";
455
+ const char* message = "The following types are not registered with Rice:\n enum `anonymous namespace'::Undefined\n";
365
456
  #else
366
- const char* message = "Type is not defined with Rice: (anonymous namespace)::Undefined";
457
+ const char* message = "The following types are not registered with Rice:\n (anonymous namespace)::Undefined\n";
367
458
  #endif
368
459
 
460
+ m.define_module_function("undefined_arg", &undefinedArg);
461
+
369
462
  ASSERT_EXCEPTION_CHECK(
370
463
  std::invalid_argument,
371
- define_global_function("undefined_arg", &undefinedArg),
464
+ Rice::detail::Registries::instance.types.validateTypes(),
465
+ ASSERT_EQUAL(message, ex.what())
466
+ );
467
+
468
+ #ifdef _MSC_VER
469
+ message = "Type is not registered with Rice: enum `anonymous namespace'::Undefined";
470
+ #else
471
+ message = "Type is not registered with Rice: (anonymous namespace)::Undefined";
472
+ #endif
473
+
474
+ m.define_module_function("undefined_return", &undefinedReturn);
475
+ ASSERT_EXCEPTION_CHECK(
476
+ Rice::Exception,
477
+ m.call("undefined_return"),
372
478
  ASSERT_EQUAL(message, ex.what())
373
479
  );
374
480
 
481
+ #ifdef _MSC_VER
482
+ message = "Type is not defined with Rice: enum `anonymous namespace'::Undefined";
483
+ #else
484
+ message = "Type is not defined with Rice: (anonymous namespace)::Undefined";
485
+ #endif
486
+
375
487
  ASSERT_EXCEPTION_CHECK(
376
- std::invalid_argument,
377
- define_global_function("undefined_return", &undefinedReturn),
488
+ Rice::Exception,
489
+ m.call("undefined_arg", 1),
378
490
  ASSERT_EQUAL(message, ex.what())
379
491
  );
380
- }
492
+ }
@@ -11,6 +11,11 @@ SETUP(Exception)
11
11
  embed_ruby();
12
12
  }
13
13
 
14
+ TEARDOWN(Exception)
15
+ {
16
+ rb_gc_start();
17
+ }
18
+
14
19
  TESTCASE(construct_from_exception_object)
15
20
  {
16
21
  VALUE v = detail::protect(rb_exc_new2, rb_eRuntimeError, "foo");
@@ -45,3 +50,233 @@ TESTCASE(what)
45
50
  ASSERT_EQUAL(foo, ex.what());
46
51
  }
47
52
 
53
+ namespace
54
+ {
55
+ enum class ErrorCode
56
+ {
57
+ MEMORY,
58
+ DISK,
59
+ CPU
60
+ };
61
+
62
+ class CustomException
63
+ {
64
+ public:
65
+ CustomException(ErrorCode code) : code(code)
66
+ {
67
+ }
68
+
69
+ const char* what() const noexcept
70
+ {
71
+ return "My custom exception occurred!";
72
+ }
73
+
74
+ ErrorCode code;
75
+ };
76
+
77
+ void raiseCustomException(ErrorCode code)
78
+ {
79
+ throw CustomException(code);
80
+ }
81
+
82
+ class MyExceptionHandler
83
+ {
84
+ public:
85
+ void operator()()
86
+ {
87
+ try
88
+ {
89
+ throw;
90
+ }
91
+ catch (const CustomException& exception)
92
+ {
93
+ Data_Object<CustomException> wrapper(exception, true);
94
+ rb_exc_raise(wrapper);
95
+ }
96
+ }
97
+ };
98
+ }
99
+
100
+ Enum<ErrorCode> define_error_code_enum()
101
+ {
102
+ static Enum<ErrorCode> errorCodes = define_enum<ErrorCode>("ErrorCode")
103
+ .define_value("MEMORY", ErrorCode::MEMORY)
104
+ .define_value("DISK", ErrorCode::DISK)
105
+ .define_value("CPU", ErrorCode::CPU);
106
+
107
+ return errorCodes;
108
+ }
109
+
110
+ TESTCASE(default_handler)
111
+ {
112
+ define_error_code_enum();
113
+ Module m = define_module("Testing");
114
+ m.define_singleton_function("raise_custom_exception", &raiseCustomException);
115
+ Class MyExceptionClass = define_class_under<CustomException>(rb_cObject, "CustomException", rb_eStandardError).
116
+ define_method("message", &CustomException::what);
117
+
118
+ ASSERT_EXCEPTION_CHECK(
119
+ Exception,
120
+ m.call("raise_custom_exception", ErrorCode::MEMORY),
121
+ ASSERT_EQUAL("Unknown C++ exception thrown", ex.what())
122
+ );
123
+ }
124
+
125
+ TESTCASE(custom_handler_functor)
126
+ {
127
+ define_error_code_enum();
128
+ Class MyExceptionClass = define_class_under<CustomException>(rb_cObject, "CustomException", rb_eStandardError).
129
+ define_method("message", &CustomException::what);
130
+
131
+ detail::Registries::instance.handlers.set(MyExceptionHandler());
132
+
133
+ Module m = define_module("Testing");
134
+ m.define_singleton_function("raise_custom_exception", &raiseCustomException);
135
+
136
+ ASSERT_EXCEPTION_CHECK(
137
+ Exception,
138
+ m.call("raise_custom_exception", ErrorCode::DISK),
139
+ ASSERT_EQUAL("My custom exception occurred!", ex.what())
140
+ );
141
+
142
+ std::string code = R"(begin
143
+ raise_custom_exception(ErrorCode::DISK)
144
+ rescue CustomException => exception
145
+ "Caught in Ruby"
146
+ end)";
147
+
148
+ String string = m.instance_eval(code);
149
+ ASSERT_EQUAL("Caught in Ruby", string.c_str());
150
+ }
151
+
152
+ TESTCASE(custom_handler_lambda)
153
+ {
154
+ define_error_code_enum();
155
+ Class rb_eCustomException = define_class("CustomException", rb_eStandardError).
156
+ define_method("message", &CustomException::what);
157
+
158
+ auto handler = []()
159
+ {
160
+ try
161
+ {
162
+ throw;
163
+ }
164
+ catch (const CustomException& exception)
165
+ {
166
+ Data_Object<CustomException> wrapper(exception);
167
+ rb_exc_raise(wrapper);
168
+ }
169
+ };
170
+
171
+ detail::Registries::instance.handlers.set(handler);
172
+
173
+ Module m = define_module("Testing");
174
+ m.define_singleton_function("raise_custom_exception", &raiseCustomException);
175
+
176
+ ASSERT_EXCEPTION_CHECK(
177
+ Exception,
178
+ m.call("raise_custom_exception", ErrorCode::CPU),
179
+ ASSERT_EQUAL("My custom exception occurred!", ex.what())
180
+ );
181
+
182
+ std::string code = R"(begin
183
+ raise_custom_exception(ErrorCode::CPU)
184
+ rescue CustomException => exception
185
+ $!
186
+ end)";
187
+
188
+ Object object = m.instance_eval(code);
189
+ ASSERT_EQUAL(rb_eCustomException.value(), object.class_of().value());
190
+ }
191
+
192
+ TESTCASE(subclasses)
193
+ {
194
+ define_error_code_enum();
195
+ Class rb_eCustomException = define_class("CustomException", rb_eStandardError).
196
+ define_method("message", &CustomException::what);
197
+ Class rb_eMemoryException = define_class_under(rb_cObject, "MemoryException", rb_eCustomException);
198
+ Class rb_eDiskException = define_class_under(rb_cObject, "DiskException", rb_eCustomException);
199
+ Class rb_eCpuException = define_class_under(rb_cObject, "CpuException", rb_eCustomException);
200
+
201
+ auto handler = [&]()
202
+ {
203
+ try
204
+ {
205
+ throw;
206
+ }
207
+ catch (const CustomException& exception)
208
+ {
209
+ Class exceptionKlass = rb_eCustomException;
210
+
211
+ switch (exception.code)
212
+ {
213
+ case ErrorCode::MEMORY:
214
+ exceptionKlass = rb_eMemoryException;
215
+ break;
216
+ case ErrorCode::DISK:
217
+ exceptionKlass = rb_eDiskException;
218
+ break;
219
+ case ErrorCode::CPU:
220
+ exceptionKlass = rb_eCpuException;
221
+ break;
222
+ }
223
+
224
+ // Take ownership of the exception object and map it to the right subclass
225
+ Data_Object<CustomException> wrapper(exception, true, exceptionKlass);
226
+ rb_exc_raise(wrapper);
227
+ }
228
+ };
229
+
230
+ detail::Registries::instance.handlers.set(handler);
231
+
232
+ Module m = define_module("Testing");
233
+ m.define_singleton_function("raise_custom_exception", &raiseCustomException);
234
+
235
+ std::string code = R"(begin
236
+ raise_custom_exception(ErrorCode::MEMORY)
237
+ rescue => e
238
+ e.class.name
239
+ end)";
240
+
241
+ String result = m.instance_eval(code);
242
+ ASSERT_EQUAL("MemoryException", result.c_str());
243
+
244
+ code = R"(begin
245
+ raise_custom_exception(ErrorCode::MEMORY)
246
+ rescue MemoryException => e
247
+ e.class.name
248
+ end)";
249
+
250
+ result = m.instance_eval(code);
251
+ ASSERT_EQUAL("MemoryException", result.c_str());
252
+
253
+ code = R"(begin
254
+ raise_custom_exception(ErrorCode::DISK)
255
+ rescue DiskException => e
256
+ e.class.name
257
+ end)";
258
+
259
+ result = m.instance_eval(code);
260
+ ASSERT_EQUAL("DiskException", result.c_str());
261
+
262
+ code = R"(begin
263
+ raise_custom_exception(ErrorCode::CPU)
264
+ rescue CpuException => e
265
+ e.class.name
266
+ end)";
267
+
268
+ result = m.instance_eval(code);
269
+ ASSERT_EQUAL("CpuException", result.c_str());
270
+
271
+ code = R"(begin
272
+ raise_custom_exception(ErrorCode::CPU)
273
+ rescue RuntimeError => e
274
+ e.class.name
275
+ end)";
276
+
277
+ ASSERT_EXCEPTION_CHECK(
278
+ Exception,
279
+ m.instance_eval(code),
280
+ ASSERT_EQUAL("My custom exception occurred!", ex.what())
281
+ );
282
+ }
@@ -0,0 +1,70 @@
1
+ #include <cstdio>
2
+ #include <filesystem>
3
+
4
+ #include "unittest.hpp"
5
+ #include "embed_ruby.hpp"
6
+ #include <rice/rice.hpp>
7
+ #include <rice/stl.hpp>
8
+
9
+ using namespace Rice;
10
+
11
+ TESTSUITE(FILE);
12
+
13
+ SETUP(FILE)
14
+ {
15
+ embed_ruby();
16
+ }
17
+
18
+ TEARDOWN(FILE)
19
+ {
20
+ rb_gc_start();
21
+ }
22
+
23
+ namespace
24
+ {
25
+ FILE* openFile()
26
+ {
27
+ std::filesystem::path path = __FILE__;
28
+ FILE* fptr = fopen(path.u8string().c_str(), "rb");
29
+ return fptr;
30
+ }
31
+
32
+ std::string readFile(FILE* fptr)
33
+ {
34
+ std::ostringstream result;
35
+
36
+ char buffer[255];
37
+ while (fgets(buffer, sizeof(buffer), fptr) != NULL)
38
+ {
39
+ result << buffer;
40
+ }
41
+ return result.str();
42
+ }
43
+
44
+ bool closeFile(FILE* fptr)
45
+ {
46
+ // Ruby override fclose and replaces it with rb_w32_fclose which causes a segementation fault. Oy!
47
+ #ifndef _MSC_VER
48
+ fclose(fptr);
49
+ #endif
50
+ return true;
51
+ }
52
+ }
53
+
54
+ TESTCASE(File)
55
+ {
56
+ Module m = define_module("TestingModule");
57
+ m.define_module_function("open_file", openFile).
58
+ define_module_function("read_file", readFile).
59
+ define_module_function("close_file", closeFile);
60
+
61
+ Data_Object<FILE> file = m.call("open_file");
62
+ ASSERT((file.value() != Qnil));
63
+ ASSERT((file.get() != nullptr));
64
+
65
+ String string = m.call("read_file", file);
66
+ ASSERT((string.length() > 1300));
67
+
68
+ Object result = m.call("close_file", file);
69
+ ASSERT_EQUAL(Qtrue, result.value());
70
+ }