rice 4.11.5 → 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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -4
  3. data/include/rice/rice.hpp +145 -102
  4. data/include/rice/stl.hpp +229 -69
  5. data/lib/rice/version.rb +1 -1
  6. data/rice/Constructor.ipp +3 -3
  7. data/rice/Data_Object.ipp +0 -42
  8. data/rice/JumpException.ipp +1 -0
  9. data/rice/Reference.hpp +2 -2
  10. data/rice/Reference.ipp +1 -1
  11. data/rice/cpp_api/Encoding.ipp +0 -48
  12. data/rice/cpp_api/Hash.ipp +19 -0
  13. data/rice/cpp_api/Object.hpp +2 -2
  14. data/rice/cpp_api/Object.ipp +3 -3
  15. data/rice/cpp_api/String.ipp +19 -0
  16. data/rice/detail/Parameter.ipp +11 -0
  17. data/rice/detail/Proc.ipp +19 -0
  18. data/rice/detail/Wrapper.ipp +3 -3
  19. data/rice/detail/from_ruby.hpp +1 -0
  20. data/rice/detail/to_ruby.ipp +58 -1
  21. data/rice/stl/function.ipp +142 -2
  22. data/rice/stl/map.ipp +2 -10
  23. data/rice/stl/multimap.ipp +2 -10
  24. data/rice/stl/optional.ipp +18 -0
  25. data/rice/stl/reference_wrapper.ipp +18 -0
  26. data/rice/stl/set.ipp +24 -25
  27. data/rice/stl/unique_ptr.ipp +3 -0
  28. data/rice/stl/unordered_map.ipp +2 -10
  29. data/rice/stl/vector.ipp +18 -15
  30. data/rice/traits/rice_traits.hpp +3 -0
  31. data/test/test_Attribute.cpp +6 -6
  32. data/test/test_Constructor.cpp +140 -1
  33. data/test/test_File.cpp +2 -3
  34. data/test/test_From_Ruby.cpp +3 -3
  35. data/test/test_Hash.cpp +8 -0
  36. data/test/test_Object.cpp +76 -0
  37. data/test/test_Overloads.cpp +74 -1
  38. data/test/test_Proc.cpp +11 -1
  39. data/test/test_Reference.cpp +20 -1
  40. data/test/test_Stl_Exception.cpp +2 -5
  41. data/test/test_Stl_Function.cpp +72 -7
  42. data/test/test_Stl_Map.cpp +10 -1
  43. data/test/test_Stl_Multimap.cpp +11 -2
  44. data/test/test_Stl_Optional.cpp +9 -0
  45. data/test/test_Stl_Reference_Wrapper.cpp +11 -0
  46. data/test/test_Stl_Set.cpp +12 -0
  47. data/test/test_Stl_Unordered_Map.cpp +10 -1
  48. data/test/test_Stl_Vector.cpp +130 -11
  49. data/test/test_String.cpp +7 -0
  50. data/test/test_To_Ruby.cpp +24 -1
  51. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad1a2f20f3096248c8bc064f5c091f3d70b326b41bbf3664fdab408f646a8fd3
4
- data.tar.gz: ed6fe5f0813e2cfee4082ddb7604e7c8f0aba9c808c0b746e6c4c801e74dda19
3
+ metadata.gz: ee35bcabc987b70a0614689a9fe28f60ceeb0d63ad492ebc1d1914acebfb63d8
4
+ data.tar.gz: 40451a4f9531b935f70aefb2fc2e7ecb5bd75a546035fb7c3eed3af8f47f2911
5
5
  SHA512:
6
- metadata.gz: 98346333fe05760e510b0485be685c55e4866e7d79412c53dcb3226796bcb6b2f42a5dcbc748263d65566fb2bf545c3a7faae408b5f9db59b37c4c0784ad15f6
7
- data.tar.gz: aabd681ffe8bc092303857466baddec01efea860e42a801e56d49137feb360229a243c1adcb2770fd5be4ffc791505e20bb84e55af867a3c765be8b847b92d8e
6
+ metadata.gz: 8cc76f3c9395f94bf5830cc60ad5c327ab9941b0f532ac095553f774481d04cf72ddc2abf7db167438063c7e3e91455a2cc660fbd37d2e917276b30bef6a3203
7
+ data.tar.gz: 4ffb5214a1bf6dfeda4222a6c6c7006313820eac1cf17fdf36994a70099506ec591be20cd77db52f8034ed47cb65297d0b80c9dd3ff08aed70ade273f1cc492e
data/CHANGELOG.md CHANGED
@@ -1,12 +1,30 @@
1
1
  # Changelog
2
2
 
3
- ## 4.11.5 (2026-03-23)
3
+ ## 4.12.0 (2026-04-09)
4
4
 
5
5
  ### Bug Fixes
6
6
 
7
- * Fix Valgrind invalid reads caused by stale GC root addresses. This was caused by an unforunate interaction with minitest when registering exit blocks via rb_set_end_proc. Fixed by switching to ruby_vm_at_exit.
8
- * Add -Wno-array-bounds for g++ 15 false positive in Ruby's RSTRING macro to avoid g++15 false positive warning when inlining through Ruby's RSTRING macro (ruby/internal/core/rstring.h)
9
- * Document g++ 15.2.1 / binutils 2.45.1 issue where LTO triggers an internal assembler segfault. This is not something Rice can fix, but the workaround (-fno-lto) is documented in build_settings.md.
7
+ * Fix undefined behavior when deleting polymorphic classes with non-virtual destructors
8
+ * Fix overload resolution to prefer `T&` or `const T&` over `T&&` after perfect forwarding changes
9
+ * Fix `Reference<T>` constructor overload resolution so forwarded arguments bind to the typed overload instead of falling through to `Reference(VALUE)`
10
+ * Reject incomplete `std::unique_ptr` bindings at compile time
11
+
12
+ ### Enhancements
13
+
14
+ * Forward `Object::call` arguments without copying
15
+ * Forward Rice constructor rvalue arguments
16
+ * Forward `std::function` callback arguments
17
+ * Add support for `To_Ruby<T&>` for types missing it
18
+ * Allow raw Ruby procs and lambdas to convert directly to `std::function` parameters while keeping the callable alive with `Pin`
19
+ * Only define equality-based STL container methods when the contained type supports C++ equality comparison
20
+
21
+ ## 4.11.5 (2026-03-24)
22
+
23
+ ### Bug Fixes
24
+
25
+ * Fix Valgrind invalid reads caused by stale GC root addresses (#399)
26
+ * Fix `rb_gc_register_address()` triggering GC before Anchor stores heap object — use `RB_GC_GUARD` to keep VALUE alive
27
+ * Fix gem packaging warning
10
28
 
11
29
  ## 4.11.4 (2026-03-13)
12
30
 
@@ -189,6 +189,9 @@ namespace Rice
189
189
  constexpr bool is_ostreamable_v = is_ostreamable<T>::value;
190
190
 
191
191
  // Is the type comparable?
192
+ // Libraries with unconstrained operator== declarations may specialize this
193
+ // trait to false when equality is not actually usable by Rice's STL
194
+ // wrappers.
192
195
  template<typename T, typename SFINAE = void>
193
196
  struct is_comparable : std::false_type {};
194
197
 
@@ -1094,6 +1097,7 @@ namespace Rice
1094
1097
  break;
1095
1098
  case RUBY_TAG_THROW:
1096
1099
  this->message_ = "Unexpected throw";
1100
+ break;
1097
1101
  case RUBY_TAG_RAISE:
1098
1102
  this->message_ = "Ruby exception was thrown";
1099
1103
  break;
@@ -1590,6 +1594,7 @@ namespace Rice::detail
1590
1594
  static constexpr double SignedToUnsigned = 0.5;// Penalty for signed to unsigned (can't represent negatives)
1591
1595
  static constexpr double FloatToInt = 0.5; // Domain change penalty when converting float to int (lossy)
1592
1596
  static constexpr double ConstMismatch = 0.99; // Penalty for const mismatch
1597
+ static constexpr double RValueMismatch = 0.98; // Prefer borrowing wrapped objects over moving from them
1593
1598
  };
1594
1599
  }
1595
1600
 
@@ -2032,7 +2037,7 @@ namespace Rice
2032
2037
  * \endcode
2033
2038
  */
2034
2039
  template<typename ...Parameter_Ts>
2035
- Object call(Identifier id, Parameter_Ts... args) const;
2040
+ Object call(Identifier id, Parameter_Ts&&... args) const;
2036
2041
 
2037
2042
  //! Call the Ruby method specified by 'id' on object 'obj'.
2038
2043
  /*! Pass in arguments (arg1, arg2, ...). The arguments will be converted to
@@ -2055,7 +2060,7 @@ namespace Rice
2055
2060
  * \endcode
2056
2061
  */
2057
2062
  template<typename ...Parameter_Ts>
2058
- Object call_kw(Identifier id, Parameter_Ts... args) const;
2063
+ Object call_kw(Identifier id, Parameter_Ts&&... args) const;
2059
2064
 
2060
2065
  //! Vectorized call.
2061
2066
  /*! Calls the method identified by id with the list of arguments
@@ -4278,7 +4283,7 @@ namespace Rice
4278
4283
 
4279
4284
  public:
4280
4285
  Reference();
4281
- Reference(T& data);
4286
+ Reference(const T& data);
4282
4287
  Reference(VALUE value);
4283
4288
  T& get();
4284
4289
 
@@ -4287,7 +4292,7 @@ namespace Rice
4287
4292
  };
4288
4293
 
4289
4294
  // Specialization needed when VALUE type matches T, causing constructor ambiguity
4290
- // between Reference(T&) and Reference(VALUE). VALUE is unsigned long when
4295
+ // between Reference(const T&) and Reference(VALUE). VALUE is unsigned long when
4291
4296
  // SIZEOF_LONG == SIZEOF_VOIDP (Linux/macOS) and unsigned long long when
4292
4297
  // SIZEOF_LONG_LONG == SIZEOF_VOIDP (Windows x64).
4293
4298
  #if SIZEOF_LONG == SIZEOF_VOIDP
@@ -4459,6 +4464,12 @@ namespace Rice::detail
4459
4464
  {
4460
4465
  result = Convertible::None;
4461
4466
  }
4467
+ // Existing wrapped Ruby objects should prefer borrowing overloads to
4468
+ // rvalue-reference overloads so they are not silently moved-from.
4469
+ else if constexpr (std::is_rvalue_reference_v<T>)
4470
+ {
4471
+ result = Convertible::RValueMismatch;
4472
+ }
4462
4473
  // It is ok to send a non-const value to a const parameter but
4463
4474
  // prefer non-const to non-const by slightly decreasing the score
4464
4475
  else if (!isConst && is_const_any_v<T>)
@@ -4495,6 +4506,11 @@ namespace Rice::detail
4495
4506
  {
4496
4507
  return this->fromRuby_.convert(valueOpt.value());
4497
4508
  }
4509
+ else if constexpr (std::is_rvalue_reference_v<T>)
4510
+ {
4511
+ // Rvalue-reference parameters cannot safely use stored default values.
4512
+ // Materializing them from std::any would require moving from shared state.
4513
+ }
4498
4514
  // Remember std::is_copy_constructible_v<std::vector<std::unique_ptr<T>>>> returns true. Sigh.
4499
4515
  // So special case vector handling
4500
4516
  else if constexpr (detail::is_std_vector_v<detail::intrinsic_type<T>>)
@@ -6459,6 +6475,25 @@ namespace Rice
6459
6475
  Arg* arg_ = nullptr;
6460
6476
  };
6461
6477
 
6478
+ template<>
6479
+ class To_Ruby<char*&>
6480
+ {
6481
+ public:
6482
+ To_Ruby() = default;
6483
+
6484
+ explicit To_Ruby(Arg* arg) : arg_(arg)
6485
+ {
6486
+ }
6487
+
6488
+ VALUE convert(const char* data)
6489
+ {
6490
+ return To_Ruby<char*>(arg_).convert(data);
6491
+ }
6492
+
6493
+ private:
6494
+ Arg* arg_ = nullptr;
6495
+ };
6496
+
6462
6497
  template<int N>
6463
6498
  class To_Ruby<char[N]>
6464
6499
  {
@@ -6491,6 +6526,25 @@ namespace Rice
6491
6526
  Arg* arg_ = nullptr;
6492
6527
  };
6493
6528
 
6529
+ template<int N>
6530
+ class To_Ruby<char(&)[N]>
6531
+ {
6532
+ public:
6533
+ To_Ruby() = default;
6534
+
6535
+ explicit To_Ruby(Arg* arg) : arg_(arg)
6536
+ {
6537
+ }
6538
+
6539
+ VALUE convert(const char (&buffer)[N])
6540
+ {
6541
+ return To_Ruby<char[N]>(arg_).convert(buffer);
6542
+ }
6543
+
6544
+ private:
6545
+ Arg* arg_ = nullptr;
6546
+ };
6547
+
6494
6548
  // =========== unsigned char ============
6495
6549
  template<>
6496
6550
  class To_Ruby<unsigned char>
@@ -7212,6 +7266,25 @@ namespace Rice
7212
7266
  Arg* arg_ = nullptr;
7213
7267
  };
7214
7268
 
7269
+ template<>
7270
+ class To_Ruby<std::nullptr_t&>
7271
+ {
7272
+ public:
7273
+ To_Ruby() = default;
7274
+
7275
+ explicit To_Ruby(Arg* arg) : arg_(arg)
7276
+ {
7277
+ }
7278
+
7279
+ VALUE convert(std::nullptr_t const)
7280
+ {
7281
+ return Qnil;
7282
+ }
7283
+
7284
+ private:
7285
+ Arg* arg_ = nullptr;
7286
+ };
7287
+
7215
7288
  // =========== void ============
7216
7289
  template<>
7217
7290
  class To_Ruby<void>
@@ -7270,6 +7343,7 @@ namespace Rice
7270
7343
  };
7271
7344
  }
7272
7345
  }
7346
+
7273
7347
  // ========= from_ruby.ipp =========
7274
7348
  #include <limits>
7275
7349
  #include <optional>
@@ -9050,7 +9124,7 @@ namespace Rice
9050
9124
  }
9051
9125
 
9052
9126
  template<typename T>
9053
- inline Reference<T>::Reference(T& data) : data_(data)
9127
+ inline Reference<T>::Reference(const T& data) : data_(data)
9054
9128
  {
9055
9129
  }
9056
9130
 
@@ -10288,10 +10362,10 @@ namespace Rice::detail
10288
10362
 
10289
10363
  if constexpr (is_complete_v<T>)
10290
10364
  {
10291
- // is_abstract_v requires a complete type, so nest inside is_complete_v.
10292
- // Deleting an abstract class through a non-virtual destructor is UB,
10365
+ // is_polymorphic_v requires a complete type, so nest inside is_complete_v.
10366
+ // Deleting a polymorphic class through a non-virtual destructor is UB,
10293
10367
  // but it is safe if the destructor is virtual.
10294
- if constexpr (std::is_destructible_v<T> && (!std::is_abstract_v<T> || std::has_virtual_destructor_v<T>))
10368
+ if constexpr (std::is_destructible_v<T> && (!std::is_polymorphic_v<T> || std::has_virtual_destructor_v<T>))
10295
10369
  {
10296
10370
  if (this->isOwner_)
10297
10371
  {
@@ -12663,6 +12737,25 @@ namespace Rice::detail
12663
12737
  }
12664
12738
  };
12665
12739
 
12740
+ // Wraps a C++ function as a Ruby proc
12741
+ template<typename Return_T, typename ...Parameter_Ts>
12742
+ class To_Ruby<Return_T(*&)(Parameter_Ts...)>
12743
+ {
12744
+ public:
12745
+ using Proc_T = Return_T(*&)(Parameter_Ts...);
12746
+
12747
+ To_Ruby() = default;
12748
+
12749
+ explicit To_Ruby(Arg*)
12750
+ {}
12751
+
12752
+ VALUE convert(Proc_T proc)
12753
+ {
12754
+ // Wrap the C+++ function pointer as a Ruby Proc
12755
+ return NativeProc<Proc_T>::createRubyProc(std::forward<Proc_T>(proc));
12756
+ }
12757
+ };
12758
+
12666
12759
  // Makes a Ruby proc callable as C callback
12667
12760
  template<typename Return_T, typename ...Parameter_Ts>
12668
12761
  class From_Ruby<Return_T(*)(Parameter_Ts...)>
@@ -12715,53 +12808,6 @@ namespace Rice
12715
12808
  }
12716
12809
  }
12717
12810
 
12718
- /*namespace Rice::detail
12719
- {
12720
- template<>
12721
- struct Type<Encoding>
12722
- {
12723
- static bool verify()
12724
- {
12725
- return true;
12726
- }
12727
- };
12728
-
12729
- template<>
12730
- class To_Ruby<Encoding>
12731
- {
12732
- public:
12733
- VALUE convert(const Encoding& encoding)
12734
- {
12735
- // return x.value();
12736
- }
12737
- };
12738
-
12739
- template<>
12740
- class From_Ruby<Encoding>
12741
- {
12742
- public:
12743
- Convertible is_convertible(VALUE value)
12744
- {
12745
- switch (rb_type(value))
12746
- {
12747
- case RUBY_T_SYMBOL:
12748
- return Convertible::Exact;
12749
- break;
12750
- case RUBY_T_STRING:
12751
- return Convertible::Cast;
12752
- break;
12753
- default:
12754
- return Convertible::None;
12755
- }
12756
- }
12757
-
12758
- Encoding convert(VALUE value)
12759
- {
12760
- // return Symbol(value);
12761
- }
12762
- };
12763
- }
12764
- */
12765
12811
  // ========= Object.ipp =========
12766
12812
  namespace Rice
12767
12813
  {
@@ -12807,7 +12853,7 @@ namespace Rice
12807
12853
  }
12808
12854
 
12809
12855
  template<typename ...Parameter_Ts>
12810
- inline Object Object::call(Identifier id, Parameter_Ts... args) const
12856
+ inline Object Object::call(Identifier id, Parameter_Ts&&... args) const
12811
12857
  {
12812
12858
  /* IMPORTANT - We store VALUEs in an array that is a local variable.
12813
12859
  That allows the Ruby garbage collector to find them when scanning
@@ -12821,10 +12867,10 @@ namespace Rice
12821
12867
  }
12822
12868
 
12823
12869
  template<typename ...Parameter_Ts>
12824
- inline Object Object::call_kw(Identifier id, Parameter_Ts... args) const
12870
+ inline Object Object::call_kw(Identifier id, Parameter_Ts&&... args) const
12825
12871
  {
12826
12872
  /* IMPORTANT - See call() above */
12827
- std::array<VALUE, sizeof...(Parameter_Ts)> values = { detail::To_Ruby<detail::remove_cv_recursive_t<Parameter_Ts>>().convert(args)... };
12873
+ std::array<VALUE, sizeof...(Parameter_Ts)> values = { detail::To_Ruby<detail::remove_cv_recursive_t<Parameter_Ts>>().convert(std::forward<Parameter_Ts>(args))... };
12828
12874
  return detail::protect(rb_funcallv_kw, this->validated_value(), id.id(), (int)values.size(), (const VALUE*)values.data(), RB_PASS_KEYWORDS);
12829
12875
  }
12830
12876
 
@@ -13172,6 +13218,25 @@ namespace Rice::detail
13172
13218
  return x.value();
13173
13219
  }
13174
13220
 
13221
+ private:
13222
+ Arg* arg_ = nullptr;
13223
+ };
13224
+
13225
+ template<>
13226
+ class To_Ruby<String&>
13227
+ {
13228
+ public:
13229
+ To_Ruby() = default;
13230
+
13231
+ explicit To_Ruby(Arg* arg) : arg_(arg)
13232
+ {
13233
+ }
13234
+
13235
+ VALUE convert(String const& x)
13236
+ {
13237
+ return x.value();
13238
+ }
13239
+
13175
13240
  private:
13176
13241
  Arg* arg_ = nullptr;
13177
13242
  };
@@ -13905,6 +13970,25 @@ namespace Rice::detail
13905
13970
  Arg* arg_ = nullptr;
13906
13971
  };
13907
13972
 
13973
+ template<>
13974
+ class To_Ruby<Hash&>
13975
+ {
13976
+ public:
13977
+ To_Ruby() = default;
13978
+
13979
+ explicit To_Ruby(Arg* arg) : arg_(arg)
13980
+ {
13981
+ }
13982
+
13983
+ VALUE convert(Hash const& x)
13984
+ {
13985
+ return x.value();
13986
+ }
13987
+
13988
+ private:
13989
+ Arg* arg_ = nullptr;
13990
+ };
13991
+
13908
13992
  template<>
13909
13993
  class From_Ruby<Hash>
13910
13994
  {
@@ -15119,7 +15203,7 @@ namespace Rice
15119
15203
  static void initialize(VALUE self, Parameter_Ts...args)
15120
15204
  {
15121
15205
  // Call C++ constructor
15122
- T* data = new T(args...);
15206
+ T* data = new T(std::forward<Parameter_Ts>(args)...);
15123
15207
  detail::wrapConstructed<T>(self, Data_Type<T>::ruby_data_type(), data);
15124
15208
  }
15125
15209
 
@@ -15153,11 +15237,12 @@ namespace Rice
15153
15237
  static void initialize(Object self, Parameter_Ts...args)
15154
15238
  {
15155
15239
  // Call C++ constructor
15156
- T* data = new T(self, args...);
15240
+ T* data = new T(self, std::forward<Parameter_Ts>(args)...);
15157
15241
  detail::wrapConstructed<T>(self.value(), Data_Type<T>::ruby_data_type(), data);
15158
15242
  }
15159
15243
  };
15160
15244
  }
15245
+
15161
15246
  // ========= Callback.hpp =========
15162
15247
 
15163
15248
  namespace Rice
@@ -15520,16 +15605,6 @@ namespace Rice::detail
15520
15605
  Arg* arg_ = nullptr;
15521
15606
  };
15522
15607
 
15523
- template<typename T>
15524
- class To_Ruby<Data_Object<T>>
15525
- {
15526
- public:
15527
- VALUE convert(const Object& x)
15528
- {
15529
- return x.value();
15530
- }
15531
- };
15532
-
15533
15608
  template <typename T>
15534
15609
  class From_Ruby
15535
15610
  {
@@ -15875,38 +15950,6 @@ namespace Rice::detail
15875
15950
  Arg* arg_ = nullptr;
15876
15951
  std::vector<Intrinsic_T*> vector_;
15877
15952
  };
15878
-
15879
- template<typename T>
15880
- class From_Ruby<Data_Object<T>>
15881
- {
15882
- static_assert(!std::is_fundamental_v<intrinsic_type<T>>,
15883
- "Data_Object cannot be used with fundamental types");
15884
-
15885
- static_assert(!std::is_same_v<T, std::map<T, T>> && !std::is_same_v<T, std::unordered_map<T, T>> &&
15886
- !std::is_same_v<T, std::monostate> && !std::is_same_v<T, std::multimap<T, T>> &&
15887
- !std::is_same_v<T, std::optional<T>> && !std::is_same_v<T, std::pair<T, T>> &&
15888
- !std::is_same_v<T, std::set<T>> && !std::is_same_v<T, std::string> &&
15889
- !std::is_same_v<T, std::vector<T>>,
15890
- "Please include rice/stl.hpp header for STL support");
15891
-
15892
- public:
15893
- double is_convertible(VALUE value)
15894
- {
15895
- switch (rb_type(value))
15896
- {
15897
- case RUBY_T_DATA:
15898
- return Data_Type<T>::is_descendant(value) ? Convertible::Exact : Convertible::None;
15899
- break;
15900
- default:
15901
- return Convertible::None;
15902
- }
15903
- }
15904
-
15905
- static Data_Object<T> convert(VALUE value)
15906
- {
15907
- return Data_Object<T>(value);
15908
- }
15909
- };
15910
15953
  }
15911
15954
 
15912
15955
  // ========= Enum.hpp =========