rice 4.8.0 → 4.9.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1cd8fd2c5aefe04f71129457cc7452fff163a593cd1395070760bd9914541414
4
- data.tar.gz: 3ca53fb0b47080e009e54b69678f30dfa039f8d68b44ec52a4ff6a95992234d1
3
+ metadata.gz: 60ee7533ef1a2e8fae4a992ef2d1dc2f3dd1cd4ab260217058e9de77ead8f80b
4
+ data.tar.gz: 83393a7a4238d817da51eccc5392d318bb2dab00e57d9ce688867a748a50f7a5
5
5
  SHA512:
6
- metadata.gz: 30452135eabeac3550aa1d7cdfbf834434ad9548c2aaa556d960a57eee7a99518537cd77341a40b4ec61992457756dc710e60cee0753b6fd37ffc67bad0de5c1
7
- data.tar.gz: d0ebe6f3dde0820d27db562bde3960669581d6470552aa55d9ab8e20fb633600a1af0e154d14f27004ac8334d4fa3e658b0dcde5826b828572cd57ce4661a2fc
6
+ metadata.gz: 26c796a5fcac6d816f9cddda0944be07a16cc93a0264221a351f4612061ed3b0e61dd4ab99eae0470ebc62e483f762820116024f6814f9f0023adccc466d66cf
7
+ data.tar.gz: aacb86cb9d086100650839ac2cf2cf49d60dcf4604f3539be24e0922c3f3ca3a8f06dfb0511e1e1b3281e9f475cf621218ac165b4c155e5d6e4e2083b3444823
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.9.0 (2026-01-01)
4
+ This release revamps smart pointer support for `std::shared_ptr` and `std::unique_ptr`.
5
+
6
+ Rice now always creates wrapper classes for smart pointers under the `Std` module (e.g., `Std::SharedPtr≺MyClass≻`, `Std::UniquePtr≺MyClass≻`). These wrapper classes expose methods like `empty?`, `get`, `swap`, and for shared_ptr, `use_count`. Methods defined on the managed type are automatically forwarded to the wrapper class using Ruby's `Forwardable` module.
7
+
8
+ This change is backwards compatible for Ruby code but not C++ code. If you have implemented your own Smart Pointer wrapper then please read the Smart Pointer documentation for more information on how to update it.
9
+
3
10
  ## 4.8.0 (2025-12-29)
4
11
  This release focuses on making Rice easier to use:
5
12
 
@@ -25,6 +32,7 @@ However, these changes did require some breaking changes, which include:
25
32
  * `Arg("").isBuffer()` is replaced by `ArgBuffer("")`
26
33
  * `Function().noGVL()` is replaced by `NoGvL()`
27
34
  * `is_convertible` methods must now return a `double` instead of a `Convertible` enum
35
+ * All function/method parameter default values are verified. You may see errors like "ArgumentError: Type is not registered with Rice" or "Invalid AnyCast". In either case, make sure to check that specified default values are correct.
28
36
 
29
37
  ## 4.7.1 (2025-10-28)
30
38
  Updates:
data/bin/rice-doc.rb CHANGED
@@ -114,6 +114,8 @@ unless Dir.exist?(config.output)
114
114
  FileUtils.mkdir_p(config.output)
115
115
  end
116
116
 
117
+ # Add the extension directory the path in case it ships with extra libraries
118
+ ENV["PATH"] = "#{File.dirname(config.extension)}#{File::PATH_SEPARATOR}#{ENV["PATH"]}"
117
119
  # Load the extension
118
120
  require config.extension
119
121
 
data/include/rice/api.hpp CHANGED
@@ -79,7 +79,20 @@ inline void Init_Native_Registry()
79
79
  }
80
80
 
81
81
  return result;
82
- }, Arg("klass").setValue());
82
+ }, Arg("klass").setValue()).
83
+
84
+ define_method("lookup_by_kind", [](detail::NativeRegistry& self, VALUE klass, detail::NativeKind kind) -> Array
85
+ {
86
+ Array result;
87
+
88
+ const std::vector<detail::Native*> natives = self.lookup(klass, kind);
89
+ for (detail::Native* native : natives)
90
+ {
91
+ result.push(native, false);
92
+ }
93
+
94
+ return result;
95
+ }, Arg("klass").setValue(), Arg("kind"));
83
96
  }
84
97
 
85
98
  // ========= TypeRegistry.hpp =========
@@ -600,9 +600,9 @@ namespace Rice::detail
600
600
  class WrapperBase
601
601
  {
602
602
  public:
603
- WrapperBase() = default;
603
+ WrapperBase(rb_data_type_t* rb_data_type);
604
604
  virtual ~WrapperBase() = default;
605
- virtual void* get() = 0;
605
+ virtual void* get(rb_data_type_t* requestedType) = 0;
606
606
  bool isConst();
607
607
 
608
608
  void ruby_mark();
@@ -610,6 +610,7 @@ namespace Rice::detail
610
610
  void setOwner(bool value);
611
611
 
612
612
  protected:
613
+ rb_data_type_t* rb_data_type_;
613
614
  bool isOwner_ = false;
614
615
  bool isConst_ = false;
615
616
 
@@ -624,10 +625,10 @@ namespace Rice::detail
624
625
  class Wrapper : public WrapperBase
625
626
  {
626
627
  public:
627
- Wrapper(T& data);
628
- Wrapper(T&& data);
628
+ Wrapper(rb_data_type_t* rb_data_type, T& data);
629
+ Wrapper(rb_data_type_t* rb_data_type, T&& data);
629
630
  ~Wrapper();
630
- void* get() override;
631
+ void* get(rb_data_type_t* requestedType) override;
631
632
 
632
633
  private:
633
634
  T data_;
@@ -637,9 +638,9 @@ namespace Rice::detail
637
638
  class Wrapper<T&> : public WrapperBase
638
639
  {
639
640
  public:
640
- Wrapper(T& data);
641
+ Wrapper(rb_data_type_t* rb_data_type, T& data);
641
642
  ~Wrapper();
642
- void* get() override;
643
+ void* get(rb_data_type_t* requestedType) override;
643
644
 
644
645
  private:
645
646
  T& data_;
@@ -649,9 +650,9 @@ namespace Rice::detail
649
650
  class Wrapper<T*> : public WrapperBase
650
651
  {
651
652
  public:
652
- Wrapper(T* data, bool isOwner);
653
+ Wrapper(rb_data_type_t* rb_data_type, T* data, bool isOwner);
653
654
  ~Wrapper();
654
- void* get() override;
655
+ void* get(rb_data_type_t* requestedType) override;
655
656
 
656
657
  private:
657
658
  T* data_ = nullptr;
@@ -661,9 +662,9 @@ namespace Rice::detail
661
662
  class Wrapper<T**> : public WrapperBase
662
663
  {
663
664
  public:
664
- Wrapper(T** data, bool isOwner);
665
+ Wrapper(rb_data_type_t* rb_data_type, T** data, bool isOwner);
665
666
  ~Wrapper();
666
- void* get() override;
667
+ void* get(rb_data_type_t* requestedType) override;
667
668
 
668
669
  private:
669
670
  T** data_ = nullptr;
@@ -1609,6 +1610,7 @@ namespace Rice
1609
1610
  namespace Rice
1610
1611
  {
1611
1612
  class Class;
1613
+ class Module;
1612
1614
  class String;
1613
1615
  class Array;
1614
1616
 
@@ -1716,6 +1718,11 @@ namespace Rice
1716
1718
  */
1717
1719
  bool is_a(Object klass) const;
1718
1720
 
1721
+ //! Extend the object with a module.
1722
+ /*! \param mod the module to extend with.
1723
+ */
1724
+ void extend(Module const& mod);
1725
+
1719
1726
  //! Determine if the objects responds to a method.
1720
1727
  /*! \param id the name of the method
1721
1728
  * \return true if the objects responds to the method, false
@@ -2697,6 +2704,11 @@ namespace Rice
2697
2704
  */
2698
2705
  const std::string base_name() const;
2699
2706
 
2707
+ //! Return the superclass of this class
2708
+ /*! \return Class.
2709
+ */
2710
+ Class superclass() const;
2711
+
2700
2712
  // Include these methods to call methods from Module but return
2701
2713
  // an instance of the current classes. This is an alternative to
2702
2714
  // using CRTP.
@@ -3796,6 +3808,7 @@ namespace Rice::detail
3796
3808
 
3797
3809
  const std::vector<Native*> lookup(VALUE klass);
3798
3810
  const std::vector<std::unique_ptr<Native>>& lookup(VALUE klass, ID methodId);
3811
+ std::vector<Native*> lookup(VALUE klass, NativeKind kind);
3799
3812
 
3800
3813
  private:
3801
3814
  // Key - Ruby klass/method
@@ -8985,6 +8998,35 @@ namespace Rice::detail
8985
8998
  // Lookup items for method
8986
8999
  return this->natives_[key];
8987
9000
  }
9001
+
9002
+ inline std::vector<Native*> NativeRegistry::lookup(VALUE klass, NativeKind kind)
9003
+ {
9004
+ std::vector<Native*> result;
9005
+
9006
+ if (rb_type(klass) == T_ICLASS)
9007
+ {
9008
+ klass = detail::protect(rb_class_of, klass);
9009
+ }
9010
+
9011
+ for (auto& pair : this->natives_)
9012
+ {
9013
+ const std::pair<VALUE, ID>& key = pair.first;
9014
+
9015
+ if (klass == key.first)
9016
+ {
9017
+ const std::vector<std::unique_ptr<Native>>& natives = pair.second;
9018
+ for (auto& native : natives)
9019
+ {
9020
+ if (native->kind() == kind)
9021
+ {
9022
+ result.push_back(native.get());
9023
+ }
9024
+ }
9025
+ }
9026
+ }
9027
+
9028
+ return result;
9029
+ }
8988
9030
  }
8989
9031
 
8990
9032
  // ========= Registries.ipp =========
@@ -9207,6 +9249,10 @@ namespace Rice::detail
9207
9249
  std::regex equalRegex(R"(,\s*std::equal_to)");
9208
9250
  removeGroup(base, equalRegex);
9209
9251
 
9252
+ // Remove default_delete (std::unique_ptr)
9253
+ std::regex defaultDeleteRegex(R"(,\s*std::default_delete)");
9254
+ removeGroup(base, defaultDeleteRegex);
9255
+
9210
9256
  // Remove spaces before pointers
9211
9257
  std::regex ptrRegex = std::regex(R"(\s+\*)");
9212
9258
  base = std::regex_replace(base, ptrRegex, "*");
@@ -9531,6 +9577,10 @@ namespace Rice::detail
9531
9577
 
9532
9578
  namespace Rice::detail
9533
9579
  {
9580
+ inline WrapperBase::WrapperBase(rb_data_type_t* rb_data_type) : rb_data_type_(rb_data_type)
9581
+ {
9582
+ }
9583
+
9534
9584
  inline bool WrapperBase::isConst()
9535
9585
  {
9536
9586
  return this->isConst_;
@@ -9556,31 +9606,40 @@ namespace Rice::detail
9556
9606
 
9557
9607
  // ---- Wrapper -----
9558
9608
  template <typename T>
9559
- inline Wrapper<T>::Wrapper(T& data): data_(data)
9609
+ inline Wrapper<T>::Wrapper(rb_data_type_t* rb_data_type, T& data) : WrapperBase(rb_data_type), data_(data)
9560
9610
  {
9561
9611
  this->isConst_ = std::is_const_v<std::remove_reference_t<T>>;
9562
9612
  }
9563
9613
 
9564
9614
  template <typename T>
9565
- inline Wrapper<T>::Wrapper(T&& data) : data_(std::move(data))
9615
+ inline Wrapper<T>::Wrapper(rb_data_type_t* rb_data_type, T&& data) : WrapperBase(rb_data_type), data_(std::move(data))
9566
9616
  {
9567
9617
  }
9568
9618
 
9569
9619
  template <typename T>
9570
9620
  inline Wrapper<T>::~Wrapper()
9571
9621
  {
9572
- Registries::instance.instances.remove(this->get());
9622
+ Registries::instance.instances.remove(this->get(this->rb_data_type_));
9573
9623
  }
9574
9624
 
9575
9625
  template <typename T>
9576
- inline void* Wrapper<T>::Wrapper::get()
9626
+ inline void* Wrapper<T>::get(rb_data_type_t* requestedType)
9577
9627
  {
9578
- return (void*)&this->data_;
9628
+ if (rb_typeddata_inherited_p(this->rb_data_type_, requestedType))
9629
+ {
9630
+ return (void*)&this->data_;
9631
+ }
9632
+ else
9633
+ {
9634
+ throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
9635
+ this->rb_data_type_->wrap_struct_name,
9636
+ requestedType->wrap_struct_name);
9637
+ }
9579
9638
  }
9580
9639
 
9581
9640
  // ---- Wrapper& -----
9582
9641
  template <typename T>
9583
- inline Wrapper<T&>::Wrapper(T& data): data_(data)
9642
+ inline Wrapper<T&>::Wrapper(rb_data_type_t* rb_data_type, T& data) : WrapperBase(rb_data_type), data_(data)
9584
9643
  {
9585
9644
  this->isConst_ = std::is_const_v<std::remove_reference_t<T>>;
9586
9645
  }
@@ -9588,18 +9647,27 @@ namespace Rice::detail
9588
9647
  template <typename T>
9589
9648
  inline Wrapper<T&>::~Wrapper()
9590
9649
  {
9591
- Registries::instance.instances.remove(this->get());
9650
+ Registries::instance.instances.remove(this->get(this->rb_data_type_));
9592
9651
  }
9593
9652
 
9594
9653
  template <typename T>
9595
- inline void* Wrapper<T&>::get()
9654
+ inline void* Wrapper<T&>::get(rb_data_type_t* requestedType)
9596
9655
  {
9597
- return (void*)&this->data_;
9656
+ if (rb_typeddata_inherited_p(this->rb_data_type_, requestedType))
9657
+ {
9658
+ return (void*)&this->data_;
9659
+ }
9660
+ else
9661
+ {
9662
+ throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
9663
+ this->rb_data_type_->wrap_struct_name,
9664
+ requestedType->wrap_struct_name);
9665
+ }
9598
9666
  }
9599
9667
 
9600
9668
  // ---- Wrapper* -----
9601
9669
  template <typename T>
9602
- inline Wrapper<T*>::Wrapper(T* data, bool isOwner) : data_(data)
9670
+ inline Wrapper<T*>::Wrapper(rb_data_type_t* rb_data_type, T* data, bool isOwner) : WrapperBase(rb_data_type), data_(data)
9603
9671
  {
9604
9672
  this->isOwner_ = isOwner;
9605
9673
  this->isConst_ = std::is_const_v<std::remove_pointer_t<T>>;
@@ -9608,7 +9676,8 @@ namespace Rice::detail
9608
9676
  template <typename T>
9609
9677
  inline Wrapper<T*>::~Wrapper()
9610
9678
  {
9611
- Registries::instance.instances.remove(this->get());
9679
+ Registries::instance.instances.remove(this->get(this->rb_data_type_));
9680
+
9612
9681
  if constexpr (std::is_destructible_v<T>)
9613
9682
  {
9614
9683
  if (this->isOwner_)
@@ -9619,14 +9688,23 @@ namespace Rice::detail
9619
9688
  }
9620
9689
 
9621
9690
  template <typename T>
9622
- inline void* Wrapper<T*>::get()
9691
+ inline void* Wrapper<T*>::get(rb_data_type_t* requestedType)
9623
9692
  {
9624
- return (void*)this->data_;
9693
+ if (rb_typeddata_inherited_p(this->rb_data_type_, requestedType))
9694
+ {
9695
+ return (void*)this->data_;
9696
+ }
9697
+ else
9698
+ {
9699
+ throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
9700
+ this->rb_data_type_->wrap_struct_name,
9701
+ requestedType->wrap_struct_name);
9702
+ }
9625
9703
  }
9626
9704
 
9627
9705
  // ---- Wrapper** -----
9628
9706
  template <typename T>
9629
- inline Wrapper<T**>::Wrapper(T** data, bool isOwner) : data_(data)
9707
+ inline Wrapper<T**>::Wrapper(rb_data_type_t* rb_data_type, T** data, bool isOwner) : WrapperBase(rb_data_type), data_(data)
9630
9708
  {
9631
9709
  this->isOwner_ = isOwner;
9632
9710
  this->isConst_ = std::is_const_v<std::remove_pointer_t<std::remove_pointer_t<T>>>;
@@ -9635,7 +9713,8 @@ namespace Rice::detail
9635
9713
  template <typename T>
9636
9714
  inline Wrapper<T**>::~Wrapper()
9637
9715
  {
9638
- Registries::instance.instances.remove(this->get());
9716
+ Registries::instance.instances.remove(this->get(this->rb_data_type_));
9717
+
9639
9718
  if constexpr (std::is_destructible_v<T>)
9640
9719
  {
9641
9720
  if (this->isOwner_)
@@ -9646,9 +9725,18 @@ namespace Rice::detail
9646
9725
  }
9647
9726
 
9648
9727
  template <typename T>
9649
- inline void* Wrapper<T**>::get()
9728
+ inline void* Wrapper<T**>::get(rb_data_type_t* requestedType)
9650
9729
  {
9651
- return (void*)this->data_;
9730
+ if (rb_typeddata_inherited_p(this->rb_data_type_, requestedType))
9731
+ {
9732
+ return (void*)this->data_;
9733
+ }
9734
+ else
9735
+ {
9736
+ throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
9737
+ this->rb_data_type_->wrap_struct_name,
9738
+ requestedType->wrap_struct_name);
9739
+ }
9652
9740
  }
9653
9741
 
9654
9742
  // ---- Helper Functions -------
@@ -9665,7 +9753,7 @@ namespace Rice::detail
9665
9753
  // If Ruby is not the owner then wrap the reference
9666
9754
  if (!isOwner)
9667
9755
  {
9668
- wrapper = new Wrapper<T&>(data);
9756
+ wrapper = new Wrapper<T&>(rb_data_type, data);
9669
9757
  result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
9670
9758
  }
9671
9759
 
@@ -9674,12 +9762,12 @@ namespace Rice::detail
9674
9762
  {
9675
9763
  if constexpr (std::is_copy_constructible_v<typename T::value_type>)
9676
9764
  {
9677
- wrapper = new Wrapper<T>(data);
9765
+ wrapper = new Wrapper<T>(rb_data_type, data);
9678
9766
  result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
9679
9767
  }
9680
9768
  else
9681
9769
  {
9682
- wrapper = new Wrapper<T>(std::move(data));
9770
+ wrapper = new Wrapper<T>(rb_data_type, std::move(data));
9683
9771
  result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
9684
9772
  }
9685
9773
  }
@@ -9687,14 +9775,14 @@ namespace Rice::detail
9687
9775
  // Ruby is the owner so copy data
9688
9776
  else if constexpr (std::is_copy_constructible_v<T>)
9689
9777
  {
9690
- wrapper = new Wrapper<T>(data);
9778
+ wrapper = new Wrapper<T>(rb_data_type, data);
9691
9779
  result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
9692
9780
  }
9693
9781
 
9694
9782
  // Ruby is the owner so move data
9695
9783
  else if constexpr (std::is_move_constructible_v<T>)
9696
9784
  {
9697
- wrapper = new Wrapper<T>(std::move(data));
9785
+ wrapper = new Wrapper<T>(rb_data_type, std::move(data));
9698
9786
  result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
9699
9787
  }
9700
9788
 
@@ -9706,7 +9794,7 @@ namespace Rice::detail
9706
9794
  throw std::runtime_error(message);
9707
9795
  }
9708
9796
 
9709
- Registries::instance.instances.add(wrapper->get(), result);
9797
+ Registries::instance.instances.add(wrapper->get(rb_data_type), result);
9710
9798
 
9711
9799
  return result;
9712
9800
  };
@@ -9719,38 +9807,40 @@ namespace Rice::detail
9719
9807
  if (result != Qnil)
9720
9808
  return result;
9721
9809
 
9722
- WrapperBase* wrapper = new Wrapper<T*>(data, isOwner);
9810
+ WrapperBase* wrapper = new Wrapper<T*>(rb_data_type, data, isOwner);
9723
9811
  result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
9724
9812
 
9725
- Registries::instance.instances.add(wrapper->get(), result);
9813
+ Registries::instance.instances.add(wrapper->get(rb_data_type), result);
9726
9814
  return result;
9727
9815
  };
9728
9816
 
9729
9817
  template <typename T>
9730
9818
  inline T* unwrap(VALUE value, rb_data_type_t* rb_data_type, bool takeOwnership)
9731
9819
  {
9732
- if (rb_type(value) != RUBY_T_DATA)
9820
+ if (!RTYPEDDATA_P(value))
9733
9821
  {
9734
9822
  std::string message = "The Ruby object does not wrap a C++ object. It is actually a " +
9735
9823
  std::string(detail::protect(rb_obj_classname, value)) + ".";
9736
9824
  throw std::runtime_error(message);
9737
9825
  }
9738
9826
 
9739
- WrapperBase* wrapper = getWrapper(value, rb_data_type);
9827
+ WrapperBase* wrapper = static_cast<WrapperBase*>(RTYPEDDATA_DATA(value));
9740
9828
 
9741
9829
  if (wrapper == nullptr)
9742
9830
  {
9743
- std::string message = "Wrapped C++ object is nil. Did you override " +
9744
- std::string(detail::protect(rb_obj_classname, value)) +
9831
+ std::string message = "Wrapped C++ object is nil. Did you override " +
9832
+ std::string(detail::protect(rb_obj_classname, value)) +
9745
9833
  "#initialize and forget to call super?";
9746
9834
 
9747
9835
  throw std::runtime_error(message);
9748
9836
  }
9749
9837
 
9750
9838
  if (takeOwnership)
9839
+ {
9751
9840
  wrapper->setOwner(false);
9841
+ }
9752
9842
 
9753
- return static_cast<T*>(wrapper->get());
9843
+ return static_cast<T*>(wrapper->get(rb_data_type));
9754
9844
  }
9755
9845
 
9756
9846
  template <typename Wrapper_T>
@@ -9772,7 +9862,8 @@ namespace Rice::detail
9772
9862
  if (!RTYPEDDATA_P(value))
9773
9863
  {
9774
9864
  throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
9775
- detail::protect(rb_obj_classname, value), "wrapped C++ object");
9865
+ detail::protect(rb_obj_classname, value),
9866
+ "wrapped C++ object");
9776
9867
  }
9777
9868
 
9778
9869
  return static_cast<WrapperBase*>(RTYPEDDATA_DATA(value));
@@ -9786,21 +9877,21 @@ namespace Rice::detail
9786
9877
  inline void wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data)
9787
9878
  {
9788
9879
  using Wrapper_T = Wrapper<T*>;
9789
-
9880
+
9790
9881
  Wrapper_T* wrapper = nullptr;
9791
9882
  TypedData_Get_Struct(value, Wrapper_T, rb_data_type, wrapper);
9792
9883
  if (wrapper)
9793
9884
  {
9794
- Registries::instance.instances.remove(wrapper->get());
9885
+ Registries::instance.instances.remove(wrapper->get(rb_data_type));
9795
9886
  delete wrapper;
9796
9887
  }
9797
9888
 
9798
- wrapper = new Wrapper_T(data, true);
9889
+ wrapper = new Wrapper_T(rb_data_type, data, true);
9799
9890
  RTYPEDDATA_DATA(value) = wrapper;
9800
9891
 
9801
9892
  Registries::instance.instances.add(data, value);
9802
9893
  }
9803
- } // namespace
9894
+ }
9804
9895
 
9805
9896
  // ========= Native.ipp =========
9806
9897
  namespace Rice::detail
@@ -12119,6 +12210,11 @@ namespace Rice
12119
12210
  return RB_TEST(result);
12120
12211
  }
12121
12212
 
12213
+ inline void Object::extend(Module const& mod)
12214
+ {
12215
+ detail::protect(rb_extend_object, this->value(), mod.value());
12216
+ }
12217
+
12122
12218
  inline bool Object::respond_to(Identifier id) const
12123
12219
  {
12124
12220
  return bool(rb_respond_to(this->value(), id.id()));
@@ -13513,6 +13609,11 @@ namespace Rice
13513
13609
  return result;
13514
13610
  }
13515
13611
 
13612
+ inline Class Class::superclass() const
13613
+ {
13614
+ return detail::protect(rb_class_superclass, this->value());
13615
+ }
13616
+
13516
13617
  inline Class define_class_under(Object parent, Identifier id, const Class& superclass)
13517
13618
  {
13518
13619
  VALUE klass = detail::protect(rb_define_class_id_under, parent.value(), id, superclass.value());
@@ -14067,17 +14168,22 @@ namespace Rice
14067
14168
  template<typename T>
14068
14169
  inline void ruby_mark_internal(detail::WrapperBase* wrapper)
14069
14170
  {
14070
- // Tell the wrapper to mark the objects its keeping alive
14071
- wrapper->ruby_mark();
14171
+ detail::cpp_protect([&]
14172
+ {
14173
+ // Tell the wrapper to mark the objects its keeping alive
14174
+ wrapper->ruby_mark();
14072
14175
 
14073
- // Get the underlying data and call custom mark function (if any)
14074
- T* data = static_cast<T*>(wrapper->get());
14075
- ruby_mark<T>(data);
14176
+ // Get the underlying data and call custom mark function (if any)
14177
+ // Use the wrapper's stored rb_data_type to avoid type mismatch
14178
+ T* data = static_cast<T*>(wrapper->get(Data_Type<T>::ruby_data_type()));
14179
+ ruby_mark<T>(data);
14180
+ });
14076
14181
  }
14077
14182
 
14078
14183
  template<typename T>
14079
14184
  inline void ruby_free_internal(detail::WrapperBase* wrapper)
14080
14185
  {
14186
+ // Destructors are noexcept so we cannot use cpp_protect here
14081
14187
  delete wrapper;
14082
14188
  }
14083
14189
 
@@ -15662,6 +15768,86 @@ namespace Rice
15662
15768
  }
15663
15769
  }
15664
15770
 
15771
+ // Dependent on Module, Array, Symbol - used by stl smart pointers
15772
+
15773
+ // ========= Forwards.hpp =========
15774
+
15775
+ namespace Rice::detail
15776
+ {
15777
+ // Setup method forwarding from a wrapper class to its wrapped type using Ruby's Forwardable.
15778
+ // This allows calling methods on the wrapper that get delegated to the wrapped object via
15779
+ // a "get" method that returns the wrapped object.
15780
+ //
15781
+ // Parameters:
15782
+ // wrapper_klass - The Ruby class to add forwarding to (e.g., SharedPtr_MyClass)
15783
+ // wrapped_klass - The Ruby class whose methods should be forwarded (e.g., MyClass)
15784
+ void define_forwarding(VALUE wrapper_klass, VALUE wrapped_klass);
15785
+ }
15786
+
15787
+
15788
+ // --------- Forwards.ipp ---------
15789
+ namespace Rice::detail
15790
+ {
15791
+ inline void define_forwarding(VALUE wrapper_klass, VALUE wrapped_klass)
15792
+ {
15793
+ protect(rb_require, "forwardable");
15794
+ Object forwardable = Object(rb_cObject).const_get("Forwardable");
15795
+ Object(wrapper_klass).extend(forwardable.value());
15796
+
15797
+ // Get wrapper class's method names to avoid conflicts
15798
+ std::set<std::string> wrapperMethodSet;
15799
+ for (Native* native : Registries::instance.natives.lookup(wrapper_klass, NativeKind::Method))
15800
+ {
15801
+ wrapperMethodSet.insert(native->name());
15802
+ }
15803
+ for (Native* native : Registries::instance.natives.lookup(wrapper_klass, NativeKind::AttributeReader))
15804
+ {
15805
+ wrapperMethodSet.insert(native->name());
15806
+ }
15807
+ for (Native* native : Registries::instance.natives.lookup(wrapper_klass, NativeKind::AttributeWriter))
15808
+ {
15809
+ wrapperMethodSet.insert(native->name() + "=");
15810
+ }
15811
+
15812
+ // Get wrapped class's method names from the registry, including ancestor classes
15813
+ std::set<std::string> wrappedMethodSet;
15814
+ Class klass(wrapped_klass);
15815
+ while (klass.value() != rb_cObject && klass.value() != Qnil)
15816
+ {
15817
+ for (Native* native : Registries::instance.natives.lookup(klass.value(), NativeKind::Method))
15818
+ {
15819
+ wrappedMethodSet.insert(native->name());
15820
+ }
15821
+ for (Native* native : Registries::instance.natives.lookup(klass.value(), NativeKind::AttributeReader))
15822
+ {
15823
+ wrappedMethodSet.insert(native->name());
15824
+ }
15825
+ for (Native* native : Registries::instance.natives.lookup(klass.value(), NativeKind::AttributeWriter))
15826
+ {
15827
+ wrappedMethodSet.insert(native->name() + "=");
15828
+ }
15829
+
15830
+ klass = klass.superclass();
15831
+ }
15832
+
15833
+ // Build the arguments array for def_delegators: [:get, :method1, :method2, ...]
15834
+ // Skip methods that are already defined on the wrapper class
15835
+ Array args;
15836
+ args.push(Symbol("get"));
15837
+ for (const std::string& method : wrappedMethodSet)
15838
+ {
15839
+ if (wrapperMethodSet.find(method) == wrapperMethodSet.end())
15840
+ {
15841
+ args.push(Symbol(method));
15842
+ }
15843
+ }
15844
+
15845
+ // Call def_delegators(*args)
15846
+ Object(wrapper_klass).vcall("def_delegators", args);
15847
+ }
15848
+ }
15849
+
15850
+
15665
15851
  // For now include libc support - maybe should be separate header file someday
15666
15852
 
15667
15853
  // ========= file.hpp =========