rice 4.8.0 → 4.9.1

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -1
  3. data/CMakePresets.json +77 -50
  4. data/FindRuby.cmake +1 -1
  5. data/bin/rice-doc.rb +2 -0
  6. data/include/rice/api.hpp +14 -1
  7. data/include/rice/rice.hpp +351 -132
  8. data/include/rice/stl.hpp +319 -256
  9. data/lib/rice/doc/config.rb +57 -57
  10. data/lib/rice/doc/cpp_reference.rb +158 -158
  11. data/lib/rice/doc/doxygen.rb +289 -289
  12. data/lib/rice/doc/mkdocs.rb +332 -332
  13. data/lib/rice/doc/rice.rb +48 -47
  14. data/lib/rice/doc/ruby.rb +26 -26
  15. data/lib/rice/native.rb +15 -15
  16. data/lib/rice/native_registry.rb +12 -17
  17. data/lib/rice/parameter.rb +5 -5
  18. data/lib/rice/rbs.rb +72 -72
  19. data/lib/rice/version.rb +1 -1
  20. data/lib/rubygems/builder.rb +9 -9
  21. data/lib/rubygems_plugin.rb +8 -8
  22. data/rice/Data_Type.ipp +12 -7
  23. data/rice/cpp_api/Class.hpp +5 -0
  24. data/rice/cpp_api/Class.ipp +5 -0
  25. data/rice/cpp_api/Object.hpp +6 -0
  26. data/rice/cpp_api/Object.ipp +5 -0
  27. data/rice/detail/Forwards.hpp +18 -0
  28. data/rice/detail/Forwards.ipp +60 -0
  29. data/rice/detail/Native.ipp +2 -4
  30. data/rice/detail/NativeAttributeGet.ipp +1 -1
  31. data/rice/detail/NativeAttributeSet.hpp +5 -3
  32. data/rice/detail/NativeAttributeSet.ipp +41 -33
  33. data/rice/detail/NativeMethod.ipp +25 -22
  34. data/rice/detail/NativeRegistry.hpp +4 -2
  35. data/rice/detail/NativeRegistry.ipp +42 -9
  36. data/rice/detail/Parameter.ipp +3 -4
  37. data/rice/detail/Type.ipp +4 -0
  38. data/rice/detail/Wrapper.hpp +17 -12
  39. data/rice/detail/Wrapper.ipp +95 -36
  40. data/rice/rice.hpp +3 -0
  41. data/rice/rice_api/NativeRegistry.ipp +14 -1
  42. data/rice/stl/exception.ipp +1 -1
  43. data/rice/stl/filesystem.ipp +1 -1
  44. data/rice/stl/map.ipp +13 -11
  45. data/rice/stl/multimap.ipp +13 -11
  46. data/rice/stl/pair.ipp +14 -8
  47. data/rice/stl/set.ipp +16 -16
  48. data/rice/stl/shared_ptr.hpp +16 -0
  49. data/rice/stl/shared_ptr.ipp +74 -37
  50. data/rice/stl/type_index.ipp +1 -1
  51. data/rice/stl/unique_ptr.hpp +9 -3
  52. data/rice/stl/unique_ptr.ipp +80 -124
  53. data/rice/stl/unordered_map.ipp +14 -12
  54. data/rice/stl/vector.ipp +67 -31
  55. data/test/test_Attribute.cpp +72 -0
  56. data/test/test_Callback.cpp +3 -0
  57. data/test/test_Inheritance.cpp +14 -14
  58. data/test/test_Keep_Alive_No_Wrapper.cpp +6 -2
  59. data/test/test_Stl_Map.cpp +46 -0
  60. data/test/test_Stl_Multimap.cpp +46 -0
  61. data/test/test_Stl_Set.cpp +34 -0
  62. data/test/test_Stl_SharedPtr.cpp +160 -45
  63. data/test/test_Stl_UniquePtr.cpp +48 -3
  64. data/test/test_Stl_Unordered_Map.cpp +46 -0
  65. data/test/test_Stl_Variant.cpp +10 -14
  66. data/test/test_Stl_Vector.cpp +140 -13
  67. data/test/test_Tracking.cpp +3 -0
  68. metadata +3 -1
@@ -600,9 +600,13 @@ namespace Rice::detail
600
600
  class WrapperBase
601
601
  {
602
602
  public:
603
- WrapperBase() = default;
603
+ static void addKeepAlive(VALUE object, VALUE keepAlive);
604
+ static bool isConst(VALUE object);
605
+
606
+ public:
607
+ WrapperBase(rb_data_type_t* rb_data_type);
604
608
  virtual ~WrapperBase() = default;
605
- virtual void* get() = 0;
609
+ virtual void* get(rb_data_type_t* requestedType) = 0;
606
610
  bool isConst();
607
611
 
608
612
  void ruby_mark();
@@ -610,6 +614,7 @@ namespace Rice::detail
610
614
  void setOwner(bool value);
611
615
 
612
616
  protected:
617
+ rb_data_type_t* rb_data_type_;
613
618
  bool isOwner_ = false;
614
619
  bool isConst_ = false;
615
620
 
@@ -624,10 +629,10 @@ namespace Rice::detail
624
629
  class Wrapper : public WrapperBase
625
630
  {
626
631
  public:
627
- Wrapper(T& data);
628
- Wrapper(T&& data);
632
+ Wrapper(rb_data_type_t* rb_data_type, T& data);
633
+ Wrapper(rb_data_type_t* rb_data_type, T&& data);
629
634
  ~Wrapper();
630
- void* get() override;
635
+ void* get(rb_data_type_t* requestedType) override;
631
636
 
632
637
  private:
633
638
  T data_;
@@ -637,9 +642,9 @@ namespace Rice::detail
637
642
  class Wrapper<T&> : public WrapperBase
638
643
  {
639
644
  public:
640
- Wrapper(T& data);
645
+ Wrapper(rb_data_type_t* rb_data_type, T& data);
641
646
  ~Wrapper();
642
- void* get() override;
647
+ void* get(rb_data_type_t* requestedType) override;
643
648
 
644
649
  private:
645
650
  T& data_;
@@ -649,9 +654,9 @@ namespace Rice::detail
649
654
  class Wrapper<T*> : public WrapperBase
650
655
  {
651
656
  public:
652
- Wrapper(T* data, bool isOwner);
657
+ Wrapper(rb_data_type_t* rb_data_type, T* data, bool isOwner);
653
658
  ~Wrapper();
654
- void* get() override;
659
+ void* get(rb_data_type_t* requestedType) override;
655
660
 
656
661
  private:
657
662
  T* data_ = nullptr;
@@ -661,9 +666,9 @@ namespace Rice::detail
661
666
  class Wrapper<T**> : public WrapperBase
662
667
  {
663
668
  public:
664
- Wrapper(T** data, bool isOwner);
669
+ Wrapper(rb_data_type_t* rb_data_type, T** data, bool isOwner);
665
670
  ~Wrapper();
666
- void* get() override;
671
+ void* get(rb_data_type_t* requestedType) override;
667
672
 
668
673
  private:
669
674
  T** data_ = nullptr;
@@ -671,7 +676,7 @@ namespace Rice::detail
671
676
 
672
677
  // ---- Helper Functions ---------
673
678
  template <typename T>
674
- void wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data);
679
+ Wrapper<T*>* wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data);
675
680
 
676
681
  template <typename T>
677
682
  VALUE wrap(VALUE klass, rb_data_type_t* rb_data_type, T& data, bool isOwner);
@@ -1609,6 +1614,7 @@ namespace Rice
1609
1614
  namespace Rice
1610
1615
  {
1611
1616
  class Class;
1617
+ class Module;
1612
1618
  class String;
1613
1619
  class Array;
1614
1620
 
@@ -1716,6 +1722,11 @@ namespace Rice
1716
1722
  */
1717
1723
  bool is_a(Object klass) const;
1718
1724
 
1725
+ //! Extend the object with a module.
1726
+ /*! \param mod the module to extend with.
1727
+ */
1728
+ void extend(Module const& mod);
1729
+
1719
1730
  //! Determine if the objects responds to a method.
1720
1731
  /*! \param id the name of the method
1721
1732
  * \return true if the objects responds to the method, false
@@ -2697,6 +2708,11 @@ namespace Rice
2697
2708
  */
2698
2709
  const std::string base_name() const;
2699
2710
 
2711
+ //! Return the superclass of this class
2712
+ /*! \return Class.
2713
+ */
2714
+ Class superclass() const;
2715
+
2700
2716
  // Include these methods to call methods from Module but return
2701
2717
  // an instance of the current classes. This is an alternative to
2702
2718
  // using CRTP.
@@ -3008,8 +3024,9 @@ namespace Rice
3008
3024
  using Receiver_T = typename attribute_traits<Attribute_T>::class_type;
3009
3025
 
3010
3026
  public:
3011
- // Register attribute getter/setter with Ruby
3012
- static void define(VALUE klass, std::string name, Attribute_T attribute);
3027
+ // Register attribute setter with Ruby
3028
+ template<typename...Arg_Ts>
3029
+ static void define(VALUE klass, std::string name, Attribute_T attribute, Arg_Ts&...args);
3013
3030
 
3014
3031
  public:
3015
3032
  // Disallow creating/copying/moving
@@ -3027,11 +3044,12 @@ namespace Rice
3027
3044
  VALUE returnKlass() override;
3028
3045
 
3029
3046
  protected:
3030
- NativeAttributeSet(VALUE klass, std::string name, Attribute_T attr);
3047
+ NativeAttributeSet(VALUE klass, std::string name, Attribute_T attr, std::unique_ptr<Parameter<T_Unqualified>> parameter);
3031
3048
 
3032
3049
  private:
3033
3050
  VALUE klass_;
3034
3051
  Attribute_T attribute_;
3052
+ std::unique_ptr<Parameter<T_Unqualified>> parameter_;
3035
3053
  };
3036
3054
  } // detail
3037
3055
  } // Rice
@@ -3792,10 +3810,12 @@ namespace Rice::detail
3792
3810
  NativeRegistry& operator=(const NativeRegistry& other) = delete;
3793
3811
 
3794
3812
  void add(VALUE klass, ID methodId, std::unique_ptr<Native>& native);
3813
+ void replace(VALUE klass, ID methodId, std::unique_ptr<Native>& native);
3795
3814
  void reset(VALUE klass);
3796
3815
 
3797
- const std::vector<Native*> lookup(VALUE klass);
3798
- const std::vector<std::unique_ptr<Native>>& lookup(VALUE klass, ID methodId);
3816
+ std::vector<Native*> lookup(VALUE klass);
3817
+ std::vector<std::unique_ptr<Native>>& lookup(VALUE klass, ID methodId);
3818
+ std::vector<Native*> lookup(VALUE klass, NativeKind kind);
3799
3819
 
3800
3820
  private:
3801
3821
  // Key - Ruby klass/method
@@ -3963,17 +3983,16 @@ namespace Rice::detail
3963
3983
  // One caveat - procs are also RUBY_T_DATA so don't check if this is a function type
3964
3984
  if (result == Convertible::Exact && rb_type(value) == RUBY_T_DATA && !std::is_function_v<std::remove_pointer_t<T>>)
3965
3985
  {
3966
- // Check the constness of the Ruby wrapped value and the parameter
3967
- WrapperBase* wrapper = getWrapper(value);
3986
+ bool isConst = WrapperBase::isConst(value);
3968
3987
 
3969
3988
  // Do not send a const value to a non-const parameter
3970
- if (wrapper->isConst() && !is_const_any_v<T>)
3989
+ if (isConst && !is_const_any_v<T>)
3971
3990
  {
3972
3991
  result = Convertible::None;
3973
3992
  }
3974
3993
  // It is ok to send a non-const value to a const parameter but
3975
3994
  // prefer non-const to non-const by slightly decreasing the score
3976
- else if (!wrapper->isConst() && is_const_any_v<T>)
3995
+ else if (!isConst && is_const_any_v<T>)
3977
3996
  {
3978
3997
  result = Convertible::ConstMismatch;
3979
3998
  }
@@ -8916,17 +8935,21 @@ namespace Rice::detail
8916
8935
  {
8917
8936
  inline void NativeRegistry::add(VALUE klass, ID methodId, std::unique_ptr<Native>& native)
8918
8937
  {
8919
- if (rb_type(klass) == T_ICLASS)
8920
- {
8921
- klass = detail::protect(rb_class_of, klass);
8922
- }
8938
+ // Lookup items for method
8939
+ std::vector<std::unique_ptr<Native>>& natives = NativeRegistry::lookup(klass, methodId);
8923
8940
 
8924
- // Create the key
8925
- std::pair<VALUE, ID> key = std::make_pair(klass, methodId);
8941
+ // Add new native
8942
+ natives.push_back(std::move(native));
8943
+ }
8926
8944
 
8945
+ inline void NativeRegistry::replace(VALUE klass, ID methodId, std::unique_ptr<Native>& native)
8946
+ {
8927
8947
  // Lookup items for method
8928
- std::vector<std::unique_ptr<Native>>& natives = this->natives_[key];
8948
+ std::vector<std::unique_ptr<Native>>& natives = NativeRegistry::lookup(klass, methodId);
8929
8949
 
8950
+ // Clear existing natives
8951
+ natives.clear();
8952
+ // Add new native
8930
8953
  natives.push_back(std::move(native));
8931
8954
  }
8932
8955
 
@@ -8946,7 +8969,7 @@ namespace Rice::detail
8946
8969
  }
8947
8970
  }
8948
8971
 
8949
- inline const std::vector<Native*> NativeRegistry::lookup(VALUE klass)
8972
+ inline std::vector<Native*> NativeRegistry::lookup(VALUE klass)
8950
8973
  {
8951
8974
  std::vector<Native*> result;
8952
8975
 
@@ -8972,7 +8995,7 @@ namespace Rice::detail
8972
8995
  return result;
8973
8996
  }
8974
8997
 
8975
- inline const std::vector<std::unique_ptr<Native>>& NativeRegistry::lookup(VALUE klass, ID methodId)
8998
+ inline std::vector<std::unique_ptr<Native>>& NativeRegistry::lookup(VALUE klass, ID methodId)
8976
8999
  {
8977
9000
  if (rb_type(klass) == T_ICLASS)
8978
9001
  {
@@ -8985,6 +9008,35 @@ namespace Rice::detail
8985
9008
  // Lookup items for method
8986
9009
  return this->natives_[key];
8987
9010
  }
9011
+
9012
+ inline std::vector<Native*> NativeRegistry::lookup(VALUE klass, NativeKind kind)
9013
+ {
9014
+ std::vector<Native*> result;
9015
+
9016
+ if (rb_type(klass) == T_ICLASS)
9017
+ {
9018
+ klass = detail::protect(rb_class_of, klass);
9019
+ }
9020
+
9021
+ for (auto& pair : this->natives_)
9022
+ {
9023
+ const std::pair<VALUE, ID>& key = pair.first;
9024
+
9025
+ if (klass == key.first)
9026
+ {
9027
+ const std::vector<std::unique_ptr<Native>>& natives = pair.second;
9028
+ for (auto& native : natives)
9029
+ {
9030
+ if (native->kind() == kind)
9031
+ {
9032
+ result.push_back(native.get());
9033
+ }
9034
+ }
9035
+ }
9036
+ }
9037
+
9038
+ return result;
9039
+ }
8988
9040
  }
8989
9041
 
8990
9042
  // ========= Registries.ipp =========
@@ -9207,6 +9259,10 @@ namespace Rice::detail
9207
9259
  std::regex equalRegex(R"(,\s*std::equal_to)");
9208
9260
  removeGroup(base, equalRegex);
9209
9261
 
9262
+ // Remove default_delete (std::unique_ptr)
9263
+ std::regex defaultDeleteRegex(R"(,\s*std::default_delete)");
9264
+ removeGroup(base, defaultDeleteRegex);
9265
+
9210
9266
  // Remove spaces before pointers
9211
9267
  std::regex ptrRegex = std::regex(R"(\s+\*)");
9212
9268
  base = std::regex_replace(base, ptrRegex, "*");
@@ -9531,6 +9587,22 @@ namespace Rice::detail
9531
9587
 
9532
9588
  namespace Rice::detail
9533
9589
  {
9590
+ inline void WrapperBase::addKeepAlive(VALUE object, VALUE keepAlive)
9591
+ {
9592
+ WrapperBase* wrapper = getWrapper(object);
9593
+ wrapper->addKeepAlive(keepAlive);
9594
+ }
9595
+
9596
+ inline bool WrapperBase::isConst(VALUE object)
9597
+ {
9598
+ WrapperBase* wrapper = getWrapper(object);
9599
+ return wrapper->isConst();
9600
+ }
9601
+
9602
+ inline WrapperBase::WrapperBase(rb_data_type_t* rb_data_type) : rb_data_type_(rb_data_type)
9603
+ {
9604
+ }
9605
+
9534
9606
  inline bool WrapperBase::isConst()
9535
9607
  {
9536
9608
  return this->isConst_;
@@ -9556,31 +9628,40 @@ namespace Rice::detail
9556
9628
 
9557
9629
  // ---- Wrapper -----
9558
9630
  template <typename T>
9559
- inline Wrapper<T>::Wrapper(T& data): data_(data)
9631
+ inline Wrapper<T>::Wrapper(rb_data_type_t* rb_data_type, T& data) : WrapperBase(rb_data_type), data_(data)
9560
9632
  {
9561
9633
  this->isConst_ = std::is_const_v<std::remove_reference_t<T>>;
9562
9634
  }
9563
9635
 
9564
9636
  template <typename T>
9565
- inline Wrapper<T>::Wrapper(T&& data) : data_(std::move(data))
9637
+ inline Wrapper<T>::Wrapper(rb_data_type_t* rb_data_type, T&& data) : WrapperBase(rb_data_type), data_(std::move(data))
9566
9638
  {
9567
9639
  }
9568
9640
 
9569
9641
  template <typename T>
9570
9642
  inline Wrapper<T>::~Wrapper()
9571
9643
  {
9572
- Registries::instance.instances.remove(this->get());
9644
+ Registries::instance.instances.remove(this->get(this->rb_data_type_));
9573
9645
  }
9574
9646
 
9575
9647
  template <typename T>
9576
- inline void* Wrapper<T>::Wrapper::get()
9648
+ inline void* Wrapper<T>::get(rb_data_type_t* requestedType)
9577
9649
  {
9578
- return (void*)&this->data_;
9650
+ if (rb_typeddata_inherited_p(this->rb_data_type_, requestedType))
9651
+ {
9652
+ return (void*)&this->data_;
9653
+ }
9654
+ else
9655
+ {
9656
+ throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
9657
+ this->rb_data_type_->wrap_struct_name,
9658
+ requestedType->wrap_struct_name);
9659
+ }
9579
9660
  }
9580
9661
 
9581
9662
  // ---- Wrapper& -----
9582
9663
  template <typename T>
9583
- inline Wrapper<T&>::Wrapper(T& data): data_(data)
9664
+ inline Wrapper<T&>::Wrapper(rb_data_type_t* rb_data_type, T& data) : WrapperBase(rb_data_type), data_(data)
9584
9665
  {
9585
9666
  this->isConst_ = std::is_const_v<std::remove_reference_t<T>>;
9586
9667
  }
@@ -9588,18 +9669,27 @@ namespace Rice::detail
9588
9669
  template <typename T>
9589
9670
  inline Wrapper<T&>::~Wrapper()
9590
9671
  {
9591
- Registries::instance.instances.remove(this->get());
9672
+ Registries::instance.instances.remove(this->get(this->rb_data_type_));
9592
9673
  }
9593
9674
 
9594
9675
  template <typename T>
9595
- inline void* Wrapper<T&>::get()
9676
+ inline void* Wrapper<T&>::get(rb_data_type_t* requestedType)
9596
9677
  {
9597
- return (void*)&this->data_;
9678
+ if (rb_typeddata_inherited_p(this->rb_data_type_, requestedType))
9679
+ {
9680
+ return (void*)&this->data_;
9681
+ }
9682
+ else
9683
+ {
9684
+ throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
9685
+ this->rb_data_type_->wrap_struct_name,
9686
+ requestedType->wrap_struct_name);
9687
+ }
9598
9688
  }
9599
9689
 
9600
9690
  // ---- Wrapper* -----
9601
9691
  template <typename T>
9602
- inline Wrapper<T*>::Wrapper(T* data, bool isOwner) : data_(data)
9692
+ inline Wrapper<T*>::Wrapper(rb_data_type_t* rb_data_type, T* data, bool isOwner) : WrapperBase(rb_data_type), data_(data)
9603
9693
  {
9604
9694
  this->isOwner_ = isOwner;
9605
9695
  this->isConst_ = std::is_const_v<std::remove_pointer_t<T>>;
@@ -9608,7 +9698,8 @@ namespace Rice::detail
9608
9698
  template <typename T>
9609
9699
  inline Wrapper<T*>::~Wrapper()
9610
9700
  {
9611
- Registries::instance.instances.remove(this->get());
9701
+ Registries::instance.instances.remove(this->get(this->rb_data_type_));
9702
+
9612
9703
  if constexpr (std::is_destructible_v<T>)
9613
9704
  {
9614
9705
  if (this->isOwner_)
@@ -9619,14 +9710,23 @@ namespace Rice::detail
9619
9710
  }
9620
9711
 
9621
9712
  template <typename T>
9622
- inline void* Wrapper<T*>::get()
9713
+ inline void* Wrapper<T*>::get(rb_data_type_t* requestedType)
9623
9714
  {
9624
- return (void*)this->data_;
9715
+ if (rb_typeddata_inherited_p(this->rb_data_type_, requestedType))
9716
+ {
9717
+ return (void*)this->data_;
9718
+ }
9719
+ else
9720
+ {
9721
+ throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
9722
+ this->rb_data_type_->wrap_struct_name,
9723
+ requestedType->wrap_struct_name);
9724
+ }
9625
9725
  }
9626
9726
 
9627
9727
  // ---- Wrapper** -----
9628
9728
  template <typename T>
9629
- inline Wrapper<T**>::Wrapper(T** data, bool isOwner) : data_(data)
9729
+ inline Wrapper<T**>::Wrapper(rb_data_type_t* rb_data_type, T** data, bool isOwner) : WrapperBase(rb_data_type), data_(data)
9630
9730
  {
9631
9731
  this->isOwner_ = isOwner;
9632
9732
  this->isConst_ = std::is_const_v<std::remove_pointer_t<std::remove_pointer_t<T>>>;
@@ -9635,7 +9735,8 @@ namespace Rice::detail
9635
9735
  template <typename T>
9636
9736
  inline Wrapper<T**>::~Wrapper()
9637
9737
  {
9638
- Registries::instance.instances.remove(this->get());
9738
+ Registries::instance.instances.remove(this->get(this->rb_data_type_));
9739
+
9639
9740
  if constexpr (std::is_destructible_v<T>)
9640
9741
  {
9641
9742
  if (this->isOwner_)
@@ -9646,9 +9747,18 @@ namespace Rice::detail
9646
9747
  }
9647
9748
 
9648
9749
  template <typename T>
9649
- inline void* Wrapper<T**>::get()
9750
+ inline void* Wrapper<T**>::get(rb_data_type_t* requestedType)
9650
9751
  {
9651
- return (void*)this->data_;
9752
+ if (rb_typeddata_inherited_p(this->rb_data_type_, requestedType))
9753
+ {
9754
+ return (void*)this->data_;
9755
+ }
9756
+ else
9757
+ {
9758
+ throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
9759
+ this->rb_data_type_->wrap_struct_name,
9760
+ requestedType->wrap_struct_name);
9761
+ }
9652
9762
  }
9653
9763
 
9654
9764
  // ---- Helper Functions -------
@@ -9665,7 +9775,7 @@ namespace Rice::detail
9665
9775
  // If Ruby is not the owner then wrap the reference
9666
9776
  if (!isOwner)
9667
9777
  {
9668
- wrapper = new Wrapper<T&>(data);
9778
+ wrapper = new Wrapper<T&>(rb_data_type, data);
9669
9779
  result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
9670
9780
  }
9671
9781
 
@@ -9674,12 +9784,12 @@ namespace Rice::detail
9674
9784
  {
9675
9785
  if constexpr (std::is_copy_constructible_v<typename T::value_type>)
9676
9786
  {
9677
- wrapper = new Wrapper<T>(data);
9787
+ wrapper = new Wrapper<T>(rb_data_type, data);
9678
9788
  result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
9679
9789
  }
9680
9790
  else
9681
9791
  {
9682
- wrapper = new Wrapper<T>(std::move(data));
9792
+ wrapper = new Wrapper<T>(rb_data_type, std::move(data));
9683
9793
  result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
9684
9794
  }
9685
9795
  }
@@ -9687,14 +9797,14 @@ namespace Rice::detail
9687
9797
  // Ruby is the owner so copy data
9688
9798
  else if constexpr (std::is_copy_constructible_v<T>)
9689
9799
  {
9690
- wrapper = new Wrapper<T>(data);
9800
+ wrapper = new Wrapper<T>(rb_data_type, data);
9691
9801
  result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
9692
9802
  }
9693
9803
 
9694
9804
  // Ruby is the owner so move data
9695
9805
  else if constexpr (std::is_move_constructible_v<T>)
9696
9806
  {
9697
- wrapper = new Wrapper<T>(std::move(data));
9807
+ wrapper = new Wrapper<T>(rb_data_type, std::move(data));
9698
9808
  result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
9699
9809
  }
9700
9810
 
@@ -9706,7 +9816,7 @@ namespace Rice::detail
9706
9816
  throw std::runtime_error(message);
9707
9817
  }
9708
9818
 
9709
- Registries::instance.instances.add(wrapper->get(), result);
9819
+ Registries::instance.instances.add(wrapper->get(rb_data_type), result);
9710
9820
 
9711
9821
  return result;
9712
9822
  };
@@ -9719,38 +9829,40 @@ namespace Rice::detail
9719
9829
  if (result != Qnil)
9720
9830
  return result;
9721
9831
 
9722
- WrapperBase* wrapper = new Wrapper<T*>(data, isOwner);
9832
+ WrapperBase* wrapper = new Wrapper<T*>(rb_data_type, data, isOwner);
9723
9833
  result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
9724
9834
 
9725
- Registries::instance.instances.add(wrapper->get(), result);
9835
+ Registries::instance.instances.add(wrapper->get(rb_data_type), result);
9726
9836
  return result;
9727
9837
  };
9728
9838
 
9729
9839
  template <typename T>
9730
9840
  inline T* unwrap(VALUE value, rb_data_type_t* rb_data_type, bool takeOwnership)
9731
9841
  {
9732
- if (rb_type(value) != RUBY_T_DATA)
9842
+ if (!RTYPEDDATA_P(value))
9733
9843
  {
9734
9844
  std::string message = "The Ruby object does not wrap a C++ object. It is actually a " +
9735
9845
  std::string(detail::protect(rb_obj_classname, value)) + ".";
9736
9846
  throw std::runtime_error(message);
9737
9847
  }
9738
9848
 
9739
- WrapperBase* wrapper = getWrapper(value, rb_data_type);
9849
+ WrapperBase* wrapper = static_cast<WrapperBase*>(RTYPEDDATA_DATA(value));
9740
9850
 
9741
9851
  if (wrapper == nullptr)
9742
9852
  {
9743
- std::string message = "Wrapped C++ object is nil. Did you override " +
9744
- std::string(detail::protect(rb_obj_classname, value)) +
9853
+ std::string message = "Wrapped C++ object is nil. Did you override " +
9854
+ std::string(detail::protect(rb_obj_classname, value)) +
9745
9855
  "#initialize and forget to call super?";
9746
9856
 
9747
9857
  throw std::runtime_error(message);
9748
9858
  }
9749
9859
 
9750
9860
  if (takeOwnership)
9861
+ {
9751
9862
  wrapper->setOwner(false);
9863
+ }
9752
9864
 
9753
- return static_cast<T*>(wrapper->get());
9865
+ return static_cast<T*>(wrapper->get(rb_data_type));
9754
9866
  }
9755
9867
 
9756
9868
  template <typename Wrapper_T>
@@ -9772,7 +9884,8 @@ namespace Rice::detail
9772
9884
  if (!RTYPEDDATA_P(value))
9773
9885
  {
9774
9886
  throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
9775
- detail::protect(rb_obj_classname, value), "wrapped C++ object");
9887
+ detail::protect(rb_obj_classname, value),
9888
+ "wrapped C++ object");
9776
9889
  }
9777
9890
 
9778
9891
  return static_cast<WrapperBase*>(RTYPEDDATA_DATA(value));
@@ -9783,24 +9896,26 @@ namespace Rice::detail
9783
9896
  }
9784
9897
 
9785
9898
  template <typename T>
9786
- inline void wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data)
9899
+ inline Wrapper<T*>* wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data)
9787
9900
  {
9788
9901
  using Wrapper_T = Wrapper<T*>;
9789
-
9902
+
9790
9903
  Wrapper_T* wrapper = nullptr;
9791
9904
  TypedData_Get_Struct(value, Wrapper_T, rb_data_type, wrapper);
9792
9905
  if (wrapper)
9793
9906
  {
9794
- Registries::instance.instances.remove(wrapper->get());
9907
+ Registries::instance.instances.remove(wrapper->get(rb_data_type));
9795
9908
  delete wrapper;
9796
9909
  }
9797
9910
 
9798
- wrapper = new Wrapper_T(data, true);
9911
+ wrapper = new Wrapper_T(rb_data_type, data, true);
9799
9912
  RTYPEDDATA_DATA(value) = wrapper;
9800
9913
 
9801
9914
  Registries::instance.instances.add(data, value);
9915
+
9916
+ return wrapper;
9802
9917
  }
9803
- } // namespace
9918
+ }
9804
9919
 
9805
9920
  // ========= Native.ipp =========
9806
9921
  namespace Rice::detail
@@ -9981,16 +10096,14 @@ namespace Rice::detail
9981
10096
  Arg* arg = parameters_[i]->arg();
9982
10097
  if (arg->isKeepAlive())
9983
10098
  {
9984
- static WrapperBase* selfWrapper = getWrapper(self);
9985
- selfWrapper->addKeepAlive(rubyValues[i].value());
10099
+ WrapperBase::addKeepAlive(self, rubyValues[i].value());
9986
10100
  }
9987
10101
  }
9988
10102
 
9989
10103
  // Check return value
9990
10104
  if (this->returnInfo_->isKeepAlive())
9991
10105
  {
9992
- WrapperBase* returnWrapper = getWrapper(returnValue);
9993
- returnWrapper->addKeepAlive(self);
10106
+ WrapperBase::addKeepAlive(returnValue, self);
9994
10107
  }
9995
10108
  }
9996
10109
 
@@ -10329,7 +10442,7 @@ namespace Rice::detail
10329
10442
  // matches or calls function pointer. Instead Ruby can call the static call method defined on
10330
10443
  // this class (&NativeAttribute_T::get).
10331
10444
  Identifier identifier(name);
10332
- detail::Registries::instance.natives.add(klass, identifier.id(), native);
10445
+ detail::Registries::instance.natives.replace(klass, identifier.id(), native);
10333
10446
  }
10334
10447
 
10335
10448
  template<typename Attribute_T>
@@ -10434,28 +10547,46 @@ namespace Rice::detail
10434
10547
  namespace Rice::detail
10435
10548
  {
10436
10549
  template<typename Attribute_T>
10437
- void NativeAttributeSet<Attribute_T>::define(VALUE klass, std::string name, Attribute_T attribute)
10550
+ template<typename...Arg_Ts>
10551
+ void NativeAttributeSet<Attribute_T>::define(VALUE klass, std::string name, Attribute_T attribute, Arg_Ts&...args)
10438
10552
  {
10439
- // Create a NativeAttributeSet that Ruby will call to read/write C++ variables
10440
- NativeAttribute_T* nativeAttribute = new NativeAttribute_T(klass, name, std::forward<Attribute_T>(attribute));
10553
+ // Extract Arg from Arg_Ts if present, otherwise create default
10554
+ using Arg_Tuple = std::tuple<Arg_Ts...>;
10555
+ constexpr std::size_t index = tuple_element_index_v<Arg_Tuple, Arg, ArgBuffer>;
10556
+
10557
+ std::unique_ptr<Arg> arg;
10558
+ if constexpr (index < std::tuple_size_v<Arg_Tuple>)
10559
+ {
10560
+ using Arg_T_Local = std::decay_t<std::tuple_element_t<index, Arg_Tuple>>;
10561
+ const Arg_T_Local& argInfo = std::get<index>(std::forward_as_tuple(std::forward<Arg_Ts>(args)...));
10562
+ arg = std::make_unique<Arg_T_Local>(argInfo);
10563
+ }
10564
+ else
10565
+ {
10566
+ arg = std::make_unique<Arg>("value");
10567
+ }
10568
+
10569
+ // Create the parameter
10570
+ auto parameter = std::make_unique<Parameter<T_Unqualified>>(std::move(arg));
10571
+
10572
+ // Create a NativeAttributeSet that Ruby will call to write C++ variables
10573
+ NativeAttribute_T* nativeAttribute = new NativeAttribute_T(klass, name, std::forward<Attribute_T>(attribute), std::move(parameter));
10441
10574
  std::unique_ptr<Native> native(nativeAttribute);
10442
10575
 
10443
10576
  // Define the write method name
10444
10577
  std::string setter = name + "=";
10445
10578
 
10446
- // Tell Ruby to invoke the static method write to get the attribute value
10579
+ // Tell Ruby to invoke the static method resolve to set the attribute value
10447
10580
  detail::protect(rb_define_method, klass, setter.c_str(), (RUBY_METHOD_FUNC)&Native::resolve, -1);
10448
10581
 
10449
- // Add to native registry. Since attributes cannot be overridden, there is no need to set the
10450
- // matches or calls function pointer. Instead Ruby can call the static call method defined on
10451
- // this class (&NativeAttribute_T::set).
10582
+ // Add to native registry
10452
10583
  Identifier identifier(setter);
10453
- detail::Registries::instance.natives.add(klass, identifier.id(), native);
10584
+ detail::Registries::instance.natives.replace(klass, identifier.id(), native);
10454
10585
  }
10455
10586
 
10456
10587
  template<typename Attribute_T>
10457
- NativeAttributeSet<Attribute_T>::NativeAttributeSet(VALUE klass, std::string name, Attribute_T attribute)
10458
- : Native(name), klass_(klass), attribute_(attribute)
10588
+ NativeAttributeSet<Attribute_T>::NativeAttributeSet(VALUE klass, std::string name, Attribute_T attribute, std::unique_ptr<Parameter<T_Unqualified>> parameter)
10589
+ : Native(name), klass_(klass), attribute_(attribute), parameter_(std::move(parameter))
10459
10590
  {
10460
10591
  }
10461
10592
 
@@ -10476,25 +10607,25 @@ namespace Rice::detail
10476
10607
  throw std::runtime_error("Incorrect number of parameters for setting attribute. Attribute: " + this->name_);
10477
10608
  }
10478
10609
 
10610
+ // Get the Ruby value and convert to native
10479
10611
  VALUE value = values.begin()->second;
10612
+ std::optional<VALUE> valueOpt(value);
10613
+ T_Unqualified nativeValue = this->parameter_->convertToNative(valueOpt);
10480
10614
 
10481
10615
  if constexpr (!std::is_null_pointer_v<Receiver_T>)
10482
10616
  {
10483
10617
  Receiver_T* nativeSelf = From_Ruby<Receiver_T*>().convert(self);
10484
-
10485
- // Deal with pointers to pointes, see Parameter::convertToNative commment
10486
- if constexpr (is_pointer_pointer_v<Attr_T> && !std::is_convertible_v<remove_cv_recursive_t<Attr_T>, Attr_T>)
10487
- {
10488
- nativeSelf->*attribute_ = (Attr_T)From_Ruby<T_Unqualified>().convert(value);
10489
- }
10490
- else
10491
- {
10492
- nativeSelf->*attribute_ = From_Ruby<T_Unqualified>().convert(value);
10493
- }
10618
+ nativeSelf->*attribute_ = (Attr_T)nativeValue;
10494
10619
  }
10495
10620
  else
10496
10621
  {
10497
- *attribute_ = From_Ruby<T_Unqualified>().convert(value);
10622
+ *attribute_ = nativeValue;
10623
+ }
10624
+
10625
+ // Check if we need to prevent the value from being garbage collected
10626
+ if (this->parameter_->arg()->isKeepAlive())
10627
+ {
10628
+ WrapperBase::addKeepAlive(self, value);
10498
10629
  }
10499
10630
 
10500
10631
  return value;
@@ -10515,18 +10646,8 @@ namespace Rice::detail
10515
10646
  template<typename Attribute_T>
10516
10647
  inline VALUE NativeAttributeSet<Attribute_T>::returnKlass()
10517
10648
  {
10518
- // Check if an array is being returned
10519
- bool isBuffer = dynamic_cast<ReturnBuffer*>(this->returnInfo_.get()) ? true : false;
10520
- if (isBuffer)
10521
- {
10522
- TypeMapper<Pointer<detail::remove_cv_recursive_t<std::remove_pointer_t<Attr_T>>>> typeMapper;
10523
- return typeMapper.rubyKlass();
10524
- }
10525
- else
10526
- {
10527
- TypeMapper<Attr_T> typeMapper;
10528
- return typeMapper.rubyKlass();
10529
- }
10649
+ TypeMapper<Attr_T> typeMapper;
10650
+ return typeMapper.rubyKlass();
10530
10651
  }
10531
10652
  }
10532
10653
 
@@ -11166,30 +11287,33 @@ namespace Rice::detail
11166
11287
  {
11167
11288
  return self;
11168
11289
  }
11169
- /* This case happens when a class wrapped by Rice is calling a method
11170
- defined on an ancestor class. For example, the std::map size method
11171
- is defined on _Tree not map. Rice needs to know the actual type
11172
- that was wrapped so it can correctly extract the C++ object from
11173
- the Ruby object. */
11174
- else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
11175
- std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
11176
- std::is_pointer_v<Receiver_T>)
11177
- {
11178
- Class_T* instance = From_Ruby<Class_T*>().convert(self);
11179
- return dynamic_cast<Receiver_T>(instance);
11180
- }
11181
- else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
11182
- std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
11183
- std::is_reference_v<Receiver_T>)
11184
- {
11185
- Class_T& instance = From_Ruby<Class_T&>().convert(self);
11186
- return dynamic_cast<Receiver_T>(instance);
11187
- }
11188
- // Self parameter could be derived from Object or it is an C++ instance and
11189
- // needs to be unwrapped from Ruby
11190
11290
  else
11191
11291
  {
11192
- return From_Ruby<Receiver_T>().convert(self);
11292
+ /* When a class wrapped by Rice calls a method defined on an ancestor class
11293
+ (e.g., std::map calling a method from _Tree), we need to unwrap as Class_T
11294
+ and dynamic_cast to the base class. Otherwise unwrap directly as Receiver_T. */
11295
+ constexpr bool isDerived = !std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
11296
+ std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T>;
11297
+
11298
+ if constexpr (isDerived)
11299
+ {
11300
+ if constexpr (std::is_pointer_v<Receiver_T>)
11301
+ {
11302
+ Class_T* instance = From_Ruby<Class_T*>().convert(self);
11303
+ return dynamic_cast<Receiver_T>(instance);
11304
+ }
11305
+ else if constexpr (std::is_reference_v<Receiver_T>)
11306
+ {
11307
+ Class_T& instance = From_Ruby<Class_T&>().convert(self);
11308
+ return dynamic_cast<Receiver_T>(instance);
11309
+ }
11310
+ }
11311
+ else
11312
+ {
11313
+ // Note GCC has a false warning: function may return address of local variable [-Wreturn-local-addr].
11314
+ // From_Ruby returns a reference to data in the Ruby object, not the temporary.
11315
+ return From_Ruby<Receiver_T>().convert(self);
11316
+ }
11193
11317
  }
11194
11318
  }
11195
11319
 
@@ -12119,6 +12243,11 @@ namespace Rice
12119
12243
  return RB_TEST(result);
12120
12244
  }
12121
12245
 
12246
+ inline void Object::extend(Module const& mod)
12247
+ {
12248
+ detail::protect(rb_extend_object, this->value(), mod.value());
12249
+ }
12250
+
12122
12251
  inline bool Object::respond_to(Identifier id) const
12123
12252
  {
12124
12253
  return bool(rb_respond_to(this->value(), id.id()));
@@ -13513,6 +13642,11 @@ namespace Rice
13513
13642
  return result;
13514
13643
  }
13515
13644
 
13645
+ inline Class Class::superclass() const
13646
+ {
13647
+ return detail::protect(rb_class_superclass, this->value());
13648
+ }
13649
+
13516
13650
  inline Class define_class_under(Object parent, Identifier id, const Class& superclass)
13517
13651
  {
13518
13652
  VALUE klass = detail::protect(rb_define_class_id_under, parent.value(), id, superclass.value());
@@ -14067,17 +14201,22 @@ namespace Rice
14067
14201
  template<typename T>
14068
14202
  inline void ruby_mark_internal(detail::WrapperBase* wrapper)
14069
14203
  {
14070
- // Tell the wrapper to mark the objects its keeping alive
14071
- wrapper->ruby_mark();
14204
+ detail::cpp_protect([&]
14205
+ {
14206
+ // Tell the wrapper to mark the objects its keeping alive
14207
+ wrapper->ruby_mark();
14072
14208
 
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);
14209
+ // Get the underlying data and call custom mark function (if any)
14210
+ // Use the wrapper's stored rb_data_type to avoid type mismatch
14211
+ T* data = static_cast<T*>(wrapper->get(Data_Type<T>::ruby_data_type()));
14212
+ ruby_mark<T>(data);
14213
+ });
14076
14214
  }
14077
14215
 
14078
14216
  template<typename T>
14079
14217
  inline void ruby_free_internal(detail::WrapperBase* wrapper)
14080
14218
  {
14219
+ // Destructors are noexcept so we cannot use cpp_protect here
14081
14220
  delete wrapper;
14082
14221
  }
14083
14222
 
@@ -14442,7 +14581,7 @@ namespace Rice
14442
14581
  }
14443
14582
  else
14444
14583
  {
14445
- detail::NativeAttributeSet<Attribute_T>::define(klass, name, std::forward<Attribute_T>(attribute));
14584
+ detail::NativeAttributeSet<Attribute_T>::define(klass, name, std::forward<Attribute_T>(attribute), args...);
14446
14585
  }
14447
14586
  }
14448
14587
 
@@ -15662,6 +15801,86 @@ namespace Rice
15662
15801
  }
15663
15802
  }
15664
15803
 
15804
+ // Dependent on Module, Array, Symbol - used by stl smart pointers
15805
+
15806
+ // ========= Forwards.hpp =========
15807
+
15808
+ namespace Rice::detail
15809
+ {
15810
+ // Setup method forwarding from a wrapper class to its wrapped type using Ruby's Forwardable.
15811
+ // This allows calling methods on the wrapper that get delegated to the wrapped object via
15812
+ // a "get" method that returns the wrapped object.
15813
+ //
15814
+ // Parameters:
15815
+ // wrapper_klass - The Ruby class to add forwarding to (e.g., SharedPtr_MyClass)
15816
+ // wrapped_klass - The Ruby class whose methods should be forwarded (e.g., MyClass)
15817
+ void define_forwarding(VALUE wrapper_klass, VALUE wrapped_klass);
15818
+ }
15819
+
15820
+
15821
+ // --------- Forwards.ipp ---------
15822
+ namespace Rice::detail
15823
+ {
15824
+ inline void define_forwarding(VALUE wrapper_klass, VALUE wrapped_klass)
15825
+ {
15826
+ protect(rb_require, "forwardable");
15827
+ Object forwardable = Object(rb_cObject).const_get("Forwardable");
15828
+ Object(wrapper_klass).extend(forwardable.value());
15829
+
15830
+ // Get wrapper class's method names to avoid conflicts
15831
+ std::set<std::string> wrapperMethodSet;
15832
+ for (Native* native : Registries::instance.natives.lookup(wrapper_klass, NativeKind::Method))
15833
+ {
15834
+ wrapperMethodSet.insert(native->name());
15835
+ }
15836
+ for (Native* native : Registries::instance.natives.lookup(wrapper_klass, NativeKind::AttributeReader))
15837
+ {
15838
+ wrapperMethodSet.insert(native->name());
15839
+ }
15840
+ for (Native* native : Registries::instance.natives.lookup(wrapper_klass, NativeKind::AttributeWriter))
15841
+ {
15842
+ wrapperMethodSet.insert(native->name() + "=");
15843
+ }
15844
+
15845
+ // Get wrapped class's method names from the registry, including ancestor classes
15846
+ std::set<std::string> wrappedMethodSet;
15847
+ Class klass(wrapped_klass);
15848
+ while (klass.value() != rb_cObject && klass.value() != Qnil)
15849
+ {
15850
+ for (Native* native : Registries::instance.natives.lookup(klass.value(), NativeKind::Method))
15851
+ {
15852
+ wrappedMethodSet.insert(native->name());
15853
+ }
15854
+ for (Native* native : Registries::instance.natives.lookup(klass.value(), NativeKind::AttributeReader))
15855
+ {
15856
+ wrappedMethodSet.insert(native->name());
15857
+ }
15858
+ for (Native* native : Registries::instance.natives.lookup(klass.value(), NativeKind::AttributeWriter))
15859
+ {
15860
+ wrappedMethodSet.insert(native->name() + "=");
15861
+ }
15862
+
15863
+ klass = klass.superclass();
15864
+ }
15865
+
15866
+ // Build the arguments array for def_delegators: [:get, :method1, :method2, ...]
15867
+ // Skip methods that are already defined on the wrapper class
15868
+ Array args;
15869
+ args.push(Symbol("get"));
15870
+ for (const std::string& method : wrappedMethodSet)
15871
+ {
15872
+ if (wrapperMethodSet.find(method) == wrapperMethodSet.end())
15873
+ {
15874
+ args.push(Symbol(method));
15875
+ }
15876
+ }
15877
+
15878
+ // Call def_delegators(*args)
15879
+ Object(wrapper_klass).vcall("def_delegators", args);
15880
+ }
15881
+ }
15882
+
15883
+
15665
15884
  // For now include libc support - maybe should be separate header file someday
15666
15885
 
15667
15886
  // ========= file.hpp =========