rice 4.3.3 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +63 -26
  3. data/README.md +7 -2
  4. data/Rakefile +7 -1
  5. data/include/rice/rice.hpp +7291 -4430
  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 -15
  52. data/rice/detail/NativeRegistry.ipp +23 -48
  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
+ }