rice 4.1.0 → 4.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f03a8ee866743d992f90b1584740922c09cc495b114e1416e3391e43ff70dd9
4
- data.tar.gz: 1fd60da5718c311a7ea4ad1443442bfe70c151f6a8ec0e84c024991114ae4741
3
+ metadata.gz: abe0f01ee83328bbd99dbf4374116b08c9b01209f3171e59b4bae01013234b81
4
+ data.tar.gz: 37180b133a4f0ec09b66367070382800a33adeef4a7916c66171be3f417f3776
5
5
  SHA512:
6
- metadata.gz: ab7089e5fae0ab6e5c11408f84ec86d1124570ec51740ed7e047df3971dcb03b36a04c0662c8b18b7030e9c44b4ec3eebdf9a7f29199888b1e3c2d1f5c8256b7
7
- data.tar.gz: f3714fc7ae13d64bd56103dc7680536aa42305daa6ef8332220f48f74c8dbdabcae95ec57035d653e416977c0096f309014a467cc2d590c5f3b6375c16bbabbd
6
+ metadata.gz: 1b4a6cc95a81a53ec6396001ac5e2fb4e9acf94701beda44cf46b35d17c835962abe5d7faffeac048b792c8bd0c079b9f190d52a0eba6dedba7d4831cd7b5ffe
7
+ data.tar.gz: 5ff9cb1abd2d3d0f32eeb7c78f9823918c12d87bcfce45a64d47331d53bf83ee8e73772856893b3cdb71bcc087825446a5df00b6b48fe96389c374591a368c54
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## 4.2.1
2
+
3
+ * Support systems who use `#include <experimental/filesystem>` over `#include<filesystem>`. See [#197](https://github.com/jasonroelofs/rice/issues/197) and [#201](https://github.com/jasonroelofs/rice/pull/201)
4
+
5
+ ## 4.2
6
+
7
+ * Support Ruby 3.3.0.
8
+ * Split Object.call to an explicit Object.call_kw for calling methods expecting keyword arguments.
9
+ * Previously, if a wrapper used `keepAlive` on an argument or return value that was itself a Rice type, calling said method would segfault. We've now added an explicit exception to be thrown in this case, prevending the segfault and providing guidance on what was wrong and how to fix it. See [#193](https://github.com/jasonroelofs/rice/pull/193) and [#194](https://github.com/jasonroelofs/rice/pull/194)
10
+ * Fix wrapping of std::shared_ptr to properly take default arguments into account.
11
+
1
12
  ## 4.1
2
13
 
3
14
  Rice 4.1 builds on the 4.0 release and has a number of improvements that both polish Rice and extend its functionality. However, there are three incompatibilities to know about:
data/CONTRIBUTORS.md CHANGED
@@ -17,3 +17,6 @@ I'd like to thank the following people for their help in making Rice what it is
17
17
  * [Charlie Savage (cfis)](https://github.com/cfis) for multiple improvements and modernizations: [#130](https://github.com/jasonroelofs/rice/pull/130), [#131](https://github.com/jasonroelofs/rice/pull/131), [#133](https://github.com/jasonroelofs/rice/pull/133), [#134](https://github.com/jasonroelofs/rice/pull/134), [#136](https://github.com/jasonroelofs/rice/pull/136), [#137](https://github.com/jasonroelofs/rice/pull/137), [#140](https://github.com/jasonroelofs/rice/pull/140), [#141](https://github.com/jasonroelofs/rice/pull/141) and many others, including the work to make Rice header-only.
18
18
  * [Atsushi Tatsuma (yoshoku)](https://github.com/yoshoku) for [#135](https://github.com/jasonroelofs/rice/pull/135)
19
19
  * [Andrew Kane (ankane)](https://github.com/ankane) for helping [test Rice 4](https://github.com/jasonroelofs/rice/issues/149).
20
+ * [Maxim Samsonov (maxirmx)](https://github.com/maxirmx) for [#193](https://github.com/jasonroelofs/rice/issues/193) and [#194](https://github.com/jasonroelofs/rice/pull/194)
21
+ * [kvtb](https://github.com/kvtb) for [#191](https://github.com/jasonroelofs/rice/issues/191)
22
+ * [thekendalmiller](https://github.com/thekendalmiller) for [#201](https://github.com/jasonroelofs/rice/pull/201)
@@ -1205,9 +1205,18 @@ namespace Rice::detail
1205
1205
  // ========= cpp_protect.hpp =========
1206
1206
 
1207
1207
  #include <regex>
1208
- #include <filesystem>
1209
1208
  #include <stdexcept>
1210
1209
 
1210
+ #if __has_include(<filesystem>)
1211
+ #include <filesystem>
1212
+ namespace fs = std::filesystem;
1213
+ #elif __has_include(<experimental/filesystem>)
1214
+ #include <experimental/filesystem>
1215
+ namespace fs = std::experimental::filesystem;
1216
+ #else
1217
+ #error "no filesystem include found :'("
1218
+ #endif
1219
+
1211
1220
 
1212
1221
  namespace Rice::detail
1213
1222
  {
@@ -1247,7 +1256,7 @@ namespace Rice::detail
1247
1256
  {
1248
1257
  rb_exc_raise(rb_exc_new2(rb_eArgError, ex.what()));
1249
1258
  }
1250
- catch (std::filesystem::filesystem_error const& ex)
1259
+ catch (fs::filesystem_error const& ex)
1251
1260
  {
1252
1261
  rb_exc_raise(rb_exc_new2(rb_eIOError, ex.what()));
1253
1262
  }
@@ -1531,7 +1540,7 @@ namespace Rice::detail
1531
1540
  #pragma GCC diagnostic push
1532
1541
  #pragma GCC diagnostic ignored "-Warray-bounds"
1533
1542
  #endif
1534
- return static_cast<Wrapper*>(RTYPEDDATA_DATA(value));
1543
+ return RTYPEDDATA_P(value) ? static_cast<Wrapper*>(RTYPEDDATA_DATA(value)) : nullptr;
1535
1544
  #ifdef __GNUC__
1536
1545
  #pragma GCC diagnostic pop
1537
1546
  #endif
@@ -4052,6 +4061,9 @@ namespace Rice::detail
4052
4061
  // Figure out what self is
4053
4062
  Class_T getReceiver(VALUE self);
4054
4063
 
4064
+ // Throw an exception when wrapper cannot be extracted
4065
+ [[noreturn]] void noWrapper(const VALUE klass, const std::string& wrapper);
4066
+
4055
4067
  // Do we need to keep alive any arguments?
4056
4068
  void checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues);
4057
4069
 
@@ -4073,6 +4085,7 @@ namespace Rice::detail
4073
4085
  #include <array>
4074
4086
  #include <algorithm>
4075
4087
  #include <stdexcept>
4088
+ #include <sstream>
4076
4089
 
4077
4090
 
4078
4091
  namespace Rice::detail
@@ -4107,7 +4120,7 @@ namespace Rice::detail
4107
4120
  NativeFunction<From_Ruby_T, Function_T, IsMethod>::NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
4108
4121
  : klass_(klass), method_name_(method_name), function_(function), methodInfo_(methodInfo)
4109
4122
  {
4110
- // Create a tuple of NativeArgs that will convert the Ruby values to native values. For
4123
+ // Create a tuple of NativeArgs that will convert the Ruby values to native values. For
4111
4124
  // builtin types NativeArgs will keep a copy of the native value so that it
4112
4125
  // can be passed by reference or pointer to the native function. For non-builtin types
4113
4126
  // it will just pass the value through.
@@ -4233,7 +4246,7 @@ namespace Rice::detail
4233
4246
  {
4234
4247
  // Call the native method and get the result
4235
4248
  Return_T nativeResult = std::apply(this->function_, nativeArgs);
4236
-
4249
+
4237
4250
  // Return the result
4238
4251
  return this->toRuby_.convert(nativeResult);
4239
4252
  }
@@ -4284,15 +4297,38 @@ namespace Rice::detail
4284
4297
  }
4285
4298
  }
4286
4299
 
4300
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
4301
+ void NativeFunction<From_Ruby_T, Function_T, IsMethod>::noWrapper(const VALUE klass, const std::string& wrapper)
4302
+ {
4303
+ std::stringstream message;
4304
+
4305
+ message << "When calling the method `";
4306
+ message << this->method_name_;
4307
+ message << "' we could not find the wrapper for the '";
4308
+ message << rb_obj_classname(klass);
4309
+ message << "' ";
4310
+ message << wrapper;
4311
+ message << " type. You should not use keepAlive() on a Return or Arg that is a builtin Rice type.";
4312
+
4313
+ throw std::runtime_error(message.str());
4314
+ }
4315
+
4287
4316
  template<typename From_Ruby_T, typename Function_T, bool IsMethod>
4288
4317
  void NativeFunction<From_Ruby_T, Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues)
4289
4318
  {
4290
- // Check function arguments
4319
+ // selfWrapper will be nullptr if this(self) is a builtin type and not an external(wrapped) type
4320
+ // it is highly unlikely that keepAlive is used in this case but we check anyway
4291
4321
  Wrapper* selfWrapper = getWrapper(self);
4322
+
4323
+ // Check function arguments
4292
4324
  for (const Arg& arg : (*this->methodInfo_))
4293
4325
  {
4294
4326
  if (arg.isKeepAlive())
4295
4327
  {
4328
+ if (selfWrapper == nullptr)
4329
+ {
4330
+ noWrapper(self, "self");
4331
+ }
4296
4332
  selfWrapper->addKeepAlive(rubyValues[arg.position]);
4297
4333
  }
4298
4334
  }
@@ -4300,7 +4336,17 @@ namespace Rice::detail
4300
4336
  // Check return value
4301
4337
  if (this->methodInfo_->returnInfo.isKeepAlive())
4302
4338
  {
4339
+ if (selfWrapper == nullptr)
4340
+ {
4341
+ noWrapper(self, "self");
4342
+ }
4343
+
4344
+ // returnWrapper will be nullptr if returnValue is a built-in type and not an external(wrapped) type
4303
4345
  Wrapper* returnWrapper = getWrapper(returnValue);
4346
+ if (returnWrapper == nullptr)
4347
+ {
4348
+ noWrapper(returnValue, "return");
4349
+ }
4304
4350
  returnWrapper->addKeepAlive(self);
4305
4351
  }
4306
4352
  }
@@ -4672,7 +4718,8 @@ namespace Rice
4672
4718
 
4673
4719
  //! Call the Ruby method specified by 'id' on object 'obj'.
4674
4720
  /*! Pass in arguments (arg1, arg2, ...). The arguments will be converted to
4675
- * Ruby objects with to_ruby<>.
4721
+ * Ruby objects with to_ruby<>. To call methods expecting keyword arguments,
4722
+ * use call_kw.
4676
4723
  *
4677
4724
  * E.g.:
4678
4725
  * \code
@@ -4690,6 +4737,29 @@ namespace Rice
4690
4737
  template<typename ...Arg_Ts>
4691
4738
  Object call(Identifier id, Arg_Ts... args) const;
4692
4739
 
4740
+ //! Call the Ruby method specified by 'id' on object 'obj'.
4741
+ /*! Pass in arguments (arg1, arg2, ...). The arguments will be converted to
4742
+ * Ruby objects with to_ruby<>. The final argument must be a Hash and will be treated
4743
+ * as keyword arguments to the function.
4744
+ *
4745
+ * E.g.:
4746
+ * \code
4747
+ * Rice::Hash kw;
4748
+ * kw[":argument"] = String("one")
4749
+ * Rice::Object obj = x.call_kw("foo", kw);
4750
+ * \endcode
4751
+ *
4752
+ * If a return type is specified, the return value will automatically be
4753
+ * converted to that type as long as 'from_ruby' exists for that type.
4754
+ *
4755
+ * E.g.:
4756
+ * \code
4757
+ * float ret = x.call_kw<float>("foo", kw);
4758
+ * \endcode
4759
+ */
4760
+ template<typename ...Arg_Ts>
4761
+ Object call_kw(Identifier id, Arg_Ts... args) const;
4762
+
4693
4763
  //! Vectorized call.
4694
4764
  /*! Calls the method identified by id with the list of arguments
4695
4765
  * identified by args.
@@ -4753,6 +4823,7 @@ namespace Rice
4753
4823
  } // namespace Rice
4754
4824
 
4755
4825
  #endif // Rice__Object_defn__hpp_
4826
+
4756
4827
  // --------- Object.ipp ---------
4757
4828
  #ifndef Rice__Object__ipp_
4758
4829
  #define Rice__Object__ipp_
@@ -4797,7 +4868,15 @@ namespace Rice
4797
4868
  easy to duplicate by setting GC.stress to true and calling a constructor
4798
4869
  that takes multiple values like a std::pair wrapper. */
4799
4870
  std::array<VALUE, sizeof...(Arg_Ts)> values = { detail::To_Ruby<detail::remove_cv_recursive_t<Arg_Ts>>().convert(args)... };
4800
- return detail::protect(rb_funcallv_kw, value(), id.id(), (int)values.size(), (const VALUE*)values.data(), RB_PASS_CALLED_KEYWORDS);
4871
+ return detail::protect(rb_funcallv, value(), id.id(), (int)values.size(), (const VALUE*)values.data());
4872
+ }
4873
+
4874
+ template<typename ...Arg_Ts>
4875
+ inline Object Object::call_kw(Identifier id, Arg_Ts... args) const
4876
+ {
4877
+ /* IMPORTANT - See call() above */
4878
+ std::array<VALUE, sizeof...(Arg_Ts)> values = { detail::To_Ruby<detail::remove_cv_recursive_t<Arg_Ts>>().convert(args)... };
4879
+ return detail::protect(rb_funcallv_kw, value(), id.id(), (int)values.size(), (const VALUE*)values.data(), RB_PASS_KEYWORDS);
4801
4880
  }
4802
4881
 
4803
4882
  template<typename T>
@@ -4975,6 +5054,7 @@ namespace Rice::detail
4975
5054
  #endif // Rice__Object__ipp_
4976
5055
 
4977
5056
 
5057
+
4978
5058
  // ========= Builtin_Object.hpp =========
4979
5059
 
4980
5060
 
data/include/rice/stl.hpp CHANGED
@@ -168,6 +168,11 @@ namespace Rice::detail
168
168
 
169
169
  return std::complex<T>(From_Ruby<T>().convert(real), From_Ruby<T>().convert(imaginary));
170
170
  }
171
+
172
+ bool is_convertible(VALUE value)
173
+ {
174
+ return rb_type(value) == RUBY_T_COMPLEX;
175
+ }
171
176
  };
172
177
 
173
178
  template<typename T>
@@ -183,11 +188,17 @@ namespace Rice::detail
183
188
  return this->converted_;
184
189
  }
185
190
 
191
+ bool is_convertible(VALUE value)
192
+ {
193
+ return rb_type(value) == RUBY_T_COMPLEX;
194
+ }
195
+
186
196
  private:
187
197
  std::complex<T> converted_;
188
198
  };
189
199
  }
190
200
 
201
+
191
202
  // ========= optional.hpp =========
192
203
 
193
204
 
@@ -219,7 +230,7 @@ namespace Rice::detail
219
230
  class To_Ruby<std::optional<T>>
220
231
  {
221
232
  public:
222
- static VALUE convert(std::optional<T>& data, bool takeOwnership = false)
233
+ static VALUE convert(const std::optional<T>& data, bool takeOwnership = false)
223
234
  {
224
235
  if (data.has_value())
225
236
  {
@@ -236,7 +247,7 @@ namespace Rice::detail
236
247
  class To_Ruby<std::optional<T>&>
237
248
  {
238
249
  public:
239
- static VALUE convert(std::optional<T>& data, bool takeOwnership = false)
250
+ static VALUE convert(const std::optional<T>& data, bool takeOwnership = false)
240
251
  {
241
252
  if (data.has_value())
242
253
  {
@@ -288,6 +299,7 @@ namespace Rice::detail
288
299
  };
289
300
  }
290
301
 
302
+
291
303
  // ========= reference_wrapper.hpp =========
292
304
 
293
305
 
@@ -309,7 +321,7 @@ namespace Rice::detail
309
321
  class To_Ruby<std::reference_wrapper<T>>
310
322
  {
311
323
  public:
312
- VALUE convert(std::reference_wrapper<T>& data, bool takeOwnership = false)
324
+ VALUE convert(const std::reference_wrapper<T>& data, bool takeOwnership = false)
313
325
  {
314
326
  return To_Ruby<T&>().convert(data.get());
315
327
  }
@@ -334,6 +346,7 @@ namespace Rice::detail
334
346
  };
335
347
  }
336
348
 
349
+
337
350
  // ========= smart_ptr.hpp =========
338
351
 
339
352
 
@@ -448,8 +461,18 @@ namespace Rice::detail
448
461
  class From_Ruby<std::shared_ptr<T>>
449
462
  {
450
463
  public:
464
+ From_Ruby() = default;
465
+
466
+ explicit From_Ruby(Arg * arg) : arg_(arg)
467
+ {
468
+ }
469
+
451
470
  std::shared_ptr<T> convert(VALUE value)
452
471
  {
472
+ if(value == Qnil && this->arg_ && this->arg_->hasDefaultValue()) {
473
+ return this->arg_->template defaultValue<std::shared_ptr<T>>();
474
+ }
475
+
453
476
  Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::ruby_data_type());
454
477
 
455
478
  using Wrapper_T = WrapperSmartPointer<std::shared_ptr, T>;
@@ -461,14 +484,27 @@ namespace Rice::detail
461
484
  }
462
485
  return smartWrapper->data();
463
486
  }
487
+
488
+ private:
489
+ Arg* arg_ = nullptr;
464
490
  };
465
491
 
466
492
  template <typename T>
467
493
  class From_Ruby<std::shared_ptr<T>&>
468
494
  {
469
495
  public:
496
+ From_Ruby() = default;
497
+
498
+ explicit From_Ruby(Arg * arg) : arg_(arg)
499
+ {
500
+ }
501
+
470
502
  std::shared_ptr<T>& convert(VALUE value)
471
503
  {
504
+ if(value == Qnil && this->arg_ && this->arg_->hasDefaultValue()) {
505
+ return this->arg_->template defaultValue<std::shared_ptr<T>>();
506
+ }
507
+
472
508
  Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::ruby_data_type());
473
509
 
474
510
  using Wrapper_T = WrapperSmartPointer<std::shared_ptr, T>;
@@ -480,6 +516,9 @@ namespace Rice::detail
480
516
  }
481
517
  return smartWrapper->data();
482
518
  }
519
+
520
+ private:
521
+ Arg* arg_ = nullptr;
483
522
  };
484
523
 
485
524
  template<typename T>
@@ -492,6 +531,7 @@ namespace Rice::detail
492
531
  };
493
532
  }
494
533
 
534
+
495
535
  // ========= monostate.hpp =========
496
536
 
497
537
 
@@ -513,7 +553,7 @@ namespace Rice::detail
513
553
  class To_Ruby<std::monostate>
514
554
  {
515
555
  public:
516
- VALUE convert(std::monostate& _)
556
+ VALUE convert(const std::monostate& _)
517
557
  {
518
558
  return Qnil;
519
559
  }
@@ -523,7 +563,7 @@ namespace Rice::detail
523
563
  class To_Ruby<std::monostate&>
524
564
  {
525
565
  public:
526
- static VALUE convert(std::monostate& data, bool takeOwnership = false)
566
+ static VALUE convert(const std::monostate& data, bool takeOwnership = false)
527
567
  {
528
568
  return Qnil;
529
569
  }
@@ -563,6 +603,7 @@ namespace Rice::detail
563
603
  };
564
604
  }
565
605
 
606
+
566
607
  // ========= variant.hpp =========
567
608
 
568
609
 
@@ -596,13 +637,13 @@ namespace Rice::detail
596
637
  public:
597
638
 
598
639
  template<typename T>
599
- static VALUE convertElement(std::variant<Types...>& data, bool takeOwnership)
640
+ static VALUE convertElement(const std::variant<Types...>& data, bool takeOwnership)
600
641
  {
601
642
  return To_Ruby<T>().convert(std::get<T>(data));
602
643
  }
603
644
 
604
645
  template<std::size_t... I>
605
- static VALUE convertIterator(std::variant<Types...>& data, bool takeOwnership, std::index_sequence<I...>& indices)
646
+ static VALUE convertIterator(const std::variant<Types...>& data, bool takeOwnership, std::index_sequence<I...>& indices)
606
647
  {
607
648
  // Create a tuple of the variant types so we can look over the tuple's types
608
649
  using Tuple_T = std::tuple<Types...>;
@@ -634,7 +675,7 @@ namespace Rice::detail
634
675
  return result;
635
676
  }
636
677
 
637
- static VALUE convert(std::variant<Types...>& data, bool takeOwnership = false)
678
+ static VALUE convert(const std::variant<Types...>& data, bool takeOwnership = false)
638
679
  {
639
680
  auto indices = std::make_index_sequence<std::variant_size_v<std::variant<Types...>>>{};
640
681
  return convertIterator(data, takeOwnership, indices);
@@ -646,13 +687,13 @@ namespace Rice::detail
646
687
  {
647
688
  public:
648
689
  template<typename T>
649
- static VALUE convertElement(std::variant<Types...>& data, bool takeOwnership)
690
+ static VALUE convertElement(const std::variant<Types...>& data, bool takeOwnership)
650
691
  {
651
692
  return To_Ruby<T>().convert(std::get<T>(data));
652
693
  }
653
694
 
654
695
  template<std::size_t... I>
655
- static VALUE convertIterator(std::variant<Types...>& data, bool takeOwnership, std::index_sequence<I...>& indices)
696
+ static VALUE convertIterator(const std::variant<Types...>& data, bool takeOwnership, std::index_sequence<I...>& indices)
656
697
  {
657
698
  // Create a tuple of the variant types so we can look over the tuple's types
658
699
  using Tuple_T = std::tuple<Types...>;
@@ -665,7 +706,7 @@ namespace Rice::detail
665
706
  return result;
666
707
  }
667
708
 
668
- static VALUE convert(std::variant<Types...>& data, bool takeOwnership = false)
709
+ static VALUE convert(const std::variant<Types...>& data, bool takeOwnership = false)
669
710
  {
670
711
  auto indices = std::make_index_sequence<std::variant_size_v<std::variant<Types...>>>{};
671
712
  return convertIterator(data, takeOwnership, indices);
@@ -755,6 +796,7 @@ namespace Rice::detail
755
796
  };
756
797
  }
757
798
 
799
+
758
800
  // ========= pair.hpp =========
759
801
 
760
802
 
@@ -2331,6 +2373,11 @@ namespace Rice
2331
2373
  }
2332
2374
  }
2333
2375
 
2376
+ bool is_convertible(VALUE value)
2377
+ {
2378
+ return rb_type(value) == RUBY_T_ARRAY;
2379
+ }
2380
+
2334
2381
  private:
2335
2382
  Arg* arg_ = nullptr;
2336
2383
  };
@@ -2378,6 +2425,11 @@ namespace Rice
2378
2425
  }
2379
2426
  }
2380
2427
 
2428
+ bool is_convertible(VALUE value)
2429
+ {
2430
+ return rb_type(value) == RUBY_T_ARRAY;
2431
+ }
2432
+
2381
2433
  private:
2382
2434
  Arg* arg_ = nullptr;
2383
2435
  std::vector<T> converted_;
@@ -2413,10 +2465,16 @@ namespace Rice
2413
2465
  }
2414
2466
  }
2415
2467
 
2468
+ bool is_convertible(VALUE value)
2469
+ {
2470
+ return rb_type(value) == RUBY_T_ARRAY;
2471
+ }
2472
+
2416
2473
  private:
2417
2474
  std::vector<T> converted_;
2418
2475
  };
2419
2476
  }
2420
2477
  }
2421
2478
 
2479
+
2422
2480
  #endif // Rice__stl__hpp_
data/lib/mkmf-rice.rb CHANGED
@@ -126,5 +126,7 @@ end
126
126
  if IS_DARWIN
127
127
  have_library('c++')
128
128
  elsif !IS_MSWIN
129
- have_library('stdc++')
129
+ if !have_library('stdc++fs')
130
+ have_library('stdc++')
131
+ end
130
132
  end
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rice
2
- VERSION = "4.1.0"
2
+ VERSION = "4.2.1"
3
3
  end
@@ -95,6 +95,9 @@ namespace Rice::detail
95
95
  // Figure out what self is
96
96
  Class_T getReceiver(VALUE self);
97
97
 
98
+ // Throw an exception when wrapper cannot be extracted
99
+ [[noreturn]] void noWrapper(const VALUE klass, const std::string& wrapper);
100
+
98
101
  // Do we need to keep alive any arguments?
99
102
  void checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues);
100
103
 
@@ -113,4 +116,4 @@ namespace Rice::detail
113
116
  }
114
117
  #include "NativeFunction.ipp"
115
118
 
116
- #endif // Rice__detail__Native_Function__hpp_
119
+ #endif // Rice__detail__Native_Function__hpp_
@@ -1,6 +1,7 @@
1
1
  #include <array>
2
2
  #include <algorithm>
3
3
  #include <stdexcept>
4
+ #include <sstream>
4
5
 
5
6
  #include "cpp_protect.hpp"
6
7
  #include "to_ruby_defn.hpp"
@@ -38,7 +39,7 @@ namespace Rice::detail
38
39
  NativeFunction<From_Ruby_T, Function_T, IsMethod>::NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
39
40
  : klass_(klass), method_name_(method_name), function_(function), methodInfo_(methodInfo)
40
41
  {
41
- // Create a tuple of NativeArgs that will convert the Ruby values to native values. For
42
+ // Create a tuple of NativeArgs that will convert the Ruby values to native values. For
42
43
  // builtin types NativeArgs will keep a copy of the native value so that it
43
44
  // can be passed by reference or pointer to the native function. For non-builtin types
44
45
  // it will just pass the value through.
@@ -164,7 +165,7 @@ namespace Rice::detail
164
165
  {
165
166
  // Call the native method and get the result
166
167
  Return_T nativeResult = std::apply(this->function_, nativeArgs);
167
-
168
+
168
169
  // Return the result
169
170
  return this->toRuby_.convert(nativeResult);
170
171
  }
@@ -215,15 +216,38 @@ namespace Rice::detail
215
216
  }
216
217
  }
217
218
 
219
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
220
+ void NativeFunction<From_Ruby_T, Function_T, IsMethod>::noWrapper(const VALUE klass, const std::string& wrapper)
221
+ {
222
+ std::stringstream message;
223
+
224
+ message << "When calling the method `";
225
+ message << this->method_name_;
226
+ message << "' we could not find the wrapper for the '";
227
+ message << rb_obj_classname(klass);
228
+ message << "' ";
229
+ message << wrapper;
230
+ message << " type. You should not use keepAlive() on a Return or Arg that is a builtin Rice type.";
231
+
232
+ throw std::runtime_error(message.str());
233
+ }
234
+
218
235
  template<typename From_Ruby_T, typename Function_T, bool IsMethod>
219
236
  void NativeFunction<From_Ruby_T, Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues)
220
237
  {
221
- // Check function arguments
238
+ // selfWrapper will be nullptr if this(self) is a builtin type and not an external(wrapped) type
239
+ // it is highly unlikely that keepAlive is used in this case but we check anyway
222
240
  Wrapper* selfWrapper = getWrapper(self);
241
+
242
+ // Check function arguments
223
243
  for (const Arg& arg : (*this->methodInfo_))
224
244
  {
225
245
  if (arg.isKeepAlive())
226
246
  {
247
+ if (selfWrapper == nullptr)
248
+ {
249
+ noWrapper(self, "self");
250
+ }
227
251
  selfWrapper->addKeepAlive(rubyValues[arg.position]);
228
252
  }
229
253
  }
@@ -231,7 +255,17 @@ namespace Rice::detail
231
255
  // Check return value
232
256
  if (this->methodInfo_->returnInfo.isKeepAlive())
233
257
  {
258
+ if (selfWrapper == nullptr)
259
+ {
260
+ noWrapper(self, "self");
261
+ }
262
+
263
+ // returnWrapper will be nullptr if returnValue is a built-in type and not an external(wrapped) type
234
264
  Wrapper* returnWrapper = getWrapper(returnValue);
265
+ if (returnWrapper == nullptr)
266
+ {
267
+ noWrapper(returnValue, "return");
268
+ }
235
269
  returnWrapper->addKeepAlive(self);
236
270
  }
237
271
  }
@@ -193,7 +193,7 @@ namespace Rice::detail
193
193
  #pragma GCC diagnostic push
194
194
  #pragma GCC diagnostic ignored "-Warray-bounds"
195
195
  #endif
196
- return static_cast<Wrapper*>(RTYPEDDATA_DATA(value));
196
+ return RTYPEDDATA_P(value) ? static_cast<Wrapper*>(RTYPEDDATA_DATA(value)) : nullptr;
197
197
  #ifdef __GNUC__
198
198
  #pragma GCC diagnostic pop
199
199
  #endif
@@ -2,9 +2,18 @@
2
2
  #define Rice__detail__cpp_protect__hpp_
3
3
 
4
4
  #include <regex>
5
- #include <filesystem>
6
5
  #include <stdexcept>
7
6
 
7
+ #if __has_include(<filesystem>)
8
+ #include <filesystem>
9
+ namespace fs = std::filesystem;
10
+ #elif __has_include(<experimental/filesystem>)
11
+ #include <experimental/filesystem>
12
+ namespace fs = std::experimental::filesystem;
13
+ #else
14
+ #error "no filesystem include found :'("
15
+ #endif
16
+
8
17
  #include "Jump_Tag.hpp"
9
18
  #include "../Exception_defn.hpp"
10
19
 
@@ -46,7 +55,7 @@ namespace Rice::detail
46
55
  {
47
56
  rb_exc_raise(rb_exc_new2(rb_eArgError, ex.what()));
48
57
  }
49
- catch (std::filesystem::filesystem_error const& ex)
58
+ catch (fs::filesystem_error const& ex)
50
59
  {
51
60
  rb_exc_raise(rb_exc_new2(rb_eIOError, ex.what()));
52
61
  }
@@ -1,5 +1,4 @@
1
1
  require 'bundler/setup'
2
- require 'rice'
3
2
  require 'mkmf-rice'
4
3
 
5
4
  create_makefile('sample_callbacks')
@@ -1,5 +1,4 @@
1
1
  require 'bundler/setup'
2
- require 'rice'
3
2
  require 'mkmf-rice'
4
3
 
5
4
  create_makefile('sample_enum')
@@ -1,5 +1,4 @@
1
1
  require 'bundler/setup'
2
- require 'rice'
3
2
  require 'mkmf-rice'
4
3
 
5
4
  create_makefile('animals')
@@ -1,5 +1,4 @@
1
1
  require 'bundler/setup'
2
- require 'rice'
3
2
  require 'mkmf-rice'
4
3
 
5
4
  create_makefile('map')
data/test/embed_ruby.cpp CHANGED
@@ -15,6 +15,12 @@ void embed_ruby()
15
15
  ruby_init();
16
16
  ruby_init_loadpath();
17
17
 
18
+ #if RUBY_API_VERSION_MAJOR == 3 && RUBY_API_VERSION_MINOR >= 3
19
+ // Force the prelude / builtins
20
+ char *opts[] = { "ruby", "-e;" };
21
+ ruby_options(2, opts);
22
+ #endif
23
+
18
24
  initialized__ = true;
19
25
  }
20
26
  }
@@ -1,5 +1,4 @@
1
1
  require 'bundler/setup'
2
- require 'rice'
3
2
  require 'mkmf-rice'
4
3
 
5
4
  create_makefile('t1')
@@ -1,5 +1,4 @@
1
1
  require 'bundler/setup'
2
- require 'rice'
3
2
  require 'mkmf-rice'
4
3
 
5
4
  create_makefile('t2')
data/test/extconf.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'bundler/setup'
2
- require 'rice'
3
2
  require 'mkmf-rice'
4
3
  require 'rbconfig'
5
4
 
@@ -1,4 +1,4 @@
1
- #include <assert.h>
1
+ #include <assert.h>
2
2
 
3
3
  #include "unittest.hpp"
4
4
  #include "embed_ruby.hpp"
@@ -60,9 +60,9 @@ TESTCASE(attributes)
60
60
  ASSERT_EXCEPTION_CHECK(
61
61
  Exception,
62
62
  o.call("read_char=", "some text"),
63
- ASSERT_EQUAL("undefined method `read_char=' for :DataStruct", ex.what())
63
+ ASSERT(std::string(ex.what()).find("undefined method `read_char='") == 0)
64
64
  );
65
-
65
+
66
66
  // Test writeonly attribute
67
67
  result = o.call("write_int=", 5);
68
68
  ASSERT_EQUAL(5, detail::From_Ruby<int>().convert(result.value()));
@@ -70,7 +70,7 @@ TESTCASE(attributes)
70
70
  ASSERT_EXCEPTION_CHECK(
71
71
  Exception,
72
72
  o.call("write_int", 3),
73
- ASSERT_EQUAL("undefined method `write_int' for :DataStruct", ex.what())
73
+ ASSERT(std::string(ex.what()).find("undefined method `write_int'") == 0)
74
74
  );
75
75
 
76
76
  // Test readwrite attribute
@@ -101,7 +101,7 @@ TESTCASE(static_attributes)
101
101
  ASSERT_EXCEPTION_CHECK(
102
102
  Exception,
103
103
  c.call("static_string=", true),
104
- ASSERT_EQUAL("undefined method `static_string=' for DataStruct:Class", ex.what())
104
+ ASSERT(std::string(ex.what()).find("undefined method `static_string='") == 0)
105
105
  );
106
106
  }
107
107
 
@@ -127,7 +127,7 @@ TESTCASE(not_defined)
127
127
  {
128
128
  Data_Type<DataStruct> c = define_class<DataStruct>("DataStruct");
129
129
 
130
- #ifdef _MSC_VER
130
+ #ifdef _MSC_VER
131
131
  const char* message = "Type is not defined with Rice: class `anonymous namespace'::SomeClass";
132
132
  #else
133
133
  const char* message = "Type is not defined with Rice: (anonymous namespace)::SomeClass";
@@ -0,0 +1,80 @@
1
+ #include "unittest.hpp"
2
+ #include "embed_ruby.hpp"
3
+ #include <rice/rice.hpp>
4
+ #include <rice/stl.hpp>
5
+
6
+ using namespace Rice;
7
+
8
+ TESTSUITE(Keep_Alive_No_Wrapper);
9
+
10
+ namespace
11
+ {
12
+ class Animal
13
+ {
14
+ public:
15
+ Animal(char const * name) : name_(name) {}
16
+ char const * getName() { return name_; }
17
+ virtual ~Animal() = default;
18
+ private:
19
+ char const * name_;
20
+ };
21
+
22
+ class Zoo
23
+ {
24
+ public:
25
+ Zoo(void)
26
+ {
27
+ pets_.push_back(new Animal("Bear"));
28
+ pets_.push_back(new Animal("Tiger"));
29
+ pets_.push_back(new Animal("Lion"));
30
+ }
31
+
32
+ ~Zoo()
33
+ {
34
+ for(auto pet : pets_)
35
+ {
36
+ delete pet;
37
+ }
38
+ pets_.clear();
39
+ }
40
+
41
+ Object getPets(void) {
42
+ Array pets;
43
+ for(auto p: pets_) {
44
+ pets.push(p);
45
+ }
46
+ return pets;
47
+ }
48
+
49
+ private:
50
+ std::vector<Animal*> pets_;
51
+ };
52
+ }
53
+
54
+ SETUP(Keep_Alive_No_Wrapper)
55
+ {
56
+ embed_ruby();
57
+ }
58
+
59
+ TESTCASE(test_keep_alive_no_wrapper)
60
+ {
61
+ define_class<Animal>("Animal")
62
+ .define_constructor(Constructor<Animal, char const *>())
63
+ .define_method("get_name", &Animal::getName);
64
+
65
+ define_class<Zoo>("Zoo")
66
+ .define_constructor(Constructor<Zoo>())
67
+ .define_method("get_pets", &Zoo::getPets, Return().keepAlive());
68
+
69
+ Module m = define_module("TestingModule");
70
+ Object zoo = m.module_eval("@zoo = Zoo.new");
71
+
72
+ // get_pets returns an Array (builtin type) so Return().keepAlive()
73
+ // shall result in std::runtime_error
74
+ ASSERT_EXCEPTION_CHECK(
75
+ Exception,
76
+ m.module_eval("@zoo.get_pets.each do |pet| puts pet.name; end"),
77
+ ASSERT_EQUAL("When calling the method `get_pets' we could not find the wrapper for the 'Array' return type. You should not use keepAlive() on a Return or Arg that is a builtin Rice type.",
78
+ ex.what())
79
+ );
80
+ }
data/test/test_Object.cpp CHANGED
@@ -174,20 +174,29 @@ TESTCASE(call_return_rice_object)
174
174
 
175
175
  TESTCASE(call_with_keywords)
176
176
  {
177
- Module kernel = Module("Kernel");
177
+ Module m(anonymous_module());
178
+
179
+ m.module_eval(R"(
180
+ def self.keywords_test(value, exception:)
181
+ if exception
182
+ raise "An exception!"
183
+ end
178
184
 
185
+ value
186
+ end
187
+ )");
179
188
 
180
189
  Hash keywords;
181
190
  keywords[":exception"] = false;
182
- Object result = kernel.call("Integer", "charlie", keywords);
183
- ASSERT_EQUAL(Qnil, result.value());
191
+ Object result = m.call_kw("keywords_test", "charlie", keywords);
192
+ ASSERT_EQUAL("charlie", detail::From_Ruby<const char*>().convert(result.value()));
184
193
 
185
194
  keywords[":exception"] = true;
186
195
 
187
196
  ASSERT_EXCEPTION_CHECK(
188
197
  Exception,
189
- kernel.call("Integer", "charlie", keywords),
190
- ASSERT_EQUAL("invalid value for Integer(): \"charlie\"", ex.what())
198
+ m.call_kw("keywords_test", "charlie", keywords),
199
+ ASSERT_EQUAL("An exception!", ex.what())
191
200
  );
192
201
  }
193
202
 
@@ -240,4 +249,4 @@ TESTCASE(test_mark)
240
249
  Object o(INT2NUM(42));
241
250
  rb_gc_start();
242
251
  ASSERT_EQUAL(42, detail::From_Ruby<int>().convert(o.value()));
243
- }
252
+ }
@@ -109,6 +109,11 @@ SETUP(SmartPointer)
109
109
  define_global_function("extract_flag_unique_ptr_ref", &extractFlagUniquePtrRef);
110
110
  define_global_function("extract_flag_shared_ptr", &extractFlagSharedPtr);
111
111
  define_global_function("extract_flag_shared_ptr_ref", &extractFlagSharedPtrRef);
112
+
113
+ define_global_function("extract_flag_shared_ptr_with_default", &extractFlagSharedPtr,
114
+ Arg("myClass") = std::make_shared<MyClass>());
115
+ define_global_function("extract_flag_shared_ptr_ref_with_default", &extractFlagSharedPtrRef,
116
+ Arg("myClass") = std::make_shared<MyClass>());
112
117
  }
113
118
 
114
119
  TESTCASE(TransferOwnership)
@@ -169,6 +174,7 @@ TESTCASE(UniquePtrRefParameter)
169
174
  extract_flag_unique_ptr_ref(my_class))";
170
175
 
171
176
  Object result = m.module_eval(code);
177
+ ASSERT_EQUAL(7, detail::From_Ruby<int>().convert(result));
172
178
  }
173
179
 
174
180
  TESTCASE(SharedPtrParameter)
@@ -179,10 +185,11 @@ TESTCASE(SharedPtrParameter)
179
185
 
180
186
  std::string code = R"(factory = Factory.new
181
187
  my_class = factory.share
182
- my_class.set_flag(7)
188
+ my_class.set_flag(8)
183
189
  extract_flag_shared_ptr(my_class))";
184
-
190
+
185
191
  Object result = m.module_eval(code);
192
+ ASSERT_EQUAL(8, detail::From_Ruby<int>().convert(result));
186
193
  }
187
194
 
188
195
  TESTCASE(SharedPtrRefParameter)
@@ -193,8 +200,41 @@ TESTCASE(SharedPtrRefParameter)
193
200
 
194
201
  std::string code = R"(factory = Factory.new
195
202
  my_class = factory.share
196
- my_class.set_flag(7)
203
+ my_class.set_flag(9)
197
204
  extract_flag_shared_ptr_ref(my_class))";
198
205
 
199
206
  Object result = m.module_eval(code);
200
- }
207
+ ASSERT_EQUAL(9, detail::From_Ruby<int>().convert(result));
208
+ }
209
+
210
+ TESTCASE(SharedPtrDefaultParameter)
211
+ {
212
+ MyClass::reset();
213
+
214
+ Module m = define_module("TestingModule");
215
+
216
+ std::string code = R"(factory = Factory.new
217
+ my_class = factory.share
218
+ my_class.set_flag(7)
219
+ extract_flag_shared_ptr_with_default())";
220
+
221
+ Object result = m.module_eval(code);
222
+ // The default value kicks in and ignores any previous pointer
223
+ ASSERT_EQUAL(0, detail::From_Ruby<int>().convert(result));
224
+ }
225
+
226
+ TESTCASE(SharedPtrRefDefaultParameter)
227
+ {
228
+ MyClass::reset();
229
+
230
+ Module m = define_module("TestingModule");
231
+
232
+ std::string code = R"(factory = Factory.new
233
+ my_class = factory.share
234
+ my_class.set_flag(7)
235
+ extract_flag_shared_ptr_ref_with_default())";
236
+
237
+ Object result = m.module_eval(code);
238
+ // The default value kicks in and ignores any previous pointer
239
+ ASSERT_EQUAL(0, detail::From_Ruby<int>().convert(result));
240
+ }
@@ -27,7 +27,10 @@ TESTCASE(std_string_to_ruby_encoding)
27
27
  Object object(value);
28
28
  Object encoding = object.call("encoding");
29
29
  Object encodingName = encoding.call("name");
30
- ASSERT_EQUAL("ASCII-8BIT", detail::From_Ruby<std::string>().convert(encodingName));
30
+ std::string result = detail::From_Ruby<std::string>().convert(encodingName);
31
+ if(result != "ASCII-8BIT" && result != "US-ASCII" && result != "UTF-8") {
32
+ FAIL("Encoding incorrect", "ASCII-8BIT, US-ASCII, or UTF-8 (Windows)", result);
33
+ }
31
34
  }
32
35
 
33
36
  TESTCASE(std_string_to_ruby_encoding_utf8)
@@ -71,4 +74,4 @@ TESTCASE(std_string_from_ruby_with_binary)
71
74
  std::string got = detail::From_Ruby<std::string>().convert(rb_str_new("\000test", 5));
72
75
  ASSERT_EQUAL(5ul, got.length());
73
76
  ASSERT_EQUAL(std::string("\000test", 5), got);
74
- }
77
+ }
@@ -4,6 +4,7 @@
4
4
  #include <rice/stl.hpp>
5
5
 
6
6
  #include <variant>
7
+ #include <complex>
7
8
 
8
9
  using namespace Rice;
9
10
 
@@ -11,7 +12,14 @@ TESTSUITE(Variant);
11
12
 
12
13
  namespace
13
14
  {
14
- using Intrinsic_Variant_T = std::variant<std::string, double, bool, int>;
15
+ using Intrinsic_Variant_T = std::variant<
16
+ std::string,
17
+ std::complex<double>,
18
+ std::vector<int>,
19
+ double,
20
+ bool,
21
+ int
22
+ >;
15
23
 
16
24
  inline std::ostream& operator<<(std::ostream& stream, Intrinsic_Variant_T const& variant)
17
25
  {
@@ -30,6 +38,19 @@ namespace
30
38
  return result;
31
39
  }
32
40
 
41
+ Intrinsic_Variant_T variantComplex()
42
+ {
43
+ using namespace std::complex_literals;
44
+ Intrinsic_Variant_T result { 1i };
45
+ return result;
46
+ }
47
+
48
+ Intrinsic_Variant_T variantVector()
49
+ {
50
+ Intrinsic_Variant_T result { std::vector<int>{1, 2, 3} };
51
+ return result;
52
+ }
53
+
33
54
  Intrinsic_Variant_T variantDouble()
34
55
  {
35
56
  Intrinsic_Variant_T result { 3.3 };
@@ -73,6 +94,8 @@ void makeIntrinsicVariant()
73
94
  define_class<MyClass>("MyClass").
74
95
  define_constructor(Constructor<MyClass>()).
75
96
  define_method("variant_string", &MyClass::variantString).
97
+ define_method("variant_complex", &MyClass::variantComplex).
98
+ define_method("variant_vector", &MyClass::variantVector).
76
99
  define_method("variant_double", &MyClass::variantDouble).
77
100
  define_method("variant_bool_true", &MyClass::variantBoolTrue).
78
101
  define_method("variant_bool_false", &MyClass::variantBoolFalse).
@@ -83,12 +106,21 @@ void makeIntrinsicVariant()
83
106
 
84
107
  TESTCASE(IntrinsicReturns)
85
108
  {
109
+ using namespace std::complex_literals;
110
+
86
111
  Module m = define_module("Testing");
87
112
  Object myClass = m.module_eval("MyClass.new");
88
113
 
89
114
  Object result = myClass.call("variant_string");
90
115
  ASSERT_EQUAL("a string", detail::From_Ruby<std::string>().convert(result));
91
-
116
+
117
+ result = myClass.call("variant_complex");
118
+ ASSERT_EQUAL(1i, detail::From_Ruby<std::complex<double>>().convert(result));
119
+
120
+ result = myClass.call("variant_vector");
121
+ std::vector<int> converted = detail::From_Ruby<std::vector<int>>().convert(result);
122
+ ASSERT_EQUAL(3, converted.size());
123
+
92
124
  result = myClass.call("variant_double");
93
125
  ASSERT_EQUAL(3.3, detail::From_Ruby<double>().convert(result));
94
126
 
@@ -104,6 +136,8 @@ TESTCASE(IntrinsicReturns)
104
136
 
105
137
  TESTCASE(IntrinsicRoundtrip)
106
138
  {
139
+ using namespace std::complex_literals;
140
+
107
141
  Module m = define_module("Testing");
108
142
  Object myClass = m.module_eval("MyClass.new");
109
143
 
@@ -112,6 +146,17 @@ TESTCASE(IntrinsicRoundtrip)
112
146
  Object result = m.module_eval(code);
113
147
  ASSERT_EQUAL("roundtrip string", detail::From_Ruby<std::string>().convert(result));
114
148
 
149
+ code = R"(my_class = MyClass.new
150
+ my_class.roundtrip(Complex(2, 3)))";
151
+ result = m.module_eval(code);
152
+ ASSERT_EQUAL((2.0 + 3i), detail::From_Ruby<std::complex<double>>().convert(result));
153
+
154
+ code = R"(my_class = MyClass.new
155
+ my_class.roundtrip([1, 2, 3]))";
156
+ result = m.module_eval(code);
157
+ std::vector<int> expected = {1, 2, 3};
158
+ ASSERT_EQUAL(expected, detail::From_Ruby<std::vector<int>>().convert(result));
159
+
115
160
  code = R"(my_class = MyClass.new
116
161
  my_class.roundtrip(44.4))";
117
162
  result = m.module_eval(code);
@@ -150,7 +195,7 @@ TESTCASE(VariantAttribute)
150
195
  ASSERT_EQUAL(77.7, detail::From_Ruby<double>().convert(result));
151
196
  result = myClass.call("variant_attr");
152
197
  ASSERT_EQUAL(77.7, detail::From_Ruby<double>().convert(result));
153
-
198
+
154
199
  result = myClass.call("variant_attr=", true);
155
200
  ASSERT(detail::From_Ruby<bool>().convert(result));
156
201
  result = myClass.call("variant_attr");
@@ -298,4 +343,4 @@ TESTCASE(ClassRoundtripRef)
298
343
  hello = instance2.call("say_hello");
299
344
  ASSERT_EQUAL("Hi from MyClass2", detail::From_Ruby<std::string>().convert(hello));
300
345
  }
301
- #endif
346
+ #endif
@@ -229,7 +229,7 @@ TESTCASE(unsigned_long_long_from_ruby)
229
229
  ASSERT_EXCEPTION_CHECK(
230
230
  Exception,
231
231
  detail::From_Ruby<unsigned long long>().convert(rb_str_new2("bad value")),
232
- ASSERT_EQUAL("no implicit conversion from string", ex.what())
232
+ ASSERT(std::string(ex.what()).find("no implicit conversion") == 0)
233
233
  );
234
234
  }
235
235
 
@@ -396,4 +396,4 @@ TESTCASE(char_star_from_ruby)
396
396
  detail::From_Ruby<const char*>().convert(rb_float_new(11.11)),
397
397
  ASSERT_EQUAL("wrong argument type Float (expected String)", ex.what())
398
398
  );
399
- }
399
+ }
data/test/unittest.hpp CHANGED
@@ -236,7 +236,7 @@ void assert_equal(
236
236
 
237
237
  if constexpr (is_streamable<std::stringstream, T>::value && is_streamable<std::stringstream, U>::value)
238
238
  {
239
- strm << s_t << " != " << s_u;
239
+ strm << s_t << " != " << s_u << " (" << u << ") ";
240
240
  }
241
241
  strm << " at " << file << ":" << line;
242
242
  throw Assertion_Failed(strm.str());
@@ -263,6 +263,14 @@ void assert_not_equal(
263
263
  }
264
264
  }
265
265
 
266
+ #define FAIL(message, expect, got) \
267
+ do \
268
+ { \
269
+ std::stringstream strm; \
270
+ strm << message << " expected: " << (expect) << " got: " << (got); \
271
+ throw Assertion_Failed(strm.str()); \
272
+ } while(0)
273
+
266
274
  #define ASSERT_EQUAL(x, y) \
267
275
  do \
268
276
  { \
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rice
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 4.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Brannan
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-04-23 00:00:00.000000000 Z
13
+ date: 2024-01-20 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -185,6 +185,7 @@ files:
185
185
  - test/test_Iterator.cpp
186
186
  - test/test_Jump_Tag.cpp
187
187
  - test/test_Keep_Alive.cpp
188
+ - test/test_Keep_Alive_No_Wrapper.cpp
188
189
  - test/test_Memory_Management.cpp
189
190
  - test/test_Module.cpp
190
191
  - test/test_Object.cpp
@@ -230,7 +231,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
230
231
  - !ruby/object:Gem::Version
231
232
  version: '0'
232
233
  requirements: []
233
- rubygems_version: 3.4.12
234
+ rubygems_version: 3.5.4
234
235
  signing_key:
235
236
  specification_version: 4
236
237
  summary: Ruby Interface for C++ Extensions