ruby-qt6-rice 2.1.0 → 6.0.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/Rakefile +12 -1
  4. data/ext/qt6/rice/extconf.rb +5 -0
  5. data/ext/qt6/rice/rice-rb.cpp +10 -0
  6. data/ext/qt6/rice/rice-rb.hpp +3 -0
  7. data/include/bando/common.hpp +2 -1
  8. data/include/bando/qobject/qdbusabstractadaptor.hpp +1 -1
  9. data/include/bando/qobject/qdbusabstractinterface.hpp +1 -1
  10. data/include/bando/qobject/qitemdelegate.hpp +1 -1
  11. data/include/bando/qobject/qlayout.hpp +1 -1
  12. data/include/bando/qobject/qwebenginepage.hpp +1 -1
  13. data/include/bando/qobject.hpp +1 -1
  14. data/include/bando/qwidget/qspinbox.hpp +1 -1
  15. data/include/bando/qwidget.hpp +1 -1
  16. data/include/rice/core/rice.hpp +1711 -1314
  17. data/include/rice/core/stl.hpp +562 -82
  18. data/include/rice/cxx/asserts.hpp +31 -0
  19. data/include/rice/cxx/concepts.hpp +31 -0
  20. data/include/rice/qt6/preludes/libqt6core.hpp +192 -0
  21. data/include/rice/qt6/preludes/libqt6gui.hpp +176 -0
  22. data/include/rice/qt6/preludes/libqt6multimedia.hpp +33 -0
  23. data/include/rice/qt6/preludes/libqt6qml.hpp +33 -0
  24. data/include/rice/qt6/preludes/libqt6quick.hpp +30 -0
  25. data/include/rice/qt6/preludes/libqt6webenginecore.hpp +30 -0
  26. data/include/rice/qt6/preludes/libqt6widgets.hpp +35 -0
  27. data/include/rice/qt6/preludes/qlass.hpp +63 -0
  28. data/include/rice/qt6/preludes/registries.hpp +52 -0
  29. data/include/rice/qt6/preludes.hpp +28 -0
  30. data/include/rice/qt6/qdbusreply.hpp +3 -3
  31. data/include/rice/qt6/qenum.hpp +1 -1
  32. data/include/rice/qt6/qflags.hpp +1 -1
  33. data/include/rice/qt6/qlist.hpp +17 -20
  34. data/include/rice/qt6/qmap.hpp +14 -11
  35. data/include/rice/qt6.hpp +3 -0
  36. data/lib/mkmf-rubyqt6.rb +24 -7
  37. data/lib/qt6/rice/version.rb +1 -1
  38. data/lib/qt6/rice.rb +1 -0
  39. metadata +18 -2
@@ -53,6 +53,7 @@
53
53
  #pragma GCC diagnostic ignored "-Wunknown-pragmas"
54
54
  #endif
55
55
 
56
+ #include <ruby/version.h>
56
57
  #include <ruby.h>
57
58
  #include <ruby/encoding.h>
58
59
  #include <ruby/thread.h>
@@ -89,23 +90,6 @@ extern "C" typedef VALUE (*RUBY_VALUE_FUNC)(VALUE);
89
90
  extern "C" typedef VALUE (*RUBY_METHOD_FUNC)(ANYARGS);
90
91
  #endif
91
92
 
92
- // This is a terrible hack for Ruby 3.1 and maybe earlier to avoid crashes when test_Attribute unit cases
93
- // are run. If ruby_options is called to initialize the interpeter (previously it was not), when
94
- // the attribute unit tests intentionally cause exceptions to happen, the exception is correctly processed.
95
- // However any calls back to Ruby, for example to get the exception message, crash because the ruby
96
- // execution context tag has been set to null. This does not happen in newer versions of Ruby. It is
97
- // unknown if this happens in real life or just the test caes.
98
- // Should be removed when Rice no longer supports Ruby 3.1
99
- #if RUBY_API_VERSION_MAJOR == 3 && RUBY_API_VERSION_MINOR < 2
100
- constexpr bool oldRuby = true;
101
- #elif RUBY_API_VERSION_MAJOR < 3
102
- constexpr bool oldRuby = true;
103
- #else
104
- constexpr bool oldRuby = false;
105
- #endif
106
-
107
-
108
-
109
93
 
110
94
 
111
95
  // C++ headers have to come after Ruby on MacOS for reasons I do not understand
@@ -125,6 +109,7 @@ extern "C" typedef VALUE (*RUBY_VALUE_FUNC)(VALUE);
125
109
 
126
110
  // ========= rice_traits.hpp =========
127
111
 
112
+ #include <complex>
128
113
  #include <ostream>
129
114
  #include <tuple>
130
115
  #include <type_traits>
@@ -237,6 +222,15 @@ namespace Rice4RubyQt6
237
222
  template <typename T>
238
223
  constexpr bool is_std_vector_v = is_std_vector<T>::value;
239
224
 
225
+ template<typename>
226
+ struct is_std_complex : std::false_type {};
227
+
228
+ template<typename T>
229
+ struct is_std_complex<std::complex<T>> : std::true_type {};
230
+
231
+ template <typename T>
232
+ constexpr bool is_std_complex_v = is_std_complex<T>::value;
233
+
240
234
  template<class T>
241
235
  struct is_pointer_pointer : std::false_type {};
242
236
 
@@ -265,17 +259,39 @@ namespace Rice4RubyQt6
265
259
  }, std::forward<Tuple_T>(tuple));
266
260
  }
267
261
 
262
+ // Detect if a type is complete (has a known size) or incomplete (forward-declared only)
263
+ template<typename T, typename = void>
264
+ struct is_complete : std::false_type {};
265
+
266
+ template<typename T>
267
+ struct is_complete<T, std::void_t<decltype(sizeof(T))>> : std::true_type {};
268
+
269
+ template<typename T>
270
+ constexpr bool is_complete_v = is_complete<T>::value;
271
+
268
272
  template<typename T, typename = void>
269
273
  struct is_wrapped : std::true_type {};
270
274
 
271
275
  template<typename T>
272
276
  struct is_wrapped<T, std::enable_if_t<std::is_fundamental_v<detail::intrinsic_type<T>> ||
273
- std::is_same_v<detail::intrinsic_type<T>, std::string>>
277
+ std::is_same_v<detail::intrinsic_type<T>, std::string> ||
278
+ is_std_complex_v<T>>
274
279
  >: std::false_type {};
275
280
 
276
281
  template<typename T>
277
282
  constexpr bool is_wrapped_v = is_wrapped<T>::value;
278
283
 
284
+ // ---------- RubyKlass ------------
285
+ template<typename, typename = std::void_t<>>
286
+ struct has_ruby_klass : std::false_type
287
+ {
288
+ };
289
+
290
+ template<typename T>
291
+ struct has_ruby_klass<T, std::void_t<decltype(T::rubyKlass())>> : std::true_type
292
+ {
293
+ };
294
+
279
295
  // -- Tuple Helpers ---
280
296
  template<typename T>
281
297
  struct tuple_shift;
@@ -474,14 +490,29 @@ namespace Rice4RubyQt6::detail
474
490
  using class_type = std::nullptr_t;
475
491
  };
476
492
 
493
+ // Lvalue references to functors and lambdas - strip the reference and defer
494
+ // to the base functor specialization. This allows named lambda variables (lvalues)
495
+ // to be passed to define_method in addition to inline temporaries (rvalues).
496
+ template<typename Function_T>
497
+ struct function_traits<Function_T&> : public function_traits<Function_T>
498
+ {
499
+ };
500
+
477
501
  // C functions and static member functions passed by pointer
478
502
  template<typename Return_T, typename ...Parameter_Ts>
479
503
  struct function_traits<Return_T(*)(Parameter_Ts...)> : public function_traits<Return_T(std::nullptr_t, Parameter_Ts...)>
480
504
  {
481
505
  using Function_T = Return_T(*)(Parameter_Ts...);
482
506
  };
483
-
484
- // C functions passed by pointer that take one or more defined parameter than a variable
507
+
508
+ // noexcept C functions and static member functions passed by pointer
509
+ template<typename Return_T, typename ...Parameter_Ts>
510
+ struct function_traits<Return_T(*)(Parameter_Ts...) noexcept> : public function_traits<Return_T(std::nullptr_t, Parameter_Ts...)>
511
+ {
512
+ using Function_T = Return_T(*)(Parameter_Ts...) noexcept;
513
+ };
514
+
515
+ // C functions passed by pointer that take one or more defined parameter than a variable
485
516
  // number of parameters (the second ...)
486
517
  template<typename Return_T, typename ...Parameter_Ts>
487
518
  struct function_traits<Return_T(*)(Parameter_Ts..., ...)> : public function_traits<Return_T(std::nullptr_t, Parameter_Ts...)>
@@ -494,6 +525,12 @@ namespace Rice4RubyQt6::detail
494
525
  {
495
526
  };
496
527
 
528
+ // noexcept C functions or static member functions passed by reference
529
+ template<typename Return_T, typename ...Parameter_Ts>
530
+ struct function_traits<Return_T(&)(Parameter_Ts...) noexcept> : public function_traits<Return_T(std::nullptr_t, Parameter_Ts...)>
531
+ {
532
+ };
533
+
497
534
  // Member Functions on C++ classes
498
535
  template<typename Return_T, typename Class_T, typename...Parameter_Ts>
499
536
  struct function_traits<Return_T(Class_T::*)(Parameter_Ts...)> : public function_traits<Return_T(Class_T*, Parameter_Ts...)>
@@ -611,6 +648,8 @@ namespace Rice4RubyQt6::detail
611
648
 
612
649
  void ruby_mark();
613
650
  void addKeepAlive(VALUE value);
651
+ const std::vector<VALUE>& getKeepAlive() const;
652
+ void setKeepAlive(const std::vector<VALUE>& keepAlive);
614
653
  void setOwner(bool value);
615
654
 
616
655
  protected:
@@ -631,7 +670,7 @@ namespace Rice4RubyQt6::detail
631
670
  public:
632
671
  Wrapper(rb_data_type_t* rb_data_type, T& data);
633
672
  Wrapper(rb_data_type_t* rb_data_type, T&& data);
634
- ~Wrapper();
673
+ ~Wrapper() = default;
635
674
  void* get(rb_data_type_t* requestedType) override;
636
675
 
637
676
  private:
@@ -676,7 +715,7 @@ namespace Rice4RubyQt6::detail
676
715
 
677
716
  // ---- Helper Functions ---------
678
717
  template <typename T>
679
- Wrapper<T*>* wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data);
718
+ Wrapper<T*>* wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data, VALUE source = Qnil);
680
719
 
681
720
  template <typename T>
682
721
  VALUE wrap(VALUE klass, rb_data_type_t* rb_data_type, T& data, bool isOwner);
@@ -693,9 +732,8 @@ namespace Rice4RubyQt6::detail
693
732
  WrapperBase* getWrapper(VALUE value);
694
733
  }
695
734
 
696
- // ========= Type.hpp =========
697
735
 
698
- #include <regex>
736
+ // ========= Type.hpp =========
699
737
 
700
738
  namespace Rice4RubyQt6::detail
701
739
  {
@@ -729,6 +767,28 @@ namespace Rice4RubyQt6::detail
729
767
  static bool verify();
730
768
  };
731
769
 
770
+ template <typename T, int N>
771
+ struct Type<T[N]>
772
+ {
773
+ static bool verify();
774
+ static VALUE rubyKlass();
775
+ };
776
+
777
+ template<typename T>
778
+ void verifyType();
779
+
780
+ template<typename Tuple_T>
781
+ void verifyTypes();
782
+ }
783
+
784
+
785
+ // ========= TypeIndexParser.hpp =========
786
+
787
+ #include <regex>
788
+ #include <typeindex>
789
+
790
+ namespace Rice4RubyQt6::detail
791
+ {
732
792
  class TypeIndexParser
733
793
  {
734
794
  public:
@@ -740,39 +800,131 @@ namespace Rice4RubyQt6::detail
740
800
  // public only for testing
741
801
  std::string findGroup(std::string& string, size_t start = 0);
742
802
 
743
- private:
803
+ protected:
744
804
  std::string demangle(char const* mangled_name);
745
805
  void removeGroup(std::string& string, std::regex regex);
746
806
  void replaceGroup(std::string& string, std::regex regex, std::string replacement);
747
807
  void capitalizeHelper(std::string& content, std::regex& regex);
748
808
  void replaceAll(std::string& string, std::regex regex, std::string replacement);
749
809
 
750
- private:
810
+ protected:
751
811
  const std::type_index typeIndex_;
752
812
  bool isFundamental_ = false;
753
813
  };
754
814
 
755
815
  template<typename T>
756
- class TypeMapper
816
+ class TypeDetail
757
817
  {
758
818
  public:
819
+ std::string name();
820
+ std::string simplifiedName();
821
+
759
822
  VALUE rubyKlass();
760
823
  std::string rubyName();
761
824
 
762
825
  private:
826
+ static std::type_index typeIndex();
827
+ static bool isFundamental();
763
828
  std::string rubyTypeName();
764
-
829
+
765
830
  private:
766
- TypeIndexParser typeIndexParser_{ typeid(T), std::is_fundamental_v<intrinsic_type<T>> };
831
+ TypeIndexParser typeIndexParser_{ typeIndex(), isFundamental() };
767
832
  };
833
+ }
768
834
 
769
- template<typename T>
770
- void verifyType();
771
835
 
772
- template<typename Tuple_T>
773
- void verifyTypes();
836
+ // Code to register Ruby objects with GC (declarations)
837
+
838
+ // ========= Anchor.hpp =========
839
+
840
+ #include <ruby.h>
841
+
842
+ namespace Rice4RubyQt6
843
+ {
844
+ namespace detail
845
+ {
846
+ //! Internal GC anchor for a Ruby VALUE.
847
+ /*!
848
+ * Anchor is a low-level adapter around the Ruby GC API.
849
+ * It owns a stable VALUE slot whose address is registered
850
+ * with the Ruby garbage collector.
851
+ *
852
+ * This class encapsulates all GC registration logic and is
853
+ * not part of the public Rice API.
854
+ */
855
+ class Anchor
856
+ {
857
+ public:
858
+ //! Construct an anchor for the given Ruby VALUE.
859
+ /*!
860
+ * The address of the internal VALUE is registered with the
861
+ * Ruby GC, preventing collection while this Anchor exists.
862
+ */
863
+ explicit Anchor(VALUE value);
864
+
865
+ //! Unregister the VALUE from the Ruby GC.
866
+ ~Anchor();
867
+
868
+ Anchor(const Anchor&) = delete;
869
+ Anchor& operator=(const Anchor&) = delete;
870
+
871
+ //! Retrieve the currently anchored VALUE.
872
+ VALUE get() const;
873
+
874
+ private:
875
+ static void disable(VALUE);
876
+ static void registerExitHandler();
877
+
878
+ inline static bool enabled_ = true;
879
+ inline static bool exitHandlerRegistered_ = false;
880
+
881
+ private:
882
+ bool registered_ = false;
883
+
884
+ //! GC-visible Ruby VALUE slot.
885
+ VALUE value_ = Qnil;
886
+ };
887
+ }
774
888
  }
775
889
 
890
+ // ========= Pin.hpp =========
891
+
892
+ namespace Rice4RubyQt6
893
+ {
894
+ //! Strong lifetime policy for a Ruby VALUE.
895
+ /*!
896
+ * Pin represents a Ruby VALUE whose lifetime is explicitly
897
+ * extended by C++ code.
898
+ *
899
+ * Internally, Pin uses a GC Anchor to keep the VALUE alive.
900
+ * Copying a Pin shares the underlying anchor; moving is cheap.
901
+ *
902
+ * This type is intended for C++-owned objects that store Ruby
903
+ * values but are not themselves owned by Ruby and thus do not
904
+ * participate in the GC via a mark function.
905
+ */
906
+ class Pin
907
+ {
908
+ public:
909
+ //! Construct a pin from a Ruby VALUE.
910
+ Pin(VALUE value);
911
+
912
+ // Copying
913
+ Pin(const Pin&) = default;
914
+ Pin& operator=(const Pin&) = default;
915
+
916
+ // Moving
917
+ Pin(Pin&&) noexcept = default;
918
+ Pin& operator=(Pin&&) noexcept = default;
919
+
920
+ //! Retrieve the pinned Ruby VALUE.
921
+ VALUE value() const;
922
+
923
+ private:
924
+ //! Shared ownership of the internal GC anchor.
925
+ std::shared_ptr<detail::Anchor> anchor_;
926
+ };
927
+ }
776
928
 
777
929
  // Code for C++ to call Ruby
778
930
 
@@ -835,8 +987,7 @@ namespace Rice4RubyQt6
835
987
  VALUE value() const;
836
988
 
837
989
  private:
838
- // TODO: Do we need to tell the Ruby gc about an exception instance?
839
- mutable VALUE exception_ = Qnil;
990
+ Pin exception_ = Qnil;
840
991
  mutable std::string message_;
841
992
  };
842
993
  } // namespace Rice4RubyQt6
@@ -1324,12 +1475,6 @@ namespace Rice4RubyQt6
1324
1475
  //! Returns if the argument should be treated as a value
1325
1476
  bool isValue() const;
1326
1477
 
1327
- //! Specifies if the argument should capture a block
1328
- virtual Arg& setBlock();
1329
-
1330
- //! Returns if the argument should capture a block
1331
- bool isBlock() const;
1332
-
1333
1478
  //! Specifies if the argument is opaque and Rice should not convert it from Ruby to C++ or vice versa.
1334
1479
  //! This is useful for callbacks and user provided data paramameters.
1335
1480
  virtual Arg& setOpaque();
@@ -1348,7 +1493,6 @@ namespace Rice4RubyQt6
1348
1493
  //! Our saved default value
1349
1494
  std::any defaultValue_;
1350
1495
  bool isValue_ = false;
1351
- bool isBlock_ = false;
1352
1496
  bool isKeepAlive_ = false;
1353
1497
  bool isOwner_ = false;
1354
1498
  bool isOpaque_ = false;
@@ -1358,6 +1502,7 @@ namespace Rice4RubyQt6
1358
1502
  {
1359
1503
  public:
1360
1504
  ArgBuffer(std::string name);
1505
+ using Arg::operator=; // Inherit the templated operator=
1361
1506
  };
1362
1507
  } // Rice
1363
1508
 
@@ -1494,6 +1639,78 @@ namespace Rice4RubyQt6::detail
1494
1639
  };
1495
1640
  }
1496
1641
 
1642
+ // Code to register Ruby objects with GC (implementations)
1643
+
1644
+ // ========= Anchor.ipp =========
1645
+ namespace Rice4RubyQt6
1646
+ {
1647
+ namespace detail
1648
+ {
1649
+ inline Anchor::Anchor(VALUE value) : value_(value)
1650
+ {
1651
+ if (!RB_SPECIAL_CONST_P(value))
1652
+ {
1653
+ Anchor::registerExitHandler();
1654
+ detail::protect(rb_gc_register_address, &this->value_);
1655
+ this->registered_ = true;
1656
+ }
1657
+ }
1658
+
1659
+ inline Anchor::~Anchor()
1660
+ {
1661
+ if (Anchor::enabled_ && this->registered_)
1662
+ {
1663
+ detail::protect(rb_gc_unregister_address, &this->value_);
1664
+ }
1665
+ // Ruby auto detects VALUEs in the stack, so make sure up in case this object is on the stack
1666
+ this->registered_ = false;
1667
+ this->value_ = Qnil;
1668
+ }
1669
+
1670
+ inline VALUE Anchor::get() const
1671
+ {
1672
+ return this->value_;
1673
+ }
1674
+
1675
+ // This will be called by ruby at exit - we want to disable further unregistering
1676
+ inline void Anchor::disable(VALUE)
1677
+ {
1678
+ Anchor::enabled_ = false;
1679
+ }
1680
+
1681
+ inline void Anchor::registerExitHandler()
1682
+ {
1683
+ if (!Anchor::exitHandlerRegistered_)
1684
+ {
1685
+ detail::protect(rb_set_end_proc, &Anchor::disable, Qnil);
1686
+ Anchor::exitHandlerRegistered_ = true;
1687
+ }
1688
+ }
1689
+ }
1690
+ }
1691
+
1692
+ // ========= Pin.ipp =========
1693
+ namespace Rice4RubyQt6
1694
+ {
1695
+ inline Pin::Pin(VALUE value)
1696
+ : anchor_(std::make_shared<detail::Anchor>(value))
1697
+ {
1698
+ }
1699
+
1700
+ inline VALUE Pin::value() const
1701
+ {
1702
+ // Anchor can be nil after a move
1703
+ if (this->anchor_)
1704
+ {
1705
+ return this->anchor_->get();
1706
+ }
1707
+ else
1708
+ {
1709
+ return Qnil;
1710
+ }
1711
+ }
1712
+ }
1713
+
1497
1714
  // C++ API declarations
1498
1715
 
1499
1716
  // ========= Encoding.hpp =========
@@ -1625,41 +1842,36 @@ namespace Rice4RubyQt6
1625
1842
  class Object
1626
1843
  {
1627
1844
  public:
1845
+ //! Construct an empty Object wrapper.
1846
+ Object();
1847
+
1628
1848
  //! Encapsulate an existing ruby object.
1629
- Object(VALUE value = Qnil) : value_(value) {}
1849
+ Object(VALUE value);
1630
1850
 
1631
1851
  //! Destructor
1632
- virtual ~Object();
1852
+ virtual ~Object() = default;
1633
1853
 
1634
1854
  // Enable copying
1635
1855
  Object(const Object& other) = default;
1636
1856
  Object& operator=(const Object& other) = default;
1637
1857
 
1638
1858
  // Enable moving
1639
- Object(Object&& other);
1640
- Object& operator=(Object&& other);
1859
+ Object(Object&& other) = default;
1860
+ Object& operator=(Object&& other) = default;
1641
1861
 
1642
- //! Returns false if the object is nil or false; returns true
1643
- //! otherwise.
1644
- // Having this conversion also prevents accidental conversion to
1645
- // undesired integral types (e.g. long or int) by making the
1646
- // conversion ambiguous.
1647
- bool test() const { return RTEST(value_); }
1862
+ //! Implicit conversion to VALUE.
1863
+ operator VALUE() const;
1864
+
1865
+ //! Explicitly get the encapsulated VALUE.
1866
+ VALUE value() const;
1648
1867
 
1649
1868
  //! Returns false if the object is nil or false; returns true
1650
1869
  //! otherwise.
1651
- operator bool() const { return test(); }
1870
+ explicit operator bool() const;
1652
1871
 
1653
1872
  //! Returns true if the object is nil, false otherwise.
1654
- bool is_nil() const { return NIL_P(value_); }
1873
+ bool is_nil() const;
1655
1874
 
1656
- //! Implicit conversion to VALUE.
1657
- operator VALUE() const { return value_; }
1658
-
1659
- //! Explicitly get the encapsulated VALUE.
1660
- // Returns a const ref so that Address_Registration_Guard can access
1661
- // the address where the VALUE is stored
1662
- VALUE const volatile& value() const { return value_; }
1663
1875
 
1664
1876
  //! Get the class of an object.
1665
1877
  /*! \return the object's Class.
@@ -1860,11 +2072,15 @@ namespace Rice4RubyQt6
1860
2072
  void remove_const(Identifier name);
1861
2073
 
1862
2074
  protected:
2075
+ //! Checks the encapsulated VALUE is not nil and returns it. If it is nil
2076
+ //! an exception is thrown.
2077
+ VALUE validated_value() const;
2078
+
1863
2079
  //! Set the encapsulated value.
1864
- void set_value(VALUE v);
2080
+ void set_value(VALUE value);
1865
2081
 
1866
2082
  private:
1867
- volatile VALUE value_;
2083
+ Pin value_;
1868
2084
  };
1869
2085
 
1870
2086
  std::ostream& operator<<(std::ostream& out, Object const& obj);
@@ -1873,42 +2089,7 @@ namespace Rice4RubyQt6
1873
2089
  bool operator!=(Object const& lhs, Object const& rhs);
1874
2090
  bool operator<(Object const& lhs, Object const& rhs);
1875
2091
  bool operator>(Object const& lhs, Object const& rhs);
1876
-
1877
- extern Object const Nil;
1878
- extern Object const True;
1879
- extern Object const False;
1880
- extern Object const Undef;
1881
- } // namespace Rice4RubyQt6
1882
-
1883
-
1884
- // ========= Builtin_Object.hpp =========
1885
-
1886
- namespace Rice4RubyQt6
1887
- {
1888
- //! A smartpointer-like wrapper for Ruby builtin objects.
1889
- /*! A builtin object is one of Ruby's internal types, e.g. RArray or
1890
- * RString. Every builtin type structure has a corresponding integer
1891
- * type number (e.g T_ARRAY for RArray or T_STRING for RString). This
1892
- * class is a wrapper for those types of objects, primarily useful as a
1893
- * base class for other wrapper classes like Array and Hash.
1894
- */
1895
- template<int Builtin_Type>
1896
- class Builtin_Object
1897
- : public Object
1898
- {
1899
- public:
1900
- //! Wrap an already allocated Ruby object.
1901
- /*! Checks to see if the object is an object of type Builtin_Type; a
1902
- * C++ exception is thrown if this is not the case.
1903
- * \param value the object to be wrapped.
1904
- */
1905
- Builtin_Object(Object value);
1906
-
1907
- RObject& operator*() const; //!< Return a reference to obj_
1908
- RObject* operator->() const; //!< Return a pointer to obj_
1909
- RObject* get() const; //!< Return a pointer to obj_
1910
- };
1911
- } // namespace Rice4RubyQt6
2092
+ }
1912
2093
 
1913
2094
 
1914
2095
  // ========= String.hpp =========
@@ -1925,7 +2106,7 @@ namespace Rice4RubyQt6
1925
2106
  * std::cout << s.length() << std::endl;
1926
2107
  * \endcode
1927
2108
  */
1928
- class String : public Builtin_Object<T_STRING>
2109
+ class String : public Object
1929
2110
  {
1930
2111
  public:
1931
2112
  //! Construct a new string.
@@ -2046,7 +2227,7 @@ namespace Rice4RubyQt6
2046
2227
  * \endcode
2047
2228
  */
2048
2229
  class Array
2049
- : public Builtin_Object<T_ARRAY>
2230
+ : public Object
2050
2231
  {
2051
2232
  public:
2052
2233
  //! Construct a new array
@@ -2184,8 +2365,8 @@ namespace Rice4RubyQt6
2184
2365
  //! Construct a new Proxy
2185
2366
  Proxy(Array array, long index);
2186
2367
 
2187
- //! Implicit conversions
2188
- operator Object() const;
2368
+ //! Implicit conversion to VALUE.
2369
+ operator VALUE() const;
2189
2370
 
2190
2371
  //! Explicit conversion to VALUE.
2191
2372
  VALUE value() const;
@@ -2290,7 +2471,7 @@ namespace Rice4RubyQt6
2290
2471
  //! h[10] = String("bar");
2291
2472
  //! std::cout << String(h[42]) << std::endl;
2292
2473
  //! \endcode
2293
- class Hash: public Builtin_Object<T_HASH>
2474
+ class Hash: public Object
2294
2475
  {
2295
2476
  public:
2296
2477
  //! Construct a new hash.
@@ -2367,8 +2548,8 @@ namespace Rice4RubyQt6
2367
2548
  //! Construct a new Proxy.
2368
2549
  Proxy(Hash* hash, Object key);
2369
2550
 
2370
- //! Implicit conversion to Object.
2371
- operator Object() const;
2551
+ //! Implicit conversion to VALUE.
2552
+ operator VALUE() const;
2372
2553
 
2373
2554
  //! Explicit conversion to VALUE.
2374
2555
  VALUE value() const;
@@ -2466,7 +2647,6 @@ namespace Rice4RubyQt6
2466
2647
 
2467
2648
 
2468
2649
 
2469
-
2470
2650
  // ========= Module.hpp =========
2471
2651
 
2472
2652
  namespace Rice4RubyQt6
@@ -2481,18 +2661,17 @@ namespace Rice4RubyQt6
2481
2661
  * Many of the methods are defined in Module_impl.hpp so that they can
2482
2662
  * return a reference to the most derived type.
2483
2663
  */
2484
- // TODO: we can't inherit from Builtin_Object, because Class needs
2485
- // type T_CLASS and Module needs type T_MODULE
2664
+ // Module and Class both derive from Object to preserve Ruby's hierarchy.
2486
2665
  class Module : public Object
2487
2666
  {
2488
2667
  public:
2489
- //! Default construct a Module and initialize it to rb_cObject.
2490
- Module();
2668
+ //! Default construct an empty Module wrapper.
2669
+ Module() = default;
2491
2670
 
2492
2671
  //! Construct a Module from an existing Module object.
2493
2672
  Module(VALUE v);
2494
2673
 
2495
- //! Construct a Module from an string that references a Module
2674
+ //! Construct a Module from a string that references a Module
2496
2675
  Module(std::string name, Object under = rb_cObject);
2497
2676
 
2498
2677
  //! Return the name of the module.
@@ -2526,7 +2705,7 @@ namespace Rice4RubyQt6
2526
2705
  */
2527
2706
  inline auto& include_module(Module const& inc)
2528
2707
  {
2529
- detail::protect(rb_include_module, this->value(), inc.value());
2708
+ detail::protect(rb_include_module, this->validated_value(), inc.value());
2530
2709
  return *this;
2531
2710
  }
2532
2711
 
@@ -2550,7 +2729,7 @@ inline auto& include_module(Module const& inc)
2550
2729
  template<typename Method_T, typename...Arg_Ts>
2551
2730
  inline auto& define_method(std::string name, Method_T&& method, const Arg_Ts&...args)
2552
2731
  {
2553
- this->wrap_native_method(this->value(), name, std::forward<Method_T>(method), args...);
2732
+ this->wrap_native_method(this->validated_value(), name, std::forward<Method_T>(method), args...);
2554
2733
  return *this;
2555
2734
  }
2556
2735
 
@@ -2568,7 +2747,7 @@ inline auto& define_method(std::string name, Method_T&& method, const Arg_Ts&...
2568
2747
  template<typename Function_T, typename...Arg_Ts>
2569
2748
  inline auto& define_function(std::string name, Function_T&& func, const Arg_Ts&...args)
2570
2749
  {
2571
- this->wrap_native_function(this->value(), name, std::forward<Function_T>(func), args...);
2750
+ this->wrap_native_function(this->validated_value(), name, std::forward<Function_T>(func), args...);
2572
2751
  return *this;
2573
2752
  }
2574
2753
 
@@ -2642,7 +2821,7 @@ template<typename Constant_T>
2642
2821
  inline auto& define_constant(std::string name, Constant_T value)
2643
2822
  {
2644
2823
  using Base_T = detail::remove_cv_recursive_t<Constant_T>;
2645
- detail::protect(rb_define_const, this->value(), name.c_str(), detail::To_Ruby<Base_T>().convert(value));
2824
+ detail::protect(rb_define_const, this->validated_value(), name.c_str(), detail::To_Ruby<Base_T>().convert(value));
2646
2825
  return *this;
2647
2826
  }
2648
2827
  protected:
@@ -2724,7 +2903,7 @@ namespace Rice4RubyQt6
2724
2903
  */
2725
2904
  inline auto& include_module(Module const& inc)
2726
2905
  {
2727
- detail::protect(rb_include_module, this->value(), inc.value());
2906
+ detail::protect(rb_include_module, this->validated_value(), inc.value());
2728
2907
  return *this;
2729
2908
  }
2730
2909
 
@@ -2748,7 +2927,7 @@ inline auto& include_module(Module const& inc)
2748
2927
  template<typename Method_T, typename...Arg_Ts>
2749
2928
  inline auto& define_method(std::string name, Method_T&& method, const Arg_Ts&...args)
2750
2929
  {
2751
- this->wrap_native_method(this->value(), name, std::forward<Method_T>(method), args...);
2930
+ this->wrap_native_method(this->validated_value(), name, std::forward<Method_T>(method), args...);
2752
2931
  return *this;
2753
2932
  }
2754
2933
 
@@ -2766,7 +2945,7 @@ inline auto& define_method(std::string name, Method_T&& method, const Arg_Ts&...
2766
2945
  template<typename Function_T, typename...Arg_Ts>
2767
2946
  inline auto& define_function(std::string name, Function_T&& func, const Arg_Ts&...args)
2768
2947
  {
2769
- this->wrap_native_function(this->value(), name, std::forward<Function_T>(func), args...);
2948
+ this->wrap_native_function(this->validated_value(), name, std::forward<Function_T>(func), args...);
2770
2949
  return *this;
2771
2950
  }
2772
2951
 
@@ -2840,7 +3019,7 @@ template<typename Constant_T>
2840
3019
  inline auto& define_constant(std::string name, Constant_T value)
2841
3020
  {
2842
3021
  using Base_T = detail::remove_cv_recursive_t<Constant_T>;
2843
- detail::protect(rb_define_const, this->value(), name.c_str(), detail::To_Ruby<Base_T>().convert(value));
3022
+ detail::protect(rb_define_const, this->validated_value(), name.c_str(), detail::To_Ruby<Base_T>().convert(value));
2844
3023
  return *this;
2845
3024
  }
2846
3025
  };
@@ -2958,11 +3137,15 @@ namespace Rice4RubyQt6::detail
2958
3137
 
2959
3138
  namespace Rice4RubyQt6
2960
3139
  {
2961
- enum class AttrAccess
3140
+ struct AttrAccess
2962
3141
  {
2963
- ReadWrite,
2964
- Read,
2965
- Write
3142
+ struct ReadWriteType {};
3143
+ struct ReadType {};
3144
+ struct WriteType {};
3145
+
3146
+ static constexpr ReadWriteType ReadWrite{};
3147
+ static constexpr ReadType Read{};
3148
+ static constexpr WriteType Write{};
2966
3149
  };
2967
3150
 
2968
3151
  namespace detail
@@ -3118,27 +3301,6 @@ namespace Rice4RubyQt6
3118
3301
  template<typename Constructor_T, typename...Rice_Arg_Ts>
3119
3302
  Data_Type<T>& define_constructor(Constructor_T constructor, Rice_Arg_Ts const& ...args);
3120
3303
 
3121
- /*! Runs a function that should define this Data_Types methods and attributes.
3122
- * This is useful when creating classes from a C++ class template.
3123
- *
3124
- * \param builder A function that addes methods/attributes to this class
3125
- *
3126
- * For example:
3127
- * \code
3128
- * void builder(Data_Type<Matrix<T, R, C>>& klass)
3129
- * {
3130
- * klass.define_method...
3131
- * return klass;
3132
- * }
3133
- *
3134
- * define_class<<Matrix<T, R, C>>>("Matrix")
3135
- * .build(&builder);
3136
- *
3137
- * \endcode
3138
- */
3139
- template<typename Func_T>
3140
- Data_Type<T>& define(Func_T func);
3141
-
3142
3304
  //! Register a Director class for this class.
3143
3305
  /*! For any class that uses Rice4RubyQt6::Director to enable polymorphism
3144
3306
  * across the languages, you need to register that director proxy
@@ -3189,11 +3351,11 @@ namespace Rice4RubyQt6
3189
3351
  template<typename Iterator_Func_T>
3190
3352
  Data_Type<T>& define_iterator(Iterator_Func_T begin, Iterator_Func_T end, std::string name = "each");
3191
3353
 
3192
- template <typename Attribute_T, typename...Arg_Ts>
3193
- Data_Type<T>& define_attr(std::string name, Attribute_T attribute, AttrAccess access = AttrAccess::ReadWrite, const Arg_Ts&...args);
3194
-
3195
- template <typename Attribute_T, typename...Arg_Ts>
3196
- Data_Type<T>& define_singleton_attr(std::string name, Attribute_T attribute, AttrAccess access = AttrAccess::ReadWrite, const Arg_Ts&...args);
3354
+ template <typename Attribute_T, typename Access_T = AttrAccess::ReadWriteType, typename...Arg_Ts>
3355
+ Data_Type<T>& define_attr(std::string name, Attribute_T attribute, Access_T access = {}, const Arg_Ts&...args);
3356
+
3357
+ template <typename Attribute_T, typename Access_T = AttrAccess::ReadWriteType, typename...Arg_Ts>
3358
+ Data_Type<T>& define_singleton_attr(std::string name, Attribute_T attribute, Access_T access = {}, const Arg_Ts&...args);
3197
3359
 
3198
3360
  // Include these methods to call methods from Module but return
3199
3361
  // an instance of the current classes. This is an alternative to
@@ -3206,7 +3368,7 @@ namespace Rice4RubyQt6
3206
3368
  */
3207
3369
  inline auto& include_module(Module const& inc)
3208
3370
  {
3209
- detail::protect(rb_include_module, this->value(), inc.value());
3371
+ detail::protect(rb_include_module, this->validated_value(), inc.value());
3210
3372
  return *this;
3211
3373
  }
3212
3374
 
@@ -3230,7 +3392,7 @@ inline auto& include_module(Module const& inc)
3230
3392
  template<typename Method_T, typename...Arg_Ts>
3231
3393
  inline auto& define_method(std::string name, Method_T&& method, const Arg_Ts&...args)
3232
3394
  {
3233
- this->wrap_native_method(this->value(), name, std::forward<Method_T>(method), args...);
3395
+ this->wrap_native_method(this->validated_value(), name, std::forward<Method_T>(method), args...);
3234
3396
  return *this;
3235
3397
  }
3236
3398
 
@@ -3248,7 +3410,7 @@ inline auto& define_method(std::string name, Method_T&& method, const Arg_Ts&...
3248
3410
  template<typename Function_T, typename...Arg_Ts>
3249
3411
  inline auto& define_function(std::string name, Function_T&& func, const Arg_Ts&...args)
3250
3412
  {
3251
- this->wrap_native_function(this->value(), name, std::forward<Function_T>(func), args...);
3413
+ this->wrap_native_function(this->validated_value(), name, std::forward<Function_T>(func), args...);
3252
3414
  return *this;
3253
3415
  }
3254
3416
 
@@ -3322,7 +3484,7 @@ template<typename Constant_T>
3322
3484
  inline auto& define_constant(std::string name, Constant_T value)
3323
3485
  {
3324
3486
  using Base_T = detail::remove_cv_recursive_t<Constant_T>;
3325
- detail::protect(rb_define_const, this->value(), name.c_str(), detail::To_Ruby<Base_T>().convert(value));
3487
+ detail::protect(rb_define_const, this->validated_value(), name.c_str(), detail::To_Ruby<Base_T>().convert(value));
3326
3488
  return *this;
3327
3489
  }
3328
3490
  protected:
@@ -3334,7 +3496,7 @@ inline auto& define_constant(std::string name, Constant_T value)
3334
3496
  * \return *this
3335
3497
  */
3336
3498
  template <typename Base_T = void>
3337
- static Data_Type<T> bind(const Module& klass);
3499
+ static Data_Type<T> bind(const Module& klass, rb_data_type_t *data_type = nullptr);
3338
3500
 
3339
3501
  template<typename T_, typename Base_T>
3340
3502
  friend Rice4RubyQt6::Data_Type<T_> define_class_under(Object parent, Identifier id, Class superKlass);
@@ -3345,11 +3507,14 @@ inline auto& define_constant(std::string name, Constant_T value)
3345
3507
  template<typename T_, typename Base_T>
3346
3508
  friend Rice4RubyQt6::Data_Type<T_> define_class(char const * name);
3347
3509
 
3510
+ template<typename T_, typename Base_T>
3511
+ friend Rice4RubyQt6::Data_Type<T_> declare_class_under(Object parent, char const* name, rb_data_type_t *data_type);
3512
+
3348
3513
  template<typename Method_T, typename...Arg_Ts>
3349
3514
  void wrap_native_method(VALUE klass, std::string name, Method_T&& function, const Arg_Ts&...args);
3350
3515
 
3351
- template <typename Attribute_T, typename...Arg_Ts>
3352
- Data_Type<T>& define_attr_internal(VALUE klass, std::string name, Attribute_T attribute, AttrAccess access, const Arg_Ts&...args);
3516
+ template <typename Attribute_T, typename Access_T, typename...Arg_Ts>
3517
+ Data_Type<T>& define_attr_internal(VALUE klass, std::string name, Attribute_T attribute, Access_T access, const Arg_Ts&...args);
3353
3518
 
3354
3519
  private:
3355
3520
  template<typename T_>
@@ -3398,6 +3563,13 @@ inline auto& define_constant(std::string name, Constant_T value)
3398
3563
  */
3399
3564
  template<typename T, typename Base_T = void>
3400
3565
  Data_Type<T> define_class(char const* name);
3566
+
3567
+ //! Identical to define_class_under, except it use an existed RTypedData.
3568
+ /*! This allows you to bind the Data_Type<T> instance in every DLL on Win32
3569
+ * to the same RTypedData, see [issue#355](https://github.com/ruby-rice/rice/issues/355).
3570
+ */
3571
+ template<typename T, typename Base_T = void>
3572
+ Data_Type<T> declare_class_under(Object parent, char const* name, rb_data_type_t *data_type);
3401
3573
  }
3402
3574
 
3403
3575
 
@@ -3635,6 +3807,17 @@ namespace Rice4RubyQt6::detail
3635
3807
  static inline std::string name = "Float";
3636
3808
  };
3637
3809
 
3810
+ template<>
3811
+ class RubyType<long double>
3812
+ {
3813
+ public:
3814
+ using FromRuby_T = double(*)(VALUE);
3815
+
3816
+ static inline FromRuby_T fromRuby = rb_num2dbl;
3817
+ static inline std::string packTemplate = "d*";
3818
+ static inline std::string name = "Float";
3819
+ };
3820
+
3638
3821
  template<>
3639
3822
  class RubyType<void>
3640
3823
  {
@@ -3693,7 +3876,10 @@ namespace Rice4RubyQt6::detail
3693
3876
  VALUE klasses();
3694
3877
 
3695
3878
  private:
3696
- std::optional<std::pair<VALUE, rb_data_type_t*>> lookup(const std::type_info& typeInfo);
3879
+ template <typename T>
3880
+ std::type_index key();
3881
+
3882
+ std::optional<std::pair<VALUE, rb_data_type_t*>> lookup(std::type_index typeIndex);
3697
3883
  void raiseUnverifiedType(const std::string& typeName);
3698
3884
 
3699
3885
  std::unordered_map<std::type_index, std::pair<VALUE, rb_data_type_t*>> registry_{};
@@ -3704,32 +3890,40 @@ namespace Rice4RubyQt6::detail
3704
3890
 
3705
3891
  // ========= InstanceRegistry.hpp =========
3706
3892
 
3893
+ #include <type_traits>
3894
+
3707
3895
  namespace Rice4RubyQt6::detail
3708
3896
  {
3709
3897
  class InstanceRegistry
3710
3898
  {
3711
3899
  public:
3900
+ enum class Mode
3901
+ {
3902
+ Off,
3903
+ Owned,
3904
+ All
3905
+ };
3906
+
3712
3907
  template <typename T>
3713
- VALUE lookup(T& cppInstance);
3908
+ VALUE lookup(T* cppInstance, bool isOwner);
3714
3909
 
3715
3910
  template <typename T>
3716
- VALUE lookup(T* cppInstance);
3717
- VALUE lookup(void* cppInstance);
3911
+ void add(T* cppInstance, VALUE rubyInstance, bool isOwner);
3718
3912
 
3719
- void add(void* cppInstance, VALUE rubyInstance);
3720
3913
  void remove(void* cppInstance);
3721
3914
  void clear();
3722
3915
 
3723
3916
  public:
3724
- bool isEnabled = false;
3917
+ Mode mode = Mode::Owned;
3725
3918
 
3726
3919
  private:
3920
+ bool shouldTrack(bool isOwner) const;
3921
+
3727
3922
  std::map<void*, VALUE> objectMap_;
3728
3923
  };
3729
3924
  } // namespace Rice4RubyQt6::detail
3730
3925
 
3731
3926
 
3732
-
3733
3927
  // ========= DefaultHandler.hpp =========
3734
3928
 
3735
3929
  namespace Rice4RubyQt6::detail
@@ -3844,571 +4038,620 @@ namespace Rice4RubyQt6::detail
3844
4038
  }
3845
4039
 
3846
4040
 
3847
- // To / From Ruby
3848
4041
 
3849
- // ========= Arg.ipp =========
4042
+ // ========= Buffer.hpp =========
4043
+
3850
4044
  namespace Rice4RubyQt6
3851
4045
  {
3852
- inline Arg::Arg(std::string name) : name(name)
4046
+ template<typename T, typename = void>
4047
+ class Buffer;
4048
+
4049
+ template<typename T>
4050
+ class Buffer<T, std::enable_if_t<!std::is_pointer_v<T> && !std::is_void_v<T>>>
3853
4051
  {
3854
- }
4052
+ public:
4053
+ Buffer(T* pointer);
4054
+ Buffer(T* pointer, size_t size);
4055
+ Buffer(VALUE value);
4056
+ Buffer(VALUE value, size_t size);
3855
4057
 
3856
- template<typename Arg_Type>
3857
- inline Arg& Arg::operator=(Arg_Type val)
3858
- {
3859
- this->defaultValue_ = val;
3860
- return *this;
3861
- }
4058
+ ~Buffer();
3862
4059
 
3863
- //! Check if this Arg has a default value associated with it
3864
- inline bool Arg::hasDefaultValue() const
3865
- {
3866
- return this->defaultValue_.has_value();
3867
- }
4060
+ Buffer(const Buffer& other) = delete;
4061
+ Buffer(Buffer&& other);
3868
4062
 
3869
- //! Return a reference to the default value associated with this Arg
3870
- /*! \return the type saved to this Arg
3871
- */
3872
- template<typename Arg_Type>
3873
- inline Arg_Type Arg::defaultValue()
3874
- {
3875
- return std::any_cast<Arg_Type>(this->defaultValue_);
3876
- }
4063
+ Buffer& operator=(const Buffer& other) = delete;
4064
+ Buffer& operator=(Buffer&& other);
4065
+ T& operator[](size_t index);
3877
4066
 
3878
- inline Arg& Arg::keepAlive()
3879
- {
3880
- this->isKeepAlive_ = true;
3881
- return *this;
3882
- }
4067
+ T* ptr();
4068
+ T& reference();
4069
+ T* release();
3883
4070
 
3884
- inline bool Arg::isKeepAlive() const
3885
- {
3886
- return this->isKeepAlive_;
3887
- }
4071
+ size_t size() const;
3888
4072
 
3889
- inline Arg& Arg::setValue()
3890
- {
3891
- isValue_ = true;
3892
- return *this;
3893
- }
4073
+ // Ruby API
4074
+ VALUE toString() const;
3894
4075
 
3895
- inline bool Arg::isValue() const
3896
- {
3897
- return isValue_;
3898
- }
4076
+ VALUE bytes() const;
4077
+ VALUE bytes(size_t count) const;
3899
4078
 
3900
- inline Arg& Arg::setBlock()
3901
- {
3902
- isBlock_ = true;
3903
- isValue_ = true;
3904
- return *this;
3905
- }
4079
+ Array toArray() const;
4080
+ Array toArray(size_t count) const;
3906
4081
 
3907
- inline bool Arg::isBlock() const
3908
- {
3909
- return isBlock_;
3910
- }
4082
+ bool isOwner() const;
4083
+ void setOwner(bool value);
3911
4084
 
3912
- inline Arg& Arg::setOpaque()
3913
- {
3914
- isOpaque_ = true;
3915
- return *this;
3916
- }
4085
+ private:
4086
+ void fromBuiltinType(VALUE value, size_t size);
4087
+ void fromWrappedType(VALUE value, size_t size);
3917
4088
 
3918
- inline bool Arg::isOpaque() const
3919
- {
3920
- return isOpaque_;
3921
- }
4089
+ bool m_owner = false;
4090
+ size_t m_size = 0;
4091
+ // std::unique_ptr would be great but std::unique_ptr<void> isn't allowed. Mutable is needed to
4092
+ // support const T* buffers
4093
+ mutable T* m_buffer = nullptr;
4094
+ };
3922
4095
 
3923
- inline Arg& Arg::takeOwnership()
4096
+ template<typename T>
4097
+ class Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>
3924
4098
  {
3925
- this->isOwner_ = true;
3926
- return *this;
3927
- }
4099
+ public:
4100
+ Buffer(T** pointer);
4101
+ Buffer(T** pointer, size_t size);
4102
+ Buffer(VALUE value);
4103
+ Buffer(VALUE value, size_t size);
3928
4104
 
3929
- inline bool Arg::isOwner()
3930
- {
3931
- return this->isOwner_;
3932
- }
4105
+ ~Buffer();
3933
4106
 
3934
- inline ArgBuffer::ArgBuffer(std::string name) : Arg(name)
3935
- {
3936
- }
4107
+ Buffer(const Buffer& other) = delete;
4108
+ Buffer(Buffer&& other);
3937
4109
 
4110
+ Buffer& operator=(const Buffer& other) = delete;
4111
+ Buffer& operator=(Buffer&& other);
3938
4112
 
3939
- } // Rice
3940
- // ========= Parameter.ipp =========
3941
- namespace Rice4RubyQt6::detail
3942
- {
3943
- // ----------- ParameterAbstract ----------------
3944
- inline ParameterAbstract::ParameterAbstract(std::unique_ptr<Arg>&& arg) : arg_(std::move(arg))
3945
- {
3946
- }
4113
+ T* operator[](size_t index);
3947
4114
 
3948
- inline ParameterAbstract::ParameterAbstract(const ParameterAbstract& other)
3949
- {
3950
- this->arg_ = std::make_unique<Arg>(*other.arg_);
3951
- }
4115
+ T** ptr();
4116
+ T** release();
3952
4117
 
3953
- inline Arg* ParameterAbstract::arg()
3954
- {
3955
- return this->arg_.get();
3956
- }
4118
+ size_t size() const;
3957
4119
 
3958
- // ----------- Parameter ----------------
3959
- template<typename T>
3960
- inline Parameter<T>::Parameter(std::unique_ptr<Arg>&& arg) : ParameterAbstract(std::move(arg)),
3961
- fromRuby_(this->arg()), toRuby_(this->arg())
3962
- {
3963
- }
4120
+ // Ruby API
4121
+ VALUE toString() const;
4122
+
4123
+ VALUE bytes() const;
4124
+ VALUE bytes(size_t count) const;
4125
+
4126
+ Array toArray() const;
4127
+ Array toArray(size_t count) const;
4128
+
4129
+ void setOwner(bool value);
4130
+ bool isOwner() const;
4131
+
4132
+ private:
4133
+ bool m_owner = false;
4134
+ size_t m_size = 0;
4135
+ T** m_buffer = nullptr;
4136
+ };
3964
4137
 
3965
4138
  template<typename T>
3966
- inline double Parameter<T>::matches(std::optional<VALUE>& valueOpt)
4139
+ class Buffer<T*, std::enable_if_t<detail::is_wrapped_v<T>>>
3967
4140
  {
3968
- if (!valueOpt.has_value())
3969
- {
3970
- return Convertible::None;
3971
- }
3972
- else if (this->arg()->isValue())
3973
- {
3974
- return Convertible::Exact;
3975
- }
4141
+ public:
4142
+ Buffer(T** pointer);
4143
+ Buffer(T** pointer, size_t size);
4144
+ Buffer(VALUE value);
4145
+ Buffer(VALUE value, size_t size);
3976
4146
 
3977
- VALUE value = valueOpt.value();
4147
+ ~Buffer();
3978
4148
 
3979
- // Check with FromRuby if the VALUE is convertible to C++
3980
- double result = this->fromRuby_.is_convertible(value);
4149
+ Buffer(const Buffer& other) = delete;
4150
+ Buffer(Buffer&& other);
3981
4151
 
3982
- // If this is an exact match check if the const-ness of the value and the parameter match.
3983
- // One caveat - procs are also RUBY_T_DATA so don't check if this is a function type
3984
- if (result == Convertible::Exact && rb_type(value) == RUBY_T_DATA && !std::is_function_v<std::remove_pointer_t<T>>)
3985
- {
3986
- bool isConst = WrapperBase::isConst(value);
4152
+ Buffer& operator=(const Buffer& other) = delete;
4153
+ Buffer& operator=(Buffer&& other);
3987
4154
 
3988
- // Do not send a const value to a non-const parameter
3989
- if (isConst && !is_const_any_v<T>)
3990
- {
3991
- result = Convertible::None;
3992
- }
3993
- // It is ok to send a non-const value to a const parameter but
3994
- // prefer non-const to non-const by slightly decreasing the score
3995
- else if (!isConst && is_const_any_v<T>)
3996
- {
3997
- result = Convertible::ConstMismatch;
3998
- }
3999
- }
4155
+ T* operator[](size_t index);
4000
4156
 
4001
- return result;
4002
- }
4157
+ T** ptr();
4158
+ T** release();
4003
4159
 
4004
- #ifdef _MSC_VER
4005
- #pragma warning(push)
4006
- #pragma warning(disable: 4702) // unreachable code
4007
- #endif
4160
+ size_t size() const;
4008
4161
 
4009
- template<typename T>
4010
- inline T Parameter<T>::convertToNative(std::optional<VALUE>& valueOpt)
4011
- {
4012
- /* In general the compiler will convert T to const T, but that does not work for converting
4013
- T** to const T** (see see https://isocpp.org/wiki/faq/const-correctness#constptrptr-conversion)
4014
- which comes up in the OpenCV bindings.
4162
+ // Ruby API
4163
+ VALUE toString() const;
4015
4164
 
4016
- An alternative solution is updating From_Ruby#convert to become a templated function that specifies
4017
- the return type. That works but requires a lot more code changes for this one case and is not
4018
- backwards compatible. */
4165
+ VALUE bytes() const;
4166
+ VALUE bytes(size_t count) const;
4019
4167
 
4020
- if constexpr (is_pointer_pointer_v<T> && !std::is_convertible_v<remove_cv_recursive_t<T>, T>)
4021
- {
4022
- return (T)this->fromRuby_.convert(valueOpt.value());
4023
- }
4024
- else if (valueOpt.has_value())
4025
- {
4026
- return this->fromRuby_.convert(valueOpt.value());
4027
- }
4028
- // Remember std::is_copy_constructible_v<std::vector<std::unique_ptr<T>>>> returns true. Sigh.
4029
- // So special case vector handling
4030
- else if constexpr (detail::is_std_vector_v<detail::intrinsic_type<T>>)
4031
- {
4032
- if constexpr (std::is_copy_constructible_v<typename detail::intrinsic_type<T>::value_type>)
4033
- {
4034
- if (this->arg()->hasDefaultValue())
4035
- {
4036
- return this->arg()->template defaultValue<T>();
4037
- }
4038
- }
4039
- }
4040
- else if constexpr (std::is_copy_constructible_v<T>)
4041
- {
4042
- if (this->arg()->hasDefaultValue())
4043
- {
4044
- return this->arg()->template defaultValue<T>();
4045
- }
4046
- }
4168
+ Array toArray() const;
4169
+ Array toArray(size_t count) const;
4047
4170
 
4048
- // This can be unreachable code
4049
- throw std::invalid_argument("Could not convert Ruby value");
4050
- }
4171
+ void setOwner(bool value);
4172
+ bool isOwner() const;
4051
4173
 
4052
- #ifdef _MSC_VER
4053
- #pragma warning(pop)
4054
- #endif
4174
+ private:
4175
+ bool m_owner = false;
4176
+ size_t m_size = 0;
4177
+ T** m_buffer = nullptr;
4178
+ };
4055
4179
 
4056
4180
  template<typename T>
4057
- inline VALUE Parameter<T>::convertToRuby(T& object)
4181
+ class Buffer<T, std::enable_if_t<std::is_void_v<T>>>
4058
4182
  {
4059
- return this->toRuby_.convert(object);
4060
- }
4183
+ public:
4184
+ Buffer(T* pointer);
4185
+ Buffer(VALUE value);
4186
+ Buffer(VALUE value, size_t size);
4187
+
4188
+ Buffer(const Buffer& other) = delete;
4189
+ Buffer(Buffer&& other);
4190
+
4191
+ Buffer& operator=(const Buffer& other) = delete;
4192
+ Buffer& operator=(Buffer&& other);
4061
4193
 
4194
+ size_t size() const;
4195
+
4196
+ VALUE bytes(size_t count) const;
4197
+ VALUE bytes() const;
4198
+
4199
+ T* ptr();
4200
+ T* release();
4201
+
4202
+ private:
4203
+ bool m_owner = false;
4204
+ size_t m_size = 0;
4205
+ T* m_buffer = nullptr;
4206
+ };
4207
+
4208
+ // Specialization for void* - can't create arrays of void, so this is a minimal wrapper
4062
4209
  template<typename T>
4063
- inline VALUE Parameter<T>::defaultValueRuby()
4210
+ class Buffer<T*, std::enable_if_t<std::is_void_v<T>>>
4064
4211
  {
4065
- if constexpr (std::is_constructible_v<std::remove_cv_t<T>, std::remove_cv_t<std::remove_reference_t<T>>&>)
4066
- {
4067
- // Remember std::is_copy_constructible_v<std::vector<std::unique_ptr<T>>>> returns true. Sigh.
4068
- // So special case vector handling
4069
- if constexpr (detail::is_std_vector_v<detail::intrinsic_type<T>>)
4070
- {
4071
- if constexpr (std::is_copy_constructible_v<typename detail::intrinsic_type<T>::value_type>)
4072
- {
4073
- if (this->arg()->hasDefaultValue())
4074
- {
4075
- T defaultValue = this->arg()->template defaultValue<T>();
4076
- return this->toRuby_.convert(defaultValue);
4077
- }
4078
- }
4079
- }
4080
- else if (this->arg()->hasDefaultValue())
4081
- {
4082
- T defaultValue = this->arg()->template defaultValue<T>();
4083
- return this->toRuby_.convert((remove_cv_recursive_t<T>)defaultValue);
4084
- }
4085
- }
4212
+ public:
4213
+ Buffer(T** pointer);
4214
+ Buffer(T** pointer, size_t size);
4086
4215
 
4087
- throw std::runtime_error("No default value set for parameter " + this->arg()->name);
4088
- }
4216
+ Buffer(const Buffer& other) = delete;
4217
+ Buffer(Buffer&& other);
4218
+
4219
+ Buffer& operator=(const Buffer& other) = delete;
4220
+ Buffer& operator=(Buffer&& other);
4221
+
4222
+ size_t size() const;
4223
+
4224
+ T** ptr();
4225
+ T** release();
4226
+
4227
+ private:
4228
+ bool m_owner = false;
4229
+ size_t m_size = 0;
4230
+ T** m_buffer = nullptr;
4231
+ };
4089
4232
 
4090
4233
  template<typename T>
4091
- inline std::string Parameter<T>::cppTypeName()
4092
- {
4093
- detail::TypeIndexParser typeIndexParser(typeid(T), std::is_fundamental_v<detail::intrinsic_type<T>>);
4094
- return typeIndexParser.simplifiedName();
4095
- }
4234
+ Data_Type<Buffer<T>> define_buffer(std::string klassName = "");
4235
+ }
4236
+
4096
4237
 
4238
+ // ========= Pointer.hpp =========
4239
+
4240
+ namespace Rice4RubyQt6
4241
+ {
4097
4242
  template<typename T>
4098
- inline VALUE Parameter<T>::klass()
4243
+ class Pointer
4099
4244
  {
4100
- TypeMapper<T> typeMapper;
4101
- return typeMapper.rubyKlass();
4102
- }
4245
+ };
4246
+
4247
+ template<typename T>
4248
+ Data_Type<Pointer<T>> define_pointer(std::string klassName = "");
4103
4249
  }
4104
4250
 
4105
- // ========= NoGVL.hpp =========
4251
+
4252
+ // ========= Reference.hpp =========
4106
4253
 
4107
4254
  namespace Rice4RubyQt6
4108
4255
  {
4109
- class NoGVL
4256
+ template<typename T>
4257
+ class Reference
4110
4258
  {
4259
+ static_assert(!detail::is_wrapped_v<detail::intrinsic_type<T>>,
4260
+ "Reference can only be used with fundamental types");
4261
+
4111
4262
  public:
4112
- NoGVL() = default;
4263
+ Reference();
4264
+ Reference(T& data);
4265
+ Reference(VALUE value);
4266
+ T& get();
4267
+
4268
+ private:
4269
+ T data_;
4113
4270
  };
4114
- } // Rice
4115
4271
 
4272
+ // Specialization needed when VALUE type matches T, causing constructor ambiguity
4273
+ // between Reference(T&) and Reference(VALUE). VALUE is unsigned long when
4274
+ // SIZEOF_LONG == SIZEOF_VOIDP (Linux/macOS) and unsigned long long when
4275
+ // SIZEOF_LONG_LONG == SIZEOF_VOIDP (Windows x64).
4276
+ #if SIZEOF_LONG == SIZEOF_VOIDP
4277
+ template<>
4278
+ class Reference<unsigned long>
4279
+ {
4280
+ public:
4281
+ Reference();
4282
+ Reference(unsigned long value, bool isValue = true);
4283
+ unsigned long& get();
4116
4284
 
4117
- // ========= Return.ipp =========
4118
- #include <any>
4285
+ private:
4286
+ unsigned long data_;
4287
+ };
4288
+ #else
4289
+ template<>
4290
+ class Reference<unsigned long long>
4291
+ {
4292
+ public:
4293
+ Reference();
4294
+ Reference(unsigned long long value, bool isValue = true);
4295
+ unsigned long long& get();
4119
4296
 
4297
+ private:
4298
+ unsigned long long data_;
4299
+ };
4300
+ #endif
4301
+
4302
+ template<typename T>
4303
+ Data_Type<Reference<T>> define_reference(std::string klassName = "");
4304
+ }
4305
+
4306
+
4307
+ // To / From Ruby
4308
+
4309
+ // ========= Arg.ipp =========
4120
4310
  namespace Rice4RubyQt6
4121
4311
  {
4122
- inline Return::Return(): Arg("Return")
4312
+ inline Arg::Arg(std::string name) : name(name)
4123
4313
  {
4124
4314
  }
4125
4315
 
4126
- inline Return& Return::keepAlive()
4316
+ template<typename Arg_Type>
4317
+ inline Arg& Arg::operator=(Arg_Type val)
4127
4318
  {
4128
- Arg::keepAlive();
4319
+ this->defaultValue_ = val;
4129
4320
  return *this;
4130
4321
  }
4131
4322
 
4132
- inline Return& Return::setValue()
4323
+ //! Check if this Arg has a default value associated with it
4324
+ inline bool Arg::hasDefaultValue() const
4133
4325
  {
4134
- Arg::setValue();
4135
- return *this;
4326
+ return this->defaultValue_.has_value();
4136
4327
  }
4137
4328
 
4138
- inline Return& Return::setOpaque()
4329
+ //! Return a reference to the default value associated with this Arg
4330
+ /*! \return the type saved to this Arg
4331
+ */
4332
+ template<typename Arg_Type>
4333
+ inline Arg_Type Arg::defaultValue()
4139
4334
  {
4140
- Arg::setOpaque();
4141
- return *this;
4335
+ return std::any_cast<Arg_Type>(this->defaultValue_);
4142
4336
  }
4143
4337
 
4144
- inline Return& Return::takeOwnership()
4338
+ inline Arg& Arg::keepAlive()
4145
4339
  {
4146
- Arg::takeOwnership();
4340
+ this->isKeepAlive_ = true;
4147
4341
  return *this;
4148
4342
  }
4149
- } // Rice
4150
4343
 
4151
- // ========= Constructor.hpp =========
4152
-
4153
- namespace Rice4RubyQt6
4154
- {
4155
- //! Define a Type's Constructor and it's arguments.
4156
- /*! E.g. for the default constructor on a Type:
4157
- \code
4158
- define_class<Test>()
4159
- .define_constructor(Constructor<Test>());
4160
- \endcode
4161
- *
4162
- * The first template argument must be the type being wrapped.
4163
- * Additional arguments must be the types of the parameters sent
4164
- * to the constructor.
4165
- *
4166
- * For more information, see Rice4RubyQt6::Data_Type::define_constructor.
4167
- */
4168
- template<typename T, typename...Parameter_Ts>
4169
- class Constructor;
4170
- }
4171
-
4172
- // ========= Buffer.hpp =========
4173
-
4174
- namespace Rice4RubyQt6
4175
- {
4176
- template<typename T, typename = void>
4177
- class Buffer;
4178
-
4179
- template<typename T>
4180
- class Buffer<T, std::enable_if_t<!std::is_pointer_v<T> && !std::is_void_v<T>>>
4344
+ inline bool Arg::isKeepAlive() const
4181
4345
  {
4182
- public:
4183
- Buffer(T* pointer);
4184
- Buffer(T* pointer, size_t size);
4185
- Buffer(VALUE value);
4186
- Buffer(VALUE value, size_t size);
4346
+ return this->isKeepAlive_;
4347
+ }
4187
4348
 
4188
- ~Buffer();
4349
+ inline Arg& Arg::setValue()
4350
+ {
4351
+ isValue_ = true;
4352
+ return *this;
4353
+ }
4189
4354
 
4190
- Buffer(const Buffer& other) = delete;
4191
- Buffer(Buffer&& other);
4355
+ inline bool Arg::isValue() const
4356
+ {
4357
+ return isValue_;
4358
+ }
4192
4359
 
4193
- Buffer& operator=(const Buffer& other) = delete;
4194
- Buffer& operator=(Buffer&& other);
4195
- T& operator[](size_t index);
4360
+ inline Arg& Arg::setOpaque()
4361
+ {
4362
+ isOpaque_ = true;
4363
+ return *this;
4364
+ }
4196
4365
 
4197
- T* ptr();
4198
- T& reference();
4199
- T* release();
4366
+ inline bool Arg::isOpaque() const
4367
+ {
4368
+ return isOpaque_;
4369
+ }
4200
4370
 
4201
- size_t size() const;
4371
+ inline Arg& Arg::takeOwnership()
4372
+ {
4373
+ this->isOwner_ = true;
4374
+ return *this;
4375
+ }
4202
4376
 
4203
- // Ruby API
4204
- VALUE toString() const;
4377
+ inline bool Arg::isOwner()
4378
+ {
4379
+ return this->isOwner_;
4380
+ }
4205
4381
 
4206
- VALUE bytes() const;
4207
- VALUE bytes(size_t count) const;
4382
+ inline ArgBuffer::ArgBuffer(std::string name) : Arg(name)
4383
+ {
4384
+ }
4208
4385
 
4209
- Array toArray() const;
4210
- Array toArray(size_t count) const;
4211
4386
 
4212
- bool isOwner() const;
4213
- void setOwner(bool value);
4387
+ } // Rice
4388
+ // ========= Parameter.ipp =========
4389
+ namespace Rice4RubyQt6::detail
4390
+ {
4391
+ // ----------- ParameterAbstract ----------------
4392
+ inline ParameterAbstract::ParameterAbstract(std::unique_ptr<Arg>&& arg) : arg_(std::move(arg))
4393
+ {
4394
+ }
4214
4395
 
4215
- private:
4216
- void fromBuiltinType(VALUE value, size_t size);
4217
- void fromWrappedType(VALUE value, size_t size);
4396
+ inline ParameterAbstract::ParameterAbstract(const ParameterAbstract& other)
4397
+ {
4398
+ this->arg_ = std::make_unique<Arg>(*other.arg_);
4399
+ }
4218
4400
 
4219
- bool m_owner = false;
4220
- size_t m_size = 0;
4221
- // std::unique_ptr would be great but std::unique_ptr<void> isn't allowed. Mutable is needed to
4222
- // support const T* buffers
4223
- mutable T* m_buffer = nullptr;
4224
- };
4401
+ inline Arg* ParameterAbstract::arg()
4402
+ {
4403
+ return this->arg_.get();
4404
+ }
4225
4405
 
4406
+ // ----------- Parameter ----------------
4226
4407
  template<typename T>
4227
- class Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>
4408
+ inline Parameter<T>::Parameter(std::unique_ptr<Arg>&& arg) : ParameterAbstract(std::move(arg)),
4409
+ fromRuby_(this->arg()), toRuby_(this->arg())
4228
4410
  {
4229
- public:
4230
- Buffer(T** pointer);
4231
- Buffer(T** pointer, size_t size);
4232
- Buffer(VALUE value);
4233
- Buffer(VALUE value, size_t size);
4234
-
4235
- ~Buffer();
4236
-
4237
- Buffer(const Buffer& other) = delete;
4238
- Buffer(Buffer&& other);
4239
-
4240
- Buffer& operator=(const Buffer& other) = delete;
4241
- Buffer& operator=(Buffer&& other);
4242
-
4243
- T* operator[](size_t index);
4244
-
4245
- T** ptr();
4246
- T** release();
4247
-
4248
- size_t size() const;
4249
-
4250
- // Ruby API
4251
- VALUE toString() const;
4252
-
4253
- VALUE bytes() const;
4254
- VALUE bytes(size_t count) const;
4255
-
4256
- Array toArray() const;
4257
- Array toArray(size_t count) const;
4258
-
4259
- void setOwner(bool value);
4260
- bool isOwner() const;
4261
-
4262
- private:
4263
- bool m_owner = false;
4264
- size_t m_size = 0;
4265
- T** m_buffer = nullptr;
4266
- };
4411
+ }
4267
4412
 
4268
4413
  template<typename T>
4269
- class Buffer<T*, std::enable_if_t<detail::is_wrapped_v<T>>>
4414
+ inline double Parameter<T>::matches(std::optional<VALUE>& valueOpt)
4270
4415
  {
4271
- public:
4272
- Buffer(T** pointer);
4273
- Buffer(T** pointer, size_t size);
4274
- Buffer(VALUE value);
4275
- Buffer(VALUE value, size_t size);
4416
+ if (!valueOpt.has_value())
4417
+ {
4418
+ return Convertible::None;
4419
+ }
4420
+ else if (this->arg()->isValue())
4421
+ {
4422
+ return Convertible::Exact;
4423
+ }
4276
4424
 
4277
- ~Buffer();
4425
+ VALUE value = valueOpt.value();
4278
4426
 
4279
- Buffer(const Buffer& other) = delete;
4280
- Buffer(Buffer&& other);
4427
+ // Check with FromRuby if the VALUE is convertible to C++
4428
+ double result = this->fromRuby_.is_convertible(value);
4281
4429
 
4282
- Buffer& operator=(const Buffer& other) = delete;
4283
- Buffer& operator=(Buffer&& other);
4430
+ // TODO this is ugly and hacky and probably doesn't belong here.
4431
+ // Some Ruby objects like Proc and Set (in Ruby 4+) are also RUBY_T_DATA so we have to check for them
4432
+ if (result == Convertible::Exact && rb_type(value) == RUBY_T_DATA)
4433
+ {
4434
+ bool isBuffer = dynamic_cast<ArgBuffer*>(this->arg()) ? true : false;
4435
+ if ((!isBuffer && Data_Type<detail::intrinsic_type<T>>::is_descendant(value)) ||
4436
+ (isBuffer && Data_Type<Pointer<detail::intrinsic_type<T>>>::is_descendant(value)))
4437
+ {
4438
+ bool isConst = WrapperBase::isConst(value);
4284
4439
 
4285
- T* operator[](size_t index);
4440
+ // Do not send a const value to a non-const parameter
4441
+ if (isConst && !is_const_any_v<T>)
4442
+ {
4443
+ result = Convertible::None;
4444
+ }
4445
+ // It is ok to send a non-const value to a const parameter but
4446
+ // prefer non-const to non-const by slightly decreasing the score
4447
+ else if (!isConst && is_const_any_v<T>)
4448
+ {
4449
+ result = Convertible::ConstMismatch;
4450
+ }
4451
+ }
4452
+ }
4286
4453
 
4287
- T** ptr();
4288
- T** release();
4454
+ return result;
4455
+ }
4289
4456
 
4290
- size_t size() const;
4457
+ #ifdef _MSC_VER
4458
+ #pragma warning(push)
4459
+ #pragma warning(disable: 4702) // unreachable code
4460
+ #endif
4291
4461
 
4292
- // Ruby API
4293
- VALUE toString() const;
4462
+ template<typename T>
4463
+ inline T Parameter<T>::convertToNative(std::optional<VALUE>& valueOpt)
4464
+ {
4465
+ /* In general the compiler will convert T to const T, but that does not work for converting
4466
+ T** to const T** (see see https://isocpp.org/wiki/faq/const-correctness#constptrptr-conversion)
4467
+ which comes up in the OpenCV bindings.
4294
4468
 
4295
- VALUE bytes() const;
4296
- VALUE bytes(size_t count) const;
4469
+ An alternative solution is updating From_Ruby#convert to become a templated function that specifies
4470
+ the return type. That works but requires a lot more code changes for this one case and is not
4471
+ backwards compatible. */
4297
4472
 
4298
- Array toArray() const;
4299
- Array toArray(size_t count) const;
4473
+ if constexpr (is_pointer_pointer_v<T> && !std::is_convertible_v<remove_cv_recursive_t<T>, T>)
4474
+ {
4475
+ return (T)this->fromRuby_.convert(valueOpt.value());
4476
+ }
4477
+ else if (valueOpt.has_value())
4478
+ {
4479
+ return this->fromRuby_.convert(valueOpt.value());
4480
+ }
4481
+ // Remember std::is_copy_constructible_v<std::vector<std::unique_ptr<T>>>> returns true. Sigh.
4482
+ // So special case vector handling
4483
+ else if constexpr (detail::is_std_vector_v<detail::intrinsic_type<T>>)
4484
+ {
4485
+ if constexpr (std::is_copy_constructible_v<typename detail::intrinsic_type<T>::value_type>)
4486
+ {
4487
+ if (this->arg()->hasDefaultValue())
4488
+ {
4489
+ return this->arg()->template defaultValue<T>();
4490
+ }
4491
+ }
4492
+ }
4493
+ // Incomplete types can't have default values (std::any requires complete types)
4494
+ else if constexpr (!is_complete_v<intrinsic_type<T>>)
4495
+ {
4496
+ // No default value possible for incomplete types
4497
+ }
4498
+ else if constexpr (std::is_copy_constructible_v<T>)
4499
+ {
4500
+ if (this->arg()->hasDefaultValue())
4501
+ {
4502
+ return this->arg()->template defaultValue<T>();
4503
+ }
4504
+ }
4300
4505
 
4301
- void setOwner(bool value);
4302
- bool isOwner() const;
4506
+ // This can be unreachable code
4507
+ throw std::invalid_argument("Could not convert Ruby value");
4508
+ }
4303
4509
 
4304
- private:
4305
- bool m_owner = false;
4306
- size_t m_size = 0;
4307
- T** m_buffer = nullptr;
4308
- };
4510
+ #ifdef _MSC_VER
4511
+ #pragma warning(pop)
4512
+ #endif
4309
4513
 
4310
4514
  template<typename T>
4311
- class Buffer<T, std::enable_if_t<std::is_void_v<T>>>
4515
+ inline VALUE Parameter<T>::convertToRuby(T& object)
4312
4516
  {
4313
- public:
4314
- Buffer(T* pointer);
4315
- Buffer(VALUE value);
4316
- Buffer(VALUE value, size_t size);
4317
-
4318
- Buffer(const Buffer& other) = delete;
4319
- Buffer(Buffer&& other);
4517
+ return this->toRuby_.convert(object);
4518
+ }
4320
4519
 
4321
- Buffer& operator=(const Buffer& other) = delete;
4322
- Buffer& operator=(Buffer&& other);
4520
+ template<typename T>
4521
+ inline VALUE Parameter<T>::defaultValueRuby()
4522
+ {
4523
+ using To_Ruby_T = remove_cv_recursive_t<T>;
4323
4524
 
4324
- size_t size() const;
4325
-
4326
- VALUE bytes(size_t count) const;
4327
- VALUE bytes() const;
4525
+ if (!this->arg()->hasDefaultValue())
4526
+ {
4527
+ throw std::runtime_error("No default value set for " + this->arg()->name);
4528
+ }
4328
4529
 
4329
- T* ptr();
4330
- T* release();
4530
+ if constexpr (!is_complete_v<intrinsic_type<T>>)
4531
+ {
4532
+ // Only pointers to incomplete types can have
4533
+ // default values (e.g., void* or Impl*), since the pointer itself is complete.
4534
+ // References and values of incomplete types cannot be stored in std::any.
4535
+ if constexpr (std::is_pointer_v<std::remove_reference_t<T>>)
4536
+ {
4537
+ T defaultValue = this->arg()->template defaultValue<T>();
4538
+ return this->toRuby_.convert((To_Ruby_T)defaultValue);
4539
+ }
4540
+ else
4541
+ {
4542
+ throw std::runtime_error("Default value not allowed for incomple type. Parameter " + this->arg()->name);
4543
+ }
4544
+ }
4545
+ else if constexpr (std::is_constructible_v<std::remove_cv_t<T>, std::remove_cv_t<std::remove_reference_t<T>>&>)
4546
+ {
4547
+ // Remember std::is_copy_constructible_v<std::vector<std::unique_ptr<T>>>> returns true. Sigh.
4548
+ // So special case vector handling
4549
+ if constexpr (detail::is_std_vector_v<detail::intrinsic_type<T>>)
4550
+ {
4551
+ if constexpr (std::is_copy_constructible_v<typename detail::intrinsic_type<T>::value_type>)
4552
+ {
4553
+ T defaultValue = this->arg()->template defaultValue<T>();
4554
+ return this->toRuby_.convert(defaultValue);
4555
+ }
4556
+ else
4557
+ {
4558
+ throw std::runtime_error("Default value not allowed for parameter " + this->arg()->name);
4559
+ }
4560
+ }
4561
+ else
4562
+ {
4563
+ T defaultValue = this->arg()->template defaultValue<T>();
4564
+ return this->toRuby_.convert((To_Ruby_T)defaultValue);
4565
+ }
4566
+ }
4567
+ else
4568
+ {
4569
+ throw std::runtime_error("Default value not allowed for parameter " + this->arg()->name);
4570
+ }
4571
+ }
4331
4572
 
4332
- private:
4333
- bool m_owner = false;
4334
- size_t m_size = 0;
4335
- T* m_buffer = nullptr;
4336
- };
4573
+ template<typename T>
4574
+ inline std::string Parameter<T>::cppTypeName()
4575
+ {
4576
+ detail::TypeDetail<T> typeDetail;
4577
+ return typeDetail.simplifiedName();
4578
+ }
4337
4579
 
4338
4580
  template<typename T>
4339
- Data_Type<Buffer<T>> define_buffer(std::string klassName = "");
4581
+ inline VALUE Parameter<T>::klass()
4582
+ {
4583
+ TypeDetail<T> typeDetail;
4584
+ return typeDetail.rubyKlass();
4585
+ }
4340
4586
  }
4341
4587
 
4342
-
4343
- // ========= Pointer.hpp =========
4588
+ // ========= NoGVL.hpp =========
4344
4589
 
4345
4590
  namespace Rice4RubyQt6
4346
4591
  {
4347
- template<typename T>
4348
- class Pointer
4592
+ class NoGVL
4349
4593
  {
4594
+ public:
4595
+ NoGVL() = default;
4350
4596
  };
4351
-
4352
- template<typename T>
4353
- Data_Type<Pointer<T>> define_pointer(std::string klassName = "");
4354
- }
4597
+ } // Rice
4355
4598
 
4356
4599
 
4357
- // ========= Reference.hpp =========
4600
+ // ========= Return.ipp =========
4601
+ #include <any>
4358
4602
 
4359
4603
  namespace Rice4RubyQt6
4360
4604
  {
4361
- template<typename T>
4362
- class Reference
4605
+ inline Return::Return(): Arg("Return")
4363
4606
  {
4364
- static_assert(!detail::is_wrapped_v<detail::intrinsic_type<T>>,
4365
- "Reference can only be used with fundamental types");
4607
+ }
4366
4608
 
4367
- public:
4368
- Reference();
4369
- Reference(T& data);
4370
- Reference(VALUE value);
4371
- T& get();
4609
+ inline Return& Return::keepAlive()
4610
+ {
4611
+ Arg::keepAlive();
4612
+ return *this;
4613
+ }
4372
4614
 
4373
- private:
4374
- T data_;
4375
- };
4615
+ inline Return& Return::setValue()
4616
+ {
4617
+ Arg::setValue();
4618
+ return *this;
4619
+ }
4376
4620
 
4377
- // Specialization needed when VALUE type matches T, causing constructor ambiguity
4378
- // between Reference(T&) and Reference(VALUE). VALUE is unsigned long when
4379
- // SIZEOF_LONG == SIZEOF_VOIDP (Linux/macOS) and unsigned long long when
4380
- // SIZEOF_LONG_LONG == SIZEOF_VOIDP (Windows x64).
4381
- #if SIZEOF_LONG == SIZEOF_VOIDP
4382
- template<>
4383
- class Reference<unsigned long>
4621
+ inline Return& Return::setOpaque()
4384
4622
  {
4385
- public:
4386
- Reference();
4387
- Reference(unsigned long value, bool isValue = true);
4388
- unsigned long& get();
4623
+ Arg::setOpaque();
4624
+ return *this;
4625
+ }
4389
4626
 
4390
- private:
4391
- unsigned long data_;
4392
- };
4393
- #else
4394
- template<>
4395
- class Reference<unsigned long long>
4627
+ inline Return& Return::takeOwnership()
4396
4628
  {
4397
- public:
4398
- Reference();
4399
- Reference(unsigned long long value, bool isValue = true);
4400
- unsigned long long& get();
4629
+ Arg::takeOwnership();
4630
+ return *this;
4631
+ }
4632
+ } // Rice
4401
4633
 
4402
- private:
4403
- unsigned long long data_;
4404
- };
4405
- #endif
4634
+ // ========= Constructor.hpp =========
4406
4635
 
4407
- template<typename T>
4408
- Data_Type<Reference<T>> define_reference(std::string klassName = "");
4636
+ namespace Rice4RubyQt6
4637
+ {
4638
+ //! Define a Type's Constructor and it's arguments.
4639
+ /*! E.g. for the default constructor on a Type:
4640
+ \code
4641
+ define_class<Test>()
4642
+ .define_constructor(Constructor<Test>());
4643
+ \endcode
4644
+ *
4645
+ * The first template argument must be the type being wrapped.
4646
+ * Additional arguments must be the types of the parameters sent
4647
+ * to the constructor.
4648
+ *
4649
+ * For more information, see Rice4RubyQt6::Data_Type::define_constructor.
4650
+ */
4651
+ template<typename T, typename...Parameter_Ts>
4652
+ class Constructor;
4409
4653
  }
4410
4654
 
4411
-
4412
4655
  // ========= Buffer.ipp =========
4413
4656
  namespace Rice4RubyQt6
4414
4657
  {
@@ -4527,8 +4770,8 @@ namespace Rice4RubyQt6
4527
4770
  }
4528
4771
  else
4529
4772
  {
4530
- detail::TypeIndexParser typeIndexParser(typeid(T), std::is_fundamental_v<detail::intrinsic_type<T>>);
4531
- std::string typeName = typeIndexParser.name();
4773
+ detail::TypeDetail<T> typeDetail;
4774
+ std::string typeName = typeDetail.name();
4532
4775
  throw Exception(rb_eTypeError, "wrong argument type %s (expected %s*)",
4533
4776
  detail::protect(rb_obj_classname, value), typeName.c_str());
4534
4777
  }
@@ -4567,9 +4810,9 @@ namespace Rice4RubyQt6
4567
4810
  }
4568
4811
  else
4569
4812
  {
4570
- detail::TypeIndexParser typeIndexParser(typeid(Intrinsic_T), std::is_fundamental_v<Intrinsic_T>);
4813
+ detail::TypeDetail<Intrinsic_T> typeDetail;
4571
4814
  throw Exception(rb_eTypeError, "Cannot construct object of type %s - type is not move or copy constructible",
4572
- typeIndexParser.name().c_str());
4815
+ typeDetail.name().c_str());
4573
4816
  }
4574
4817
  }
4575
4818
  break;
@@ -4609,8 +4852,8 @@ namespace Rice4RubyQt6
4609
4852
  }
4610
4853
  default:
4611
4854
  {
4612
- detail::TypeIndexParser typeIndexParser(typeid(T), std::is_fundamental_v<detail::intrinsic_type<T>>);
4613
- std::string typeName = typeIndexParser.name();
4855
+ detail::TypeDetail<T> typeDetail;
4856
+ std::string typeName = typeDetail.name();
4614
4857
  throw Exception(rb_eTypeError, "wrong argument type %s (expected %s*)",
4615
4858
  detail::protect(rb_obj_classname, value), typeName.c_str());
4616
4859
  }
@@ -4680,8 +4923,8 @@ namespace Rice4RubyQt6
4680
4923
  template<typename T>
4681
4924
  inline VALUE Buffer<T, std::enable_if_t<!std::is_pointer_v<T> && !std::is_void_v<T>>>::toString() const
4682
4925
  {
4683
- detail::TypeIndexParser typeIndexParser(typeid(T*), std::is_fundamental_v<detail::intrinsic_type<T>>);
4684
- std::string description = "Buffer<type: " + typeIndexParser.simplifiedName() + ", size: " + std::to_string(this->m_size) + ">";
4926
+ detail::TypeDetail<T*> typeDetail;
4927
+ std::string description = "Buffer<type: " + typeDetail.simplifiedName() + ", size: " + std::to_string(this->m_size) + ">";
4685
4928
 
4686
4929
  // We can't use To_Ruby because To_Ruby depends on Buffer - ie a circular reference
4687
4930
  return detail::protect(rb_utf8_str_new_cstr, description.c_str());
@@ -4753,22 +4996,22 @@ namespace Rice4RubyQt6
4753
4996
 
4754
4997
  // ---- Buffer<T*> - Builtin -------
4755
4998
  template<typename T>
4756
- inline Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::Buffer(T** pointer) : m_buffer(pointer)
4999
+ inline Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::Buffer(T** pointer) : m_buffer(pointer)
4757
5000
  {
4758
5001
  }
4759
5002
 
4760
5003
  template<typename T>
4761
- inline Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::Buffer(T** pointer, size_t size) : m_size(size), m_buffer(pointer)
5004
+ inline Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::Buffer(T** pointer, size_t size) : m_size(size), m_buffer(pointer)
4762
5005
  {
4763
5006
  }
4764
5007
 
4765
5008
  template <typename T>
4766
- inline Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::Buffer(VALUE value) : Buffer(value, 0)
5009
+ inline Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::Buffer(VALUE value) : Buffer(value, 0)
4767
5010
  {
4768
5011
  }
4769
5012
 
4770
5013
  template <typename T>
4771
- inline Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::Buffer(VALUE value, size_t size)
5014
+ inline Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::Buffer(VALUE value, size_t size)
4772
5015
  {
4773
5016
  using Intrinsic_T = typename detail::intrinsic_type<T>;
4774
5017
 
@@ -4803,7 +5046,7 @@ namespace Rice4RubyQt6
4803
5046
  if (inner.size() != 1)
4804
5047
  {
4805
5048
  throw Exception(rb_eTypeError, "Expected inner array size 1 for type %s* but got %ld",
4806
- detail::TypeIndexParser(typeid(T)).name().c_str(), inner.size());
5049
+ detail::TypeDetail<T>().name().c_str(), inner.size());
4807
5050
  }
4808
5051
  this->m_buffer[i] = fromRuby.convert(inner[0].value());
4809
5052
  }
@@ -4825,8 +5068,8 @@ namespace Rice4RubyQt6
4825
5068
  }
4826
5069
  default:
4827
5070
  {
4828
- detail::TypeIndexParser typeIndexParser(typeid(T), std::is_fundamental_v<detail::intrinsic_type<T>>);
4829
- std::string typeName = typeIndexParser.name();
5071
+ detail::TypeDetail<T> typeDetail;
5072
+ std::string typeName = typeDetail.name();
4830
5073
  throw Exception(rb_eTypeError, "wrong argument type %s (expected %s*)",
4831
5074
  detail::protect(rb_obj_classname, value), typeName.c_str());
4832
5075
  }
@@ -4834,7 +5077,7 @@ namespace Rice4RubyQt6
4834
5077
  }
4835
5078
 
4836
5079
  template <typename T>
4837
- inline Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::~Buffer()
5080
+ inline Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::~Buffer()
4838
5081
  {
4839
5082
  if (this->m_owner)
4840
5083
  {
@@ -4848,7 +5091,7 @@ namespace Rice4RubyQt6
4848
5091
  }
4849
5092
 
4850
5093
  template <typename T>
4851
- inline Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::Buffer(Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>&& other) : m_owner(other.m_owner), m_size(other.m_size),
5094
+ inline Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::Buffer(Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>&& other) : m_owner(other.m_owner), m_size(other.m_size),
4852
5095
  m_buffer(other.m_buffer)
4853
5096
  {
4854
5097
  other.m_buffer = nullptr;
@@ -4857,7 +5100,7 @@ namespace Rice4RubyQt6
4857
5100
  }
4858
5101
 
4859
5102
  template <typename T>
4860
- inline Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>& Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::operator=(Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>&& other)
5103
+ inline Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>& Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::operator=(Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>&& other)
4861
5104
  {
4862
5105
  this->m_buffer = other.m_buffer;
4863
5106
  other.m_buffer = nullptr;
@@ -4872,54 +5115,54 @@ namespace Rice4RubyQt6
4872
5115
  }
4873
5116
 
4874
5117
  template <typename T>
4875
- inline T* Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::operator[](size_t index)
5118
+ inline T* Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::operator[](size_t index)
4876
5119
  {
4877
5120
  return this->m_buffer[index];
4878
5121
  }
4879
5122
 
4880
5123
  template <typename T>
4881
- inline size_t Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::size() const
5124
+ inline size_t Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::size() const
4882
5125
  {
4883
5126
  return this->m_size;
4884
5127
  }
4885
5128
 
4886
5129
  template <typename T>
4887
- inline T** Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::ptr()
5130
+ inline T** Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::ptr()
4888
5131
  {
4889
5132
  return this->m_buffer;
4890
5133
  }
4891
5134
 
4892
5135
  template <typename T>
4893
- inline T** Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::release()
5136
+ inline T** Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::release()
4894
5137
  {
4895
5138
  this->m_owner = false;
4896
5139
  return this->m_buffer;
4897
5140
  }
4898
5141
 
4899
5142
  template <typename T>
4900
- inline bool Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::isOwner() const
5143
+ inline bool Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::isOwner() const
4901
5144
  {
4902
5145
  return this->m_owner;
4903
5146
  }
4904
5147
 
4905
5148
  template <typename T>
4906
- inline void Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::setOwner(bool value)
5149
+ inline void Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::setOwner(bool value)
4907
5150
  {
4908
5151
  this->m_owner = value;
4909
5152
  }
4910
5153
 
4911
5154
  template<typename T>
4912
- inline VALUE Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::toString() const
5155
+ inline VALUE Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::toString() const
4913
5156
  {
4914
- detail::TypeIndexParser typeIndexParser(typeid(T*), std::is_fundamental_v<detail::intrinsic_type<T>>);
4915
- std::string description = "Buffer<type: " + typeIndexParser.simplifiedName() + ", size: " + std::to_string(this->m_size) + ">";
5157
+ detail::TypeDetail<T*> typeDetail;
5158
+ std::string description = "Buffer<type: " + typeDetail.simplifiedName() + ", size: " + std::to_string(this->m_size) + ">";
4916
5159
 
4917
5160
  // We can't use To_Ruby because To_Ruby depends on Buffer - ie a circular reference
4918
5161
  return detail::protect(rb_utf8_str_new_cstr, description.c_str());
4919
5162
  }
4920
5163
 
4921
5164
  template<typename T>
4922
- inline VALUE Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::bytes(size_t count) const
5165
+ inline VALUE Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::bytes(size_t count) const
4923
5166
  {
4924
5167
  if (!this->m_buffer)
4925
5168
  {
@@ -4934,13 +5177,13 @@ namespace Rice4RubyQt6
4934
5177
  }
4935
5178
 
4936
5179
  template<typename T>
4937
- inline VALUE Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::bytes() const
5180
+ inline VALUE Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::bytes() const
4938
5181
  {
4939
5182
  return this->bytes(this->m_size);
4940
5183
  }
4941
5184
 
4942
5185
  template<typename T>
4943
- inline Array Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::toArray(size_t count) const
5186
+ inline Array Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::toArray(size_t count) const
4944
5187
  {
4945
5188
  if (!this->m_buffer)
4946
5189
  {
@@ -4963,7 +5206,7 @@ namespace Rice4RubyQt6
4963
5206
  }
4964
5207
 
4965
5208
  template<typename T>
4966
- inline Array Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T>>>::toArray() const
5209
+ inline Array Buffer<T*, std::enable_if_t<!detail::is_wrapped_v<T> && !std::is_void_v<T>>>::toArray() const
4967
5210
  {
4968
5211
  return this->toArray(this->m_size);
4969
5212
  }
@@ -5018,8 +5261,8 @@ namespace Rice4RubyQt6
5018
5261
  }
5019
5262
  default:
5020
5263
  {
5021
- detail::TypeIndexParser typeIndexParser(typeid(T), std::is_fundamental_v<detail::intrinsic_type<T>>);
5022
- std::string typeName = typeIndexParser.name();
5264
+ detail::TypeDetail<T> typeDetail;
5265
+ std::string typeName = typeDetail.name();
5023
5266
  throw Exception(rb_eTypeError, "wrong argument type %s (expected %s*)",
5024
5267
  detail::protect(rb_obj_classname, value), typeName.c_str());
5025
5268
  }
@@ -5103,8 +5346,8 @@ namespace Rice4RubyQt6
5103
5346
  template<typename T>
5104
5347
  inline VALUE Buffer<T*, std::enable_if_t<detail::is_wrapped_v<T>>>::toString() const
5105
5348
  {
5106
- detail::TypeIndexParser typeIndexParser(typeid(T*), std::is_fundamental_v<detail::intrinsic_type<T>>);
5107
- std::string description = "Buffer<type: " + typeIndexParser.simplifiedName() + ", size: " + std::to_string(this->m_size) + ">";
5349
+ detail::TypeDetail<T*> typeDetail;
5350
+ std::string description = "Buffer<type: " + typeDetail.simplifiedName() + ", size: " + std::to_string(this->m_size) + ">";
5108
5351
 
5109
5352
  // We can't use To_Ruby because To_Ruby depends on Buffer - ie a circular reference
5110
5353
  return detail::protect(rb_utf8_str_new_cstr, description.c_str());
@@ -5183,8 +5426,8 @@ namespace Rice4RubyQt6
5183
5426
  }
5184
5427
  default:
5185
5428
  {
5186
- detail::TypeIndexParser typeIndexParser(typeid(T), std::is_fundamental_v<detail::intrinsic_type<T>>);
5187
- std::string typeName = typeIndexParser.name();
5429
+ detail::TypeDetail<T> typeDetail;
5430
+ std::string typeName = typeDetail.name();
5188
5431
  throw Exception(rb_eTypeError, "wrong argument type %s (expected %s*)",
5189
5432
  detail::protect(rb_obj_classname, value), typeName.c_str());
5190
5433
  }
@@ -5251,6 +5494,57 @@ namespace Rice4RubyQt6
5251
5494
  return this->bytes(this->m_size);
5252
5495
  }
5253
5496
 
5497
+ // ---- Buffer<void*> -------
5498
+ template<typename T>
5499
+ inline Buffer<T*, std::enable_if_t<std::is_void_v<T>>>::Buffer(T** pointer) : m_buffer(pointer)
5500
+ {
5501
+ }
5502
+
5503
+ template<typename T>
5504
+ inline Buffer<T*, std::enable_if_t<std::is_void_v<T>>>::Buffer(T** pointer, size_t size) : m_size(size), m_buffer(pointer)
5505
+ {
5506
+ }
5507
+
5508
+ template<typename T>
5509
+ inline Buffer<T*, std::enable_if_t<std::is_void_v<T>>>::Buffer(Buffer<T*, std::enable_if_t<std::is_void_v<T>>>&& other) : m_owner(other.m_owner), m_size(other.m_size), m_buffer(other.m_buffer)
5510
+ {
5511
+ other.m_buffer = nullptr;
5512
+ other.m_size = 0;
5513
+ other.m_owner = false;
5514
+ }
5515
+
5516
+ template<typename T>
5517
+ inline Buffer<T*, std::enable_if_t<std::is_void_v<T>>>& Buffer<T*, std::enable_if_t<std::is_void_v<T>>>::operator=(Buffer<T*, std::enable_if_t<std::is_void_v<T>>>&& other)
5518
+ {
5519
+ this->m_buffer = other.m_buffer;
5520
+ this->m_size = other.m_size;
5521
+ this->m_owner = other.m_owner;
5522
+ other.m_buffer = nullptr;
5523
+ other.m_size = 0;
5524
+ other.m_owner = false;
5525
+
5526
+ return *this;
5527
+ }
5528
+
5529
+ template<typename T>
5530
+ inline size_t Buffer<T*, std::enable_if_t<std::is_void_v<T>>>::size() const
5531
+ {
5532
+ return this->m_size;
5533
+ }
5534
+
5535
+ template<typename T>
5536
+ inline T** Buffer<T*, std::enable_if_t<std::is_void_v<T>>>::ptr()
5537
+ {
5538
+ return this->m_buffer;
5539
+ }
5540
+
5541
+ template <typename T>
5542
+ inline T** Buffer<T*, std::enable_if_t<std::is_void_v<T>>>::release()
5543
+ {
5544
+ this->m_owner = false;
5545
+ return this->m_buffer;
5546
+ }
5547
+
5254
5548
  // ------ define_buffer ----------
5255
5549
  template<typename T>
5256
5550
  inline Data_Type<Buffer<T>> define_buffer(std::string klassName)
@@ -5260,8 +5554,8 @@ namespace Rice4RubyQt6
5260
5554
 
5261
5555
  if (klassName.empty())
5262
5556
  {
5263
- detail::TypeMapper<Buffer_T> typeMapper;
5264
- klassName = typeMapper.rubyName();
5557
+ detail::TypeDetail<Buffer_T> typeDetail;
5558
+ klassName = typeDetail.rubyName();
5265
5559
  }
5266
5560
 
5267
5561
  Module rb_mRice = define_module("Rice4RubyQt6");
@@ -5282,6 +5576,14 @@ namespace Rice4RubyQt6
5282
5576
  define_method("data", &Buffer_T::ptr, ReturnBuffer()).
5283
5577
  define_method("release", &Buffer_T::release, ReturnBuffer());
5284
5578
  }
5579
+ // void* - minimal wrapper, no Ruby array conversion support
5580
+ else if constexpr (std::is_void_v<detail::intrinsic_type<T>>)
5581
+ {
5582
+ return define_class_under<Buffer_T>(rb_mRice, klassName).
5583
+ define_method("size", &Buffer_T::size).
5584
+ define_method("data", &Buffer_T::ptr, ReturnBuffer()).
5585
+ define_method("release", &Buffer_T::release, ReturnBuffer());
5586
+ }
5285
5587
  else
5286
5588
  {
5287
5589
  Data_Type<Buffer_T> klass = define_class_under<Buffer_T>(rb_mRice, klassName).
@@ -5297,19 +5599,22 @@ namespace Rice4RubyQt6
5297
5599
  define_method("data", &Buffer_T::ptr, ReturnBuffer()).
5298
5600
  define_method("release", &Buffer_T::release, ReturnBuffer());
5299
5601
 
5300
- if constexpr (!std::is_pointer_v<T> && !std::is_void_v<T> && !std::is_const_v<T> && std::is_copy_assignable_v<T>)
5602
+ if constexpr (detail::is_complete_v<detail::intrinsic_type<T>>)
5301
5603
  {
5302
- klass.define_method("[]=", [](Buffer_T& self, size_t index, T& value) -> void
5604
+ if constexpr (!std::is_pointer_v<T> && !std::is_void_v<T> && !std::is_const_v<T> && std::is_copy_assignable_v<T>)
5303
5605
  {
5304
- self[index] = value;
5305
- });
5306
- }
5307
- else if constexpr (std::is_pointer_v<T> && !std::is_const_v<std::remove_pointer_t<T>> && std::is_copy_assignable_v<std::remove_pointer_t<T>>)
5308
- {
5309
- klass.define_method("[]=", [](Buffer_T& self, size_t index, T value) -> void
5606
+ klass.define_method("[]=", [](Buffer_T& self, size_t index, T& value) -> void
5607
+ {
5608
+ self[index] = value;
5609
+ });
5610
+ }
5611
+ else if constexpr (std::is_pointer_v<T> && !std::is_const_v<std::remove_pointer_t<T>> && std::is_copy_assignable_v<std::remove_pointer_t<T>>)
5310
5612
  {
5311
- *self[index] = *value;
5312
- });
5613
+ klass.define_method("[]=", [](Buffer_T& self, size_t index, T value) -> void
5614
+ {
5615
+ *self[index] = *value;
5616
+ });
5617
+ }
5313
5618
  }
5314
5619
 
5315
5620
  return klass;
@@ -5342,8 +5647,8 @@ namespace Rice4RubyQt6
5342
5647
 
5343
5648
  if (klassName.empty())
5344
5649
  {
5345
- detail::TypeMapper<Pointer_T> typeMapper;
5346
- klassName = typeMapper.rubyName();
5650
+ detail::TypeDetail<Pointer_T> typeDetail;
5651
+ klassName = typeDetail.rubyName();
5347
5652
  }
5348
5653
 
5349
5654
  Module rb_mRice = define_module("Rice4RubyQt6");
@@ -5789,6 +6094,35 @@ namespace Rice4RubyQt6::detail
5789
6094
  }
5790
6095
  };
5791
6096
 
6097
+ template<>
6098
+ struct Type<long double>
6099
+ {
6100
+ static bool verify()
6101
+ {
6102
+ return true;
6103
+ }
6104
+
6105
+ static VALUE rubyKlass()
6106
+ {
6107
+ return rb_cFloat;
6108
+ }
6109
+ };
6110
+
6111
+ template<int N>
6112
+ struct Type<long double[N]>
6113
+ {
6114
+ static bool verify()
6115
+ {
6116
+ define_buffer<long double>();
6117
+ return true;
6118
+ }
6119
+
6120
+ static VALUE rubyKlass()
6121
+ {
6122
+ return rb_cString;
6123
+ }
6124
+ };
6125
+
5792
6126
  template<>
5793
6127
  struct Type<void>
5794
6128
  {
@@ -6087,16 +6421,18 @@ namespace Rice4RubyQt6
6087
6421
  }
6088
6422
  else if (this->arg_ && this->arg_->isOwner())
6089
6423
  {
6090
- // This copies the buffer but does not free it. So Ruby is not really
6091
- // taking ownership of it. But there isn't a Ruby API for creating a string
6092
- // from an existing buffer and later freeing it.
6093
- return protect(rb_usascii_str_new_cstr, data);
6424
+ // This copies the buffer but does not free it
6425
+ VALUE result = protect(rb_usascii_str_new_cstr, data);
6426
+ // And free the char* since we were told to take "ownership"
6427
+ // TODO - is this a good idea?
6428
+ //free(data);
6429
+ return result;
6094
6430
  }
6095
6431
  else
6096
6432
  {
6097
6433
  // Does NOT copy the passed in buffer and does NOT free it when the string is GCed
6098
- long size = (long)strlen(data);
6099
- VALUE result = protect(rb_str_new_static, data, size);
6434
+ long len = (long)strlen(data);
6435
+ VALUE result = protect(rb_str_new_static, data, len);
6100
6436
  // Freeze the object so Ruby can't modify the C string
6101
6437
  return rb_obj_freeze(result);
6102
6438
  }
@@ -6286,7 +6622,64 @@ namespace Rice4RubyQt6
6286
6622
  {
6287
6623
  }
6288
6624
 
6289
- VALUE convert(const double& native)
6625
+ VALUE convert(const double& native)
6626
+ {
6627
+ return protect(rb_float_new, native);
6628
+ }
6629
+
6630
+ private:
6631
+ Arg* arg_ = nullptr;
6632
+ };
6633
+
6634
+ template<int N>
6635
+ class To_Ruby<double[N]>
6636
+ {
6637
+ public:
6638
+ To_Ruby() = default;
6639
+
6640
+ explicit To_Ruby(Arg* arg) : arg_(arg)
6641
+ {
6642
+ }
6643
+
6644
+ VALUE convert(double data[N])
6645
+ {
6646
+ Buffer<double> buffer(data, N);
6647
+ Data_Object<Buffer<double>> dataObject(std::move(buffer));
6648
+ return dataObject.value();
6649
+ }
6650
+ private:
6651
+ Arg* arg_ = nullptr;
6652
+ };
6653
+
6654
+ // =========== long double ============
6655
+ template<>
6656
+ class To_Ruby<long double>
6657
+ {
6658
+ public:
6659
+ To_Ruby() = default;
6660
+
6661
+ explicit To_Ruby(Arg* arg) : arg_(arg)
6662
+ {}
6663
+
6664
+ VALUE convert(const long double& native)
6665
+ {
6666
+ return protect(rb_float_new, native);
6667
+ }
6668
+
6669
+ private:
6670
+ Arg* arg_ = nullptr;
6671
+ };
6672
+
6673
+ template<>
6674
+ class To_Ruby<long double&>
6675
+ {
6676
+ public:
6677
+ To_Ruby() = default;
6678
+
6679
+ explicit To_Ruby(Arg* arg) : arg_(arg)
6680
+ {}
6681
+
6682
+ VALUE convert(const long double& native)
6290
6683
  {
6291
6684
  return protect(rb_float_new, native);
6292
6685
  }
@@ -6296,19 +6689,18 @@ namespace Rice4RubyQt6
6296
6689
  };
6297
6690
 
6298
6691
  template<int N>
6299
- class To_Ruby<double[N]>
6692
+ class To_Ruby<long double[N]>
6300
6693
  {
6301
6694
  public:
6302
6695
  To_Ruby() = default;
6303
6696
 
6304
6697
  explicit To_Ruby(Arg* arg) : arg_(arg)
6305
- {
6306
- }
6698
+ {}
6307
6699
 
6308
- VALUE convert(double data[N])
6700
+ VALUE convert(long double data[N])
6309
6701
  {
6310
- Buffer<double> buffer(data, N);
6311
- Data_Object<Buffer<double>> dataObject(std::move(buffer));
6702
+ Buffer<long double> buffer(data, N);
6703
+ Data_Object<Buffer<long double>> dataObject(std::move(buffer));
6312
6704
  return dataObject.value();
6313
6705
  }
6314
6706
  private:
@@ -7040,8 +7432,8 @@ namespace Rice4RubyQt6::detail
7040
7432
  }
7041
7433
  default:
7042
7434
  {
7043
- detail::TypeMapper<Pointer<T>> typeMapper;
7044
- std::string expected = typeMapper.rubyName();
7435
+ detail::TypeDetail<Pointer<T>> typeDetail;
7436
+ std::string expected = typeDetail.rubyName();
7045
7437
  throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
7046
7438
  detail::protect(rb_obj_classname, value), expected.c_str());
7047
7439
  }
@@ -7098,8 +7490,8 @@ namespace Rice4RubyQt6::detail
7098
7490
  }
7099
7491
  default:
7100
7492
  {
7101
- detail::TypeMapper<Pointer<T*>> typeMapper;
7102
- std::string expected = typeMapper.rubyName();
7493
+ detail::TypeDetail<Pointer<T*>> typeDetail;
7494
+ std::string expected = typeDetail.rubyName();
7103
7495
  throw Exception(rb_eTypeError, "wrong argument type %s (expected %s)",
7104
7496
  detail::protect(rb_obj_classname, value), expected.c_str());
7105
7497
  }
@@ -7319,13 +7711,25 @@ namespace Rice4RubyQt6::detail
7319
7711
  }
7320
7712
  case RUBY_T_STRING:
7321
7713
  {
7322
- // WARNING - this shares the Ruby string memory directly with C++. value really should be frozen.
7323
- // Maybe we should enforce that? Note the user can always create a Buffer to allocate new memory.
7324
- return rb_string_value_cstr(&value);
7714
+ if (this->arg_ && this->arg_->isOwner())
7715
+ {
7716
+ // Warning - the receiver needs to free this string!
7717
+ // TODO - raise an exception if the string has null values?
7718
+ long len = RSTRING_LEN(value);
7719
+ char* result = (char*)malloc(len + 1);
7720
+ memcpy(result, RSTRING_PTR(value), len);
7721
+ result[len] = '\0';
7722
+ return result;
7723
+ }
7724
+ else
7725
+ {
7726
+ // WARNING - this shares the Ruby string memory directly with C++. value really should be frozen.
7727
+ // Maybe we should enforce that? Note the user can always create a Buffer to allocate new memory.
7728
+ return rb_string_value_cstr(&value);
7729
+ }
7325
7730
  }
7326
7731
  default:
7327
7732
  {
7328
- char* rb_string_value_cstr(volatile VALUE * ptr);
7329
7733
  return FromRubyFundamental<char*>::convert(value);
7330
7734
  }
7331
7735
  }
@@ -7581,6 +7985,86 @@ namespace Rice4RubyQt6::detail
7581
7985
  Reference<double> reference_;
7582
7986
  };
7583
7987
 
7988
+ // =========== long double ============
7989
+ template<>
7990
+ class From_Ruby<long double>
7991
+ {
7992
+ public:
7993
+ From_Ruby() = default;
7994
+
7995
+ explicit From_Ruby(Arg* arg) : arg_(arg)
7996
+ {}
7997
+
7998
+ long double is_convertible(VALUE value)
7999
+ {
8000
+ return FromRubyFundamental<long double>::is_convertible(value);
8001
+ }
8002
+
8003
+ long double convert(VALUE value)
8004
+ {
8005
+ return FromRubyFundamental<long double>::convert(value);
8006
+ }
8007
+
8008
+ private:
8009
+ Arg* arg_ = nullptr;
8010
+ };
8011
+
8012
+ template<>
8013
+ class From_Ruby<long double&>
8014
+ {
8015
+ public:
8016
+ using Reference_T = Reference<long double>;
8017
+
8018
+ From_Ruby() = default;
8019
+
8020
+ explicit From_Ruby(Arg* arg) : arg_(arg)
8021
+ {}
8022
+
8023
+ long double is_convertible(VALUE value)
8024
+ {
8025
+ switch (rb_type(value))
8026
+ {
8027
+ case RUBY_T_DATA:
8028
+ {
8029
+ if (Data_Type<Reference_T>::is_descendant(value))
8030
+ {
8031
+ return Convertible::Exact;
8032
+ }
8033
+ [[fallthrough]];
8034
+ }
8035
+ default:
8036
+ {
8037
+ return FromRubyFundamental<long double>::is_convertible(value);
8038
+ }
8039
+ }
8040
+ }
8041
+
8042
+ long double& convert(VALUE value)
8043
+ {
8044
+ switch (rb_type(value))
8045
+ {
8046
+ case RUBY_T_DATA:
8047
+ {
8048
+ if (Data_Type<Reference_T>::is_descendant(value))
8049
+ {
8050
+ Reference_T* reference = unwrap<Reference_T>(value, Data_Type<Reference_T>::ruby_data_type(), false);
8051
+ return reference->get();
8052
+ }
8053
+ [[fallthrough]];
8054
+ }
8055
+ default:
8056
+ {
8057
+ this->reference_ = Reference<long double>(value);
8058
+ return this->reference_.get();
8059
+ }
8060
+ }
8061
+ }
8062
+
8063
+ private:
8064
+ Arg* arg_ = nullptr;
8065
+ Reference<long double> reference_;
8066
+ };
8067
+
7584
8068
  // =========== float ============
7585
8069
  template<>
7586
8070
  class From_Ruby<float>
@@ -8621,8 +9105,8 @@ namespace Rice4RubyQt6
8621
9105
 
8622
9106
  if (klassName.empty())
8623
9107
  {
8624
- detail::TypeMapper<Reference_T> typeMapper;
8625
- klassName = typeMapper.rubyName();
9108
+ detail::TypeDetail<Reference_T> typeDetail;
9109
+ klassName = typeDetail.rubyName();
8626
9110
  }
8627
9111
 
8628
9112
  Module rb_mRice = define_module("Rice4RubyQt6");
@@ -8668,40 +9152,55 @@ namespace Rice4RubyQt6::detail
8668
9152
 
8669
9153
  namespace Rice4RubyQt6::detail
8670
9154
  {
9155
+ template <typename T>
9156
+ inline std::type_index TypeRegistry::key()
9157
+ {
9158
+ if constexpr (is_complete_v<T>)
9159
+ {
9160
+ return std::type_index(typeid(T));
9161
+ }
9162
+ else if constexpr (std::is_reference_v<T>)
9163
+ {
9164
+ // For incomplete reference types, strip the reference and use pointer.
9165
+ // Can't form T* when T is a reference type (pointer-to-reference is illegal).
9166
+ return std::type_index(typeid(std::remove_reference_t<T>*));
9167
+ }
9168
+ else
9169
+ {
9170
+ return std::type_index(typeid(T*));
9171
+ }
9172
+ }
9173
+
8671
9174
  template <typename T>
8672
9175
  inline void TypeRegistry::add(VALUE klass, rb_data_type_t* rbType)
8673
9176
  {
8674
- std::type_index key(typeid(T));
8675
- registry_[key] = std::pair(klass, rbType);
9177
+ registry_[key<T>()] = std::pair(klass, rbType);
8676
9178
  }
8677
9179
 
8678
9180
  template <typename T>
8679
9181
  inline void TypeRegistry::remove()
8680
9182
  {
8681
- std::type_index key(typeid(T));
8682
- registry_.erase(key);
9183
+ registry_.erase(key<T>());
8683
9184
  }
8684
9185
 
8685
9186
  template <typename T>
8686
9187
  inline bool TypeRegistry::isDefined()
8687
9188
  {
8688
- std::type_index key(typeid(T));
8689
- auto iter = registry_.find(key);
9189
+ auto iter = registry_.find(key<T>());
8690
9190
  return iter != registry_.end();
8691
9191
  }
8692
9192
 
8693
9193
  template <typename T>
8694
9194
  std::pair<VALUE, rb_data_type_t*> TypeRegistry::getType()
8695
9195
  {
8696
- std::type_index key(typeid(T));
8697
- auto iter = registry_.find(key);
9196
+ auto iter = registry_.find(key<T>());
8698
9197
  if (iter != registry_.end())
8699
9198
  {
8700
9199
  return iter->second;
8701
9200
  }
8702
9201
  else
8703
9202
  {
8704
- this->raiseUnverifiedType(typeid(T).name());
9203
+ this->raiseUnverifiedType(TypeDetail<T>().name());
8705
9204
  // Make compiler happy
8706
9205
  return std::make_pair(Qnil, nullptr);
8707
9206
  }
@@ -8716,17 +9215,14 @@ namespace Rice4RubyQt6::detail
8716
9215
  }
8717
9216
  else
8718
9217
  {
8719
- const std::type_info& typeInfo = typeid(T);
8720
- std::type_index key(typeInfo);
8721
- this->unverified_.insert(key);
9218
+ this->unverified_.insert(key<T>());
8722
9219
  return false;
8723
9220
  }
8724
9221
  }
8725
9222
 
8726
- inline std::optional<std::pair<VALUE, rb_data_type_t*>> TypeRegistry::lookup(const std::type_info& typeInfo)
9223
+ inline std::optional<std::pair<VALUE, rb_data_type_t*>> TypeRegistry::lookup(std::type_index typeIndex)
8727
9224
  {
8728
- std::type_index key(typeInfo);
8729
- auto iter = registry_.find(key);
9225
+ auto iter = registry_.find(typeIndex);
8730
9226
 
8731
9227
  if (iter == registry_.end())
8732
9228
  {
@@ -8741,26 +9237,30 @@ namespace Rice4RubyQt6::detail
8741
9237
  template <typename T>
8742
9238
  inline std::pair<VALUE, rb_data_type_t*> TypeRegistry::figureType(const T& object)
8743
9239
  {
8744
- // First check and see if the actual type of the object is registered
8745
- std::optional<std::pair<VALUE, rb_data_type_t*>> result = lookup(typeid(object));
9240
+ std::optional<std::pair<VALUE, rb_data_type_t*>> result;
8746
9241
 
8747
- if (result)
9242
+ // First check and see if the actual type of the object is registered.
9243
+ // This requires a complete type for typeid to work.
9244
+ if constexpr (is_complete_v<T>)
8748
9245
  {
8749
- return result.value();
9246
+ result = lookup(std::type_index(typeid(object)));
9247
+
9248
+ if (result)
9249
+ {
9250
+ return result.value();
9251
+ }
8750
9252
  }
8751
9253
 
8752
9254
  // If not, then we are willing to accept an ancestor class specified by T. This is needed
8753
9255
  // to support Directors. Classes inherited from Directors are never actually registered
8754
9256
  // with Rice - and what we really want it to return the C++ class they inherit from.
8755
- const std::type_info& typeInfo = typeid(T);
8756
- result = lookup(typeInfo);
9257
+ result = lookup(key<T>());
8757
9258
  if (result)
8758
9259
  {
8759
9260
  return result.value();
8760
9261
  }
8761
9262
 
8762
- detail::TypeIndexParser typeIndexParser(typeid(T), std::is_fundamental_v<detail::intrinsic_type<T>>);
8763
- raiseUnverifiedType(typeIndexParser.name());
9263
+ raiseUnverifiedType(TypeDetail<T>().name());
8764
9264
 
8765
9265
  // Make the compiler happy
8766
9266
  return std::pair<VALUE, rb_data_type_t*>(Qnil, nullptr);
@@ -8794,8 +9294,8 @@ namespace Rice4RubyQt6::detail
8794
9294
 
8795
9295
  for (const std::type_index& typeIndex : this->unverified_)
8796
9296
  {
8797
- detail::TypeIndexParser typeIndexParser(typeIndex);
8798
- stream << " " << typeIndexParser.name() << "\n";
9297
+ detail::TypeIndexParser typeDetail(typeIndex);
9298
+ stream << " " << typeDetail.name() << "\n";
8799
9299
  }
8800
9300
 
8801
9301
  throw std::invalid_argument(stream.str());
@@ -8831,39 +9331,26 @@ namespace Rice4RubyQt6::detail
8831
9331
  namespace Rice4RubyQt6::detail
8832
9332
  {
8833
9333
  template <typename T>
8834
- inline VALUE InstanceRegistry::lookup(T& cppInstance)
8835
- {
8836
- return this->lookup((void*)&cppInstance);
8837
- }
8838
-
8839
- template <typename T>
8840
- inline VALUE InstanceRegistry::lookup(T* cppInstance)
8841
- {
8842
- return this->lookup((void*)cppInstance);
8843
- }
8844
-
8845
- inline VALUE InstanceRegistry::lookup(void* cppInstance)
9334
+ inline VALUE InstanceRegistry::lookup(T* cppInstance, bool isOwner)
8846
9335
  {
8847
- if (!this->isEnabled)
8848
- return Qnil;
8849
-
8850
- auto it = this->objectMap_.find(cppInstance);
8851
- if (it != this->objectMap_.end())
8852
- {
8853
- return it->second;
8854
- }
8855
- else
9336
+ if (!this->shouldTrack(isOwner))
8856
9337
  {
8857
9338
  return Qnil;
8858
9339
  }
9340
+
9341
+ auto it = this->objectMap_.find((void*)cppInstance);
9342
+ return it != this->objectMap_.end() ? it->second : Qnil;
8859
9343
  }
8860
9344
 
8861
- inline void InstanceRegistry::add(void* cppInstance, VALUE rubyInstance)
9345
+ template <typename T>
9346
+ inline void InstanceRegistry::add(T* cppInstance, VALUE rubyInstance, bool isOwner)
8862
9347
  {
8863
- if (this->isEnabled)
9348
+ if (!this->shouldTrack(isOwner))
8864
9349
  {
8865
- this->objectMap_[cppInstance] = rubyInstance;
9350
+ return;
8866
9351
  }
9352
+
9353
+ this->objectMap_[(void*)cppInstance] = rubyInstance;
8867
9354
  }
8868
9355
 
8869
9356
  inline void InstanceRegistry::remove(void* cppInstance)
@@ -8875,7 +9362,22 @@ namespace Rice4RubyQt6::detail
8875
9362
  {
8876
9363
  this->objectMap_.clear();
8877
9364
  }
8878
- } // namespace
9365
+
9366
+ inline bool InstanceRegistry::shouldTrack(bool isOwner) const
9367
+ {
9368
+ switch (this->mode)
9369
+ {
9370
+ case Mode::Off:
9371
+ return false;
9372
+ case Mode::Owned:
9373
+ return isOwner;
9374
+ case Mode::All:
9375
+ return true;
9376
+ default:
9377
+ return false;
9378
+ }
9379
+ }
9380
+ }
8879
9381
 
8880
9382
  // ========= DefaultHandler.ipp =========
8881
9383
  namespace Rice4RubyQt6::detail
@@ -9048,12 +9550,6 @@ namespace Rice4RubyQt6::detail
9048
9550
 
9049
9551
 
9050
9552
  // ========= Type.ipp =========
9051
- #ifdef __GNUC__
9052
- #include <cxxabi.h>
9053
- #include <cstdlib>
9054
- #include <cstring>
9055
- #endif
9056
-
9057
9553
  // Rice saves types either as the intrinsic type (MyObject) or pointer (MyObject*).
9058
9554
  // It strips out references, const and volatile to avoid an explosion of template classes.
9059
9555
  // Pointers are used for C function pointers used in callbacks and for the Buffer class.
@@ -9090,6 +9586,19 @@ namespace Rice4RubyQt6::detail
9090
9586
  return Type<T>::verify();
9091
9587
  }
9092
9588
 
9589
+ template <typename T, int N>
9590
+ inline bool Type<T[N]>::verify()
9591
+ {
9592
+ return Type<T>::verify();
9593
+ }
9594
+
9595
+ template <typename T, int N>
9596
+ inline VALUE Type<T[N]>::rubyKlass()
9597
+ {
9598
+ detail::TypeDetail<Pointer<T>> typeDetail;
9599
+ return typeDetail.rubyKlass();
9600
+ }
9601
+
9093
9602
  template<typename T>
9094
9603
  void verifyType()
9095
9604
  {
@@ -9108,19 +9617,17 @@ namespace Rice4RubyQt6::detail
9108
9617
  std::make_index_sequence<std::tuple_size_v<Tuple_T>> indexes;
9109
9618
  verifyTypesImpl<Tuple_T>(indexes);
9110
9619
  }
9620
+ }
9111
9621
 
9112
- // ---------- RubyKlass ------------
9113
- // Helper template to see if the method rubyKlass is defined on a Type specialization
9114
- template<typename, typename = std::void_t<>>
9115
- struct has_ruby_klass : std::false_type
9116
- {
9117
- };
9118
-
9119
- template<typename T>
9120
- struct has_ruby_klass<T, std::void_t<decltype(T::rubyKlass())>> : std::true_type
9121
- {
9122
- };
9622
+ // ========= TypeIndexParser.ipp =========
9623
+ #ifdef __GNUC__
9624
+ #include <cxxabi.h>
9625
+ #include <cstdlib>
9626
+ #include <cstring>
9627
+ #endif
9123
9628
 
9629
+ namespace Rice4RubyQt6::detail
9630
+ {
9124
9631
  // ---------- TypeIndexParser ------------
9125
9632
  inline TypeIndexParser::TypeIndexParser(const std::type_index& typeIndex, bool isFundamental) :
9126
9633
  typeIndex_(typeIndex), isFundamental_(isFundamental)
@@ -9151,9 +9658,9 @@ namespace Rice4RubyQt6::detail
9151
9658
  }
9152
9659
 
9153
9660
  // Find text inside of < > taking into account nested groups.
9154
- //
9661
+ //
9155
9662
  // Example:
9156
- //
9663
+ //
9157
9664
  // std::vector<std::vector<int>, std::allocator<std::vector, std::allocator<int>>>
9158
9665
  inline std::string TypeIndexParser::findGroup(std::string& string, size_t offset)
9159
9666
  {
@@ -9267,10 +9774,18 @@ namespace Rice4RubyQt6::detail
9267
9774
  std::regex ptrRegex = std::regex(R"(\s+\*)");
9268
9775
  base = std::regex_replace(base, ptrRegex, "*");
9269
9776
 
9777
+ // Remove spaces before left parentheses
9778
+ std::regex parenRegex = std::regex(R"(\s+\()");
9779
+ base = std::regex_replace(base, parenRegex, "(");
9780
+
9270
9781
  // Remove __ptr64
9271
9782
  std::regex ptr64Regex(R"(\s*__ptr64\s*)");
9272
9783
  base = std::regex_replace(base, ptr64Regex, "");
9273
9784
 
9785
+ // Remove calling conventions (__cdecl, __stdcall, __fastcall, etc.)
9786
+ std::regex callingConventionRegex(R"(\s*__cdecl|__stdcall|__fastcall)");
9787
+ base = std::regex_replace(base, callingConventionRegex, "");
9788
+
9274
9789
  // Replace " >" with ">"
9275
9790
  std::regex trailingAngleBracketSpaceRegex = std::regex(R"(\s+>)");
9276
9791
  replaceAll(base, trailingAngleBracketSpaceRegex, ">");
@@ -9342,6 +9857,16 @@ namespace Rice4RubyQt6::detail
9342
9857
  //replaceAll(base, greaterThanRegex, "≻");
9343
9858
  this->replaceAll(base, greaterThanRegex, "\u227B");
9344
9859
 
9860
+ // Replace ( with Unicode Character (U+2768) - Medium Left Parenthesis Ornament
9861
+ // This happens in std::function
9862
+ auto leftParenRegex = std::regex(R"(\()");
9863
+ this->replaceAll(base, leftParenRegex, "\u2768");
9864
+
9865
+ // Replace ) with Unicode Character (U+2769) - Medium Right Parenthesis Ornament
9866
+ // This happens in std::function
9867
+ auto rightParenRegex = std::regex(R"(\))");
9868
+ this->replaceAll(base, rightParenRegex, "\u2769");
9869
+
9345
9870
  // Replace , with Unicode Character (U+066C) - Arabic Thousands Separator
9346
9871
  auto commaRegex = std::regex(R"(,\s*)");
9347
9872
  this->replaceAll(base, commaRegex, "\u201A");
@@ -9359,7 +9884,7 @@ namespace Rice4RubyQt6::detail
9359
9884
  while (std::regex_search(content, match, regex))
9360
9885
  {
9361
9886
  std::string replacement = match[1];
9362
- std::transform(replacement.begin(), replacement.end(), replacement.begin(),
9887
+ std::transform(replacement.begin(), replacement.end(), replacement.begin(),
9363
9888
  [](unsigned char c) -> char
9364
9889
  {
9365
9890
  return static_cast<char>(std::toupper(c));
@@ -9368,9 +9893,53 @@ namespace Rice4RubyQt6::detail
9368
9893
  }
9369
9894
  }
9370
9895
 
9371
- // ---------- TypeMapper ------------
9896
+ // ---------- TypeDetail<T> ------------
9897
+ template<typename T>
9898
+ inline std::type_index TypeDetail<T>::typeIndex()
9899
+ {
9900
+ if constexpr (is_complete_v<T>)
9901
+ {
9902
+ return typeid(T);
9903
+ }
9904
+ else if constexpr (std::is_reference_v<T>)
9905
+ {
9906
+ // For incomplete reference types, strip the reference and use pointer.
9907
+ // Can't use typeid(T&) because it still requires complete type on MSVC.
9908
+ return typeid(std::remove_reference_t<T>*);
9909
+ }
9910
+ else
9911
+ {
9912
+ return typeid(T*);
9913
+ }
9914
+ }
9915
+
9916
+ template<typename T>
9917
+ inline bool TypeDetail<T>::isFundamental()
9918
+ {
9919
+ if constexpr (is_complete_v<T>)
9920
+ {
9921
+ return std::is_fundamental_v<intrinsic_type<T>>;
9922
+ }
9923
+ else
9924
+ {
9925
+ return false;
9926
+ }
9927
+ }
9928
+
9929
+ template<typename T>
9930
+ inline std::string TypeDetail<T>::name()
9931
+ {
9932
+ return this->typeIndexParser_.name();
9933
+ }
9934
+
9935
+ template<typename T>
9936
+ inline std::string TypeDetail<T>::simplifiedName()
9937
+ {
9938
+ return this->typeIndexParser_.simplifiedName();
9939
+ }
9940
+
9372
9941
  template<typename T>
9373
- inline std::string TypeMapper<T>::rubyTypeName()
9942
+ inline std::string TypeDetail<T>::rubyTypeName()
9374
9943
  {
9375
9944
  using Intrinsic_T = detail::intrinsic_type<T>;
9376
9945
 
@@ -9384,20 +9953,20 @@ namespace Rice4RubyQt6::detail
9384
9953
  }
9385
9954
  else
9386
9955
  {
9387
- detail::TypeIndexParser typeIndexParser(typeid(Intrinsic_T), std::is_fundamental_v<detail::intrinsic_type<Intrinsic_T>>);
9388
- return typeIndexParser.simplifiedName();
9956
+ TypeDetail<Intrinsic_T> typeDetail;
9957
+ return typeDetail.simplifiedName();
9389
9958
  }
9390
9959
  }
9391
9960
 
9392
9961
  template<typename T>
9393
- inline std::string TypeMapper<T>::rubyName()
9962
+ inline std::string TypeDetail<T>::rubyName()
9394
9963
  {
9395
9964
  std::string base = this->rubyTypeName();
9396
9965
  return this->typeIndexParser_.rubyName(base);
9397
9966
  }
9398
9967
 
9399
9968
  template<typename T>
9400
- inline VALUE TypeMapper<T>::rubyKlass()
9969
+ inline VALUE TypeDetail<T>::rubyKlass()
9401
9970
  {
9402
9971
  using Type_T = Type<std::remove_reference_t<detail::remove_cv_recursive_t<T>>>;
9403
9972
  using Intrinsic_T = detail::intrinsic_type<T>;
@@ -9408,7 +9977,7 @@ namespace Rice4RubyQt6::detail
9408
9977
  }
9409
9978
  else if constexpr (std::is_fundamental_v<Intrinsic_T> && std::is_pointer_v<T>)
9410
9979
  {
9411
- using Pointer_T = Pointer<std::remove_pointer_t<remove_cv_recursive_t<T>>>;
9980
+ using Pointer_T = Pointer<std::remove_pointer_t<remove_cv_recursive_t<T>>>;
9412
9981
  std::pair<VALUE, rb_data_type_t*> pair = Registries::instance.types.getType<Pointer_T>();
9413
9982
  return pair.first;
9414
9983
  }
@@ -9419,6 +9988,7 @@ namespace Rice4RubyQt6::detail
9419
9988
  }
9420
9989
  }
9421
9990
  }
9991
+
9422
9992
  // Code for Ruby to call C++
9423
9993
 
9424
9994
  // ========= Exception.ipp =========
@@ -9456,7 +10026,8 @@ namespace Rice4RubyQt6
9456
10026
  #endif
9457
10027
 
9458
10028
  // Now create the Ruby exception
9459
- this->exception_ = detail::protect(rb_exc_new2, exceptionClass, this->message_.c_str());
10029
+ VALUE exception = detail::protect(rb_exc_new2, exceptionClass, this->message_.c_str());
10030
+ this->exception_ = Pin(exception);
9460
10031
  }
9461
10032
 
9462
10033
  inline char const* Exception::what() const noexcept
@@ -9465,7 +10036,7 @@ namespace Rice4RubyQt6
9465
10036
  {
9466
10037
  // This isn't protected because if it fails then either we could eat the exception
9467
10038
  // (not good) or crash the program (better)
9468
- VALUE rubyMessage = rb_funcall(this->exception_, rb_intern("message"), 0);
10039
+ VALUE rubyMessage = rb_funcall(this->exception_.value(), rb_intern("message"), 0);
9469
10040
  this->message_ = std::string(RSTRING_PTR(rubyMessage), RSTRING_LEN(rubyMessage));
9470
10041
  }
9471
10042
  return this->message_.c_str();
@@ -9473,12 +10044,12 @@ namespace Rice4RubyQt6
9473
10044
 
9474
10045
  inline VALUE Exception::class_of() const
9475
10046
  {
9476
- return detail::protect(rb_class_of, this->exception_);
10047
+ return detail::protect(rb_class_of, this->exception_.value());
9477
10048
  }
9478
10049
 
9479
10050
  inline VALUE Exception::value() const
9480
10051
  {
9481
- return this->exception_;
10052
+ return this->exception_.value();
9482
10053
  }
9483
10054
  }
9484
10055
 
@@ -9503,6 +10074,9 @@ namespace Rice4RubyQt6::detail
9503
10074
  template <typename Callable_T>
9504
10075
  auto cpp_protect(Callable_T&& func)
9505
10076
  {
10077
+ VALUE excValue = Qnil;
10078
+ int jumpTag = 0;
10079
+
9506
10080
  try
9507
10081
  {
9508
10082
  return func();
@@ -9516,69 +10090,76 @@ namespace Rice4RubyQt6::detail
9516
10090
  }
9517
10091
  catch (::Rice4RubyQt6::Exception const& ex)
9518
10092
  {
9519
- rb_exc_raise(ex.value());
10093
+ excValue = ex.value();
9520
10094
  }
9521
10095
  catch (::Rice4RubyQt6::JumpException const& ex)
9522
10096
  {
9523
- rb_jump_tag(ex.tag);
10097
+ jumpTag = ex.tag;
9524
10098
  }
9525
10099
  catch (std::bad_alloc const& ex)
9526
10100
  {
9527
10101
  /* This won't work quite right if the rb_exc_new2 fails; not
9528
10102
  much we can do about that, since Ruby doesn't give us access
9529
10103
  to a pre-allocated NoMemoryError object */
9530
- rb_exc_raise(rb_exc_new2(rb_eNoMemError, ex.what()));
10104
+ excValue = rb_exc_new2(rb_eNoMemError, ex.what());
9531
10105
  }
9532
10106
  catch (std::domain_error const& ex)
9533
10107
  {
9534
- rb_exc_raise(rb_exc_new2(rb_eFloatDomainError, ex.what()));
10108
+ excValue = rb_exc_new2(rb_eFloatDomainError, ex.what());
9535
10109
  }
9536
10110
  catch (std::invalid_argument const& ex)
9537
10111
  {
9538
- rb_exc_raise(rb_exc_new2(rb_eArgError, ex.what()));
10112
+ excValue = rb_exc_new2(rb_eArgError, ex.what());
9539
10113
  }
9540
10114
  catch (fs::filesystem_error const& ex)
9541
10115
  {
9542
- rb_exc_raise(rb_exc_new2(rb_eIOError, ex.what()));
10116
+ excValue = rb_exc_new2(rb_eIOError, ex.what());
9543
10117
  }
9544
10118
  catch (std::length_error const& ex)
9545
10119
  {
9546
- rb_exc_raise(rb_exc_new2(rb_eIndexError, ex.what()));
10120
+ excValue = rb_exc_new2(rb_eIndexError, ex.what());
9547
10121
  }
9548
10122
  catch (std::out_of_range const& ex)
9549
10123
  {
9550
- rb_exc_raise(rb_exc_new2(rb_eIndexError, ex.what()));
10124
+ excValue = rb_exc_new2(rb_eIndexError, ex.what());
9551
10125
  }
9552
10126
  catch (std::overflow_error const& ex)
9553
10127
  {
9554
- rb_exc_raise(rb_exc_new2(rb_eRangeError, ex.what()));
10128
+ excValue = rb_exc_new2(rb_eRangeError, ex.what());
9555
10129
  }
9556
10130
  catch (std::range_error const& ex)
9557
10131
  {
9558
- rb_exc_raise(rb_exc_new2(rb_eRangeError, ex.what()));
10132
+ excValue = rb_exc_new2(rb_eRangeError, ex.what());
9559
10133
  }
9560
10134
  catch (std::regex_error const& ex)
9561
10135
  {
9562
- rb_exc_raise(rb_exc_new2(rb_eRegexpError, ex.what()));
10136
+ excValue = rb_exc_new2(rb_eRegexpError, ex.what());
9563
10137
  }
9564
10138
  catch (std::system_error const& ex)
9565
10139
  {
9566
- rb_exc_raise(rb_exc_new2(rb_eSystemCallError, ex.what()));
10140
+ excValue = rb_exc_new2(rb_eSystemCallError, ex.what());
9567
10141
  }
9568
10142
  catch (std::underflow_error const& ex)
9569
10143
  {
9570
- rb_exc_raise(rb_exc_new2(rb_eRangeError, ex.what()));
10144
+ excValue = rb_exc_new2(rb_eRangeError, ex.what());
9571
10145
  }
9572
10146
  catch (std::exception const& ex)
9573
10147
  {
9574
- rb_exc_raise(rb_exc_new2(rb_eRuntimeError, ex.what()));
10148
+ excValue = rb_exc_new2(rb_eRuntimeError, ex.what());
9575
10149
  }
9576
10150
  catch (...)
9577
10151
  {
9578
- rb_exc_raise(rb_exc_new2(rb_eRuntimeError, "Unknown C++ exception thrown"));
10152
+ excValue = rb_exc_new2(rb_eRuntimeError, "Unknown C++ exception thrown");
9579
10153
  }
9580
- throw std::runtime_error("Should never get here - just making compilers happy");
9581
10154
  }
10155
+ // All C++ exception objects and the handler are now destroyed.
10156
+ // It is safe to call rb_jump_tag/rb_exc_raise which use longjmp.
10157
+ if (jumpTag)
10158
+ rb_jump_tag(jumpTag);
10159
+ else
10160
+ rb_exc_raise(excValue);
10161
+
10162
+ throw std::runtime_error("Should never get here - just making compilers happy");
9582
10163
  }
9583
10164
  }
9584
10165
 
@@ -9621,6 +10202,16 @@ namespace Rice4RubyQt6::detail
9621
10202
  this->keepAlive_.push_back(value);
9622
10203
  }
9623
10204
 
10205
+ inline const std::vector<VALUE>& WrapperBase::getKeepAlive() const
10206
+ {
10207
+ return this->keepAlive_;
10208
+ }
10209
+
10210
+ inline void WrapperBase::setKeepAlive(const std::vector<VALUE>& keepAlive)
10211
+ {
10212
+ this->keepAlive_ = keepAlive;
10213
+ }
10214
+
9624
10215
  inline void WrapperBase::setOwner(bool value)
9625
10216
  {
9626
10217
  this->isOwner_ = value;
@@ -9638,12 +10229,6 @@ namespace Rice4RubyQt6::detail
9638
10229
  {
9639
10230
  }
9640
10231
 
9641
- template <typename T>
9642
- inline Wrapper<T>::~Wrapper()
9643
- {
9644
- Registries::instance.instances.remove(this->get(this->rb_data_type_));
9645
- }
9646
-
9647
10232
  template <typename T>
9648
10233
  inline void* Wrapper<T>::get(rb_data_type_t* requestedType)
9649
10234
  {
@@ -9700,11 +10285,14 @@ namespace Rice4RubyQt6::detail
9700
10285
  {
9701
10286
  Registries::instance.instances.remove(this->get(this->rb_data_type_));
9702
10287
 
9703
- if constexpr (std::is_destructible_v<T>)
10288
+ if constexpr (is_complete_v<T>)
9704
10289
  {
9705
- if (this->isOwner_)
10290
+ if constexpr (std::is_destructible_v<T>)
9706
10291
  {
9707
- delete this->data_;
10292
+ if (this->isOwner_)
10293
+ {
10294
+ delete this->data_;
10295
+ }
9708
10296
  }
9709
10297
  }
9710
10298
  }
@@ -9737,11 +10325,14 @@ namespace Rice4RubyQt6::detail
9737
10325
  {
9738
10326
  Registries::instance.instances.remove(this->get(this->rb_data_type_));
9739
10327
 
9740
- if constexpr (std::is_destructible_v<T>)
10328
+ if constexpr (is_complete_v<T>)
9741
10329
  {
9742
- if (this->isOwner_)
10330
+ if constexpr (std::is_destructible_v<T>)
9743
10331
  {
9744
- delete this->data_;
10332
+ if (this->isOwner_)
10333
+ {
10334
+ delete this->data_;
10335
+ }
9745
10336
  }
9746
10337
  }
9747
10338
  }
@@ -9763,68 +10354,83 @@ namespace Rice4RubyQt6::detail
9763
10354
 
9764
10355
  // ---- Helper Functions -------
9765
10356
  template <typename T>
9766
- inline VALUE wrap(VALUE klass, rb_data_type_t* rb_data_type, T& data, bool isOwner)
10357
+ inline VALUE wrapLvalue(VALUE klass, rb_data_type_t* rb_data_type, T& data)
9767
10358
  {
9768
- VALUE result = Registries::instance.instances.lookup(&data);
9769
-
9770
- if (result != Qnil)
9771
- return result;
10359
+ WrapperBase* wrapper = new Wrapper<T>(rb_data_type, data);
10360
+ return TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
10361
+ }
9772
10362
 
9773
- WrapperBase* wrapper = nullptr;
10363
+ template <typename T>
10364
+ inline VALUE wrapRvalue(VALUE klass, rb_data_type_t* rb_data_type, T& data)
10365
+ {
10366
+ WrapperBase* wrapper = new Wrapper<T>(rb_data_type, std::move(data));
10367
+ return TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
10368
+ }
9774
10369
 
9775
- // If Ruby is not the owner then wrap the reference
9776
- if (!isOwner)
10370
+ template <typename T>
10371
+ inline VALUE wrapReference(VALUE klass, rb_data_type_t* rb_data_type, T& data, bool isOwner)
10372
+ {
10373
+ VALUE result = Registries::instance.instances.lookup(&data, isOwner);
10374
+ if (result == Qnil)
9777
10375
  {
9778
- wrapper = new Wrapper<T&>(rb_data_type, data);
10376
+ WrapperBase* wrapper = new Wrapper<T&>(rb_data_type, data);
9779
10377
  result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
10378
+ Registries::instance.instances.add(&data, result, isOwner);
9780
10379
  }
10380
+ return result;
10381
+ }
9781
10382
 
10383
+ template <typename T>
10384
+ inline VALUE wrap(VALUE klass, rb_data_type_t* rb_data_type, T& data, bool isOwner)
10385
+ {
10386
+ // Incomplete types can't be copied/moved, just wrap as reference
10387
+ if constexpr (!is_complete_v<T>)
10388
+ {
10389
+ return wrapReference(klass, rb_data_type, data, isOwner);
10390
+ }
10391
+ // If Ruby is not the owner then wrap the reference
10392
+ else if (!isOwner)
10393
+ {
10394
+ return wrapReference(klass, rb_data_type, data, isOwner);
10395
+ }
9782
10396
  // std::is_copy_constructible_v<std::vector<std::unique_ptr<T>>>> returns true. Sigh.
9783
10397
  else if constexpr (detail::is_std_vector_v<T>)
9784
10398
  {
9785
10399
  if constexpr (std::is_copy_constructible_v<typename T::value_type>)
9786
10400
  {
9787
- wrapper = new Wrapper<T>(rb_data_type, data);
9788
- result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
10401
+ return wrapLvalue(klass, rb_data_type, data);
9789
10402
  }
9790
10403
  else
9791
10404
  {
9792
- wrapper = new Wrapper<T>(rb_data_type, std::move(data));
9793
- result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
10405
+ return wrapRvalue(klass, rb_data_type, data);
9794
10406
  }
9795
10407
  }
9796
10408
 
9797
10409
  // Ruby is the owner so copy data
9798
10410
  else if constexpr (std::is_copy_constructible_v<T>)
9799
10411
  {
9800
- wrapper = new Wrapper<T>(rb_data_type, data);
9801
- result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
10412
+ return wrapLvalue(klass, rb_data_type, data);
9802
10413
  }
9803
10414
 
9804
10415
  // Ruby is the owner so move data
9805
10416
  else if constexpr (std::is_move_constructible_v<T>)
9806
10417
  {
9807
- wrapper = new Wrapper<T>(rb_data_type, std::move(data));
9808
- result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
10418
+ return wrapRvalue(klass, rb_data_type, data);
9809
10419
  }
9810
10420
 
9811
10421
  else
9812
10422
  {
9813
- detail::TypeIndexParser typeIndexParser(typeid(T), std::is_fundamental_v<detail::intrinsic_type<T>>);
10423
+ detail::TypeDetail<T> typeDetail;
9814
10424
  std::string message = "Rice was directed to take ownership of a C++ object but it does not have an accessible copy or move constructor. Type: " +
9815
- typeIndexParser.name();
10425
+ typeDetail.name();
9816
10426
  throw std::runtime_error(message);
9817
10427
  }
9818
-
9819
- Registries::instance.instances.add(wrapper->get(rb_data_type), result);
9820
-
9821
- return result;
9822
10428
  };
9823
10429
 
9824
10430
  template <typename T>
9825
10431
  inline VALUE wrap(VALUE klass, rb_data_type_t* rb_data_type, T* data, bool isOwner)
9826
10432
  {
9827
- VALUE result = Registries::instance.instances.lookup(data);
10433
+ VALUE result = Registries::instance.instances.lookup(data, isOwner);
9828
10434
 
9829
10435
  if (result != Qnil)
9830
10436
  return result;
@@ -9832,7 +10438,7 @@ namespace Rice4RubyQt6::detail
9832
10438
  WrapperBase* wrapper = new Wrapper<T*>(rb_data_type, data, isOwner);
9833
10439
  result = TypedData_Wrap_Struct(klass, rb_data_type, wrapper);
9834
10440
 
9835
- Registries::instance.instances.add(wrapper->get(rb_data_type), result);
10441
+ Registries::instance.instances.add(data, result, isOwner);
9836
10442
  return result;
9837
10443
  };
9838
10444
 
@@ -9846,6 +10452,12 @@ namespace Rice4RubyQt6::detail
9846
10452
  throw std::runtime_error(message);
9847
10453
  }
9848
10454
 
10455
+ if (protect(rb_obj_is_kind_of, value, rb_cProc))
10456
+ {
10457
+ std::string message = "The Ruby object is a proc or lambda and does not wrap a C++ object";
10458
+ throw std::runtime_error(message);
10459
+ }
10460
+
9849
10461
  WrapperBase* wrapper = static_cast<WrapperBase*>(RTYPEDDATA_DATA(value));
9850
10462
 
9851
10463
  if (wrapper == nullptr)
@@ -9896,7 +10508,7 @@ namespace Rice4RubyQt6::detail
9896
10508
  }
9897
10509
 
9898
10510
  template <typename T>
9899
- inline Wrapper<T*>* wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data)
10511
+ inline Wrapper<T*>* wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data, VALUE source)
9900
10512
  {
9901
10513
  using Wrapper_T = Wrapper<T*>;
9902
10514
 
@@ -9911,7 +10523,15 @@ namespace Rice4RubyQt6::detail
9911
10523
  wrapper = new Wrapper_T(rb_data_type, data, true);
9912
10524
  RTYPEDDATA_DATA(value) = wrapper;
9913
10525
 
9914
- Registries::instance.instances.add(data, value);
10526
+ Registries::instance.instances.add(data, value, true);
10527
+
10528
+ // Copy keepAlive references from the source object (used by initialize_copy
10529
+ // so that cloned containers directly protect the same Ruby objects)
10530
+ if (source != Qnil)
10531
+ {
10532
+ WrapperBase* sourceWrapper = getWrapper(source);
10533
+ wrapper->setKeepAlive(sourceWrapper->getKeepAlive());
10534
+ }
9915
10535
 
9916
10536
  return wrapper;
9917
10537
  }
@@ -10233,7 +10853,7 @@ namespace Rice4RubyQt6::detail
10233
10853
  std::map<std::string, VALUE> result;
10234
10854
 
10235
10855
  // Keyword handling
10236
- if (rb_keyword_given_p())
10856
+ if (protect(rb_keyword_given_p))
10237
10857
  {
10238
10858
  // Keywords are stored in the last element in a hash
10239
10859
  size_t actualArgc = argc - 1;
@@ -10264,6 +10884,13 @@ namespace Rice4RubyQt6::detail
10264
10884
  }
10265
10885
  }
10266
10886
 
10887
+ // If a block is given we assume it maps to the last argument
10888
+ if (protect(rb_block_given_p))
10889
+ {
10890
+ std::string key = "arg_" + std::to_string(result.size());
10891
+ result[key] = protect(rb_block_proc);
10892
+ }
10893
+
10267
10894
  return result;
10268
10895
  }
10269
10896
 
@@ -10307,10 +10934,6 @@ namespace Rice4RubyQt6::detail
10307
10934
  {
10308
10935
  result[i] = parameter->defaultValueRuby();
10309
10936
  }
10310
- else if (arg->isBlock() && rb_block_given_p())
10311
- {
10312
- result[i] = protect(rb_block_proc);
10313
- }
10314
10937
  else if (validate)
10315
10938
  {
10316
10939
  std::string message = "Missing argument. Name: " + arg->name + ". Index: " + std::to_string(i) + ".";
@@ -10529,13 +11152,13 @@ namespace Rice4RubyQt6::detail
10529
11152
  bool isBuffer = dynamic_cast<ReturnBuffer*>(this->returnInfo_.get()) ? true : false;
10530
11153
  if (isBuffer)
10531
11154
  {
10532
- TypeMapper<Pointer<detail::remove_cv_recursive_t<std::remove_pointer_t<Attr_T>>>> typeMapper;
10533
- return typeMapper.rubyKlass();
11155
+ TypeDetail<Pointer<detail::remove_cv_recursive_t<std::remove_pointer_t<Attr_T>>>> typeDetail;
11156
+ return typeDetail.rubyKlass();
10534
11157
  }
10535
11158
  else
10536
11159
  {
10537
- TypeMapper<Attr_T> typeMapper;
10538
- return typeMapper.rubyKlass();
11160
+ TypeDetail<Attr_T> typeDetail;
11161
+ return typeDetail.rubyKlass();
10539
11162
  }
10540
11163
  }
10541
11164
  }
@@ -10646,8 +11269,8 @@ namespace Rice4RubyQt6::detail
10646
11269
  template<typename Attribute_T>
10647
11270
  inline VALUE NativeAttributeSet<Attribute_T>::returnKlass()
10648
11271
  {
10649
- TypeMapper<Attr_T> typeMapper;
10650
- return typeMapper.rubyKlass();
11272
+ TypeDetail<Attr_T> typeDetail;
11273
+ return typeDetail.rubyKlass();
10651
11274
  }
10652
11275
  }
10653
11276
 
@@ -10795,8 +11418,8 @@ namespace Rice4RubyQt6::detail
10795
11418
  {
10796
11419
  std::ostringstream result;
10797
11420
 
10798
- detail::TypeIndexParser typeIndexParser(typeid(Return_T), std::is_fundamental_v<detail::intrinsic_type<Return_T>>);
10799
- result << typeIndexParser.simplifiedName() << " ";
11421
+ detail::TypeDetail<Return_T> typeDetail;
11422
+ result << typeDetail.simplifiedName() << " ";
10800
11423
  result << this->name();
10801
11424
 
10802
11425
  result << "(";
@@ -10904,13 +11527,13 @@ namespace Rice4RubyQt6::detail
10904
11527
  bool isBuffer = dynamic_cast<ReturnBuffer*>(this->returnInfo_.get()) ? true : false;
10905
11528
  if (isBuffer)
10906
11529
  {
10907
- TypeMapper<Pointer<detail::remove_cv_recursive_t<std::remove_pointer_t<Return_T>>>> typeMapper;
10908
- return typeMapper.rubyKlass();
11530
+ TypeDetail<Pointer<detail::remove_cv_recursive_t<std::remove_pointer_t<Return_T>>>> typeDetail;
11531
+ return typeDetail.rubyKlass();
10909
11532
  }
10910
11533
  else
10911
11534
  {
10912
- TypeMapper<Return_T> typeMapper;
10913
- return typeMapper.rubyKlass();
11535
+ TypeDetail<Return_T> typeDetail;
11536
+ return typeDetail.rubyKlass();
10914
11537
  }
10915
11538
  }
10916
11539
  }
@@ -11055,7 +11678,12 @@ namespace Rice4RubyQt6::detail
11055
11678
  detail::To_Ruby<To_Ruby_T> toRuby;
11056
11679
  for (; it != end; ++it)
11057
11680
  {
11058
- protect(rb_yield, toRuby.convert(*it));
11681
+ // Use auto&& to accept both reference- and value-returning iterators.
11682
+ // - If *it is an lvalue (T&), auto&& deduces to T&, no copy made.
11683
+ // - If *it is a prvalue (T), auto&& deduces to T&&, value binds to the temporary for the scope of this loop iteration.
11684
+ // This also avoids MSVC C4239 when convert expects a non-const lvalue reference.
11685
+ auto&& value = *it;
11686
+ protect(rb_yield, toRuby.convert(value));
11059
11687
  }
11060
11688
 
11061
11689
  return self;
@@ -11081,13 +11709,13 @@ namespace Rice4RubyQt6::detail
11081
11709
  bool isBuffer = dynamic_cast<ReturnBuffer*>(this->returnInfo_.get()) ? true : false;
11082
11710
  if (isBuffer)
11083
11711
  {
11084
- TypeMapper<Pointer<detail::remove_cv_recursive_t<std::remove_pointer_t<Value_T>>>> typeMapper;
11085
- return typeMapper.rubyKlass();
11712
+ TypeDetail<Pointer<detail::remove_cv_recursive_t<std::remove_pointer_t<Value_T>>>> typeDetail;
11713
+ return typeDetail.rubyKlass();
11086
11714
  }
11087
11715
  else
11088
11716
  {
11089
- TypeMapper<Value_T> typeMapper;
11090
- return typeMapper.rubyKlass();
11717
+ TypeDetail<Value_T> typeDetail;
11718
+ return typeDetail.rubyKlass();
11091
11719
  }
11092
11720
  }
11093
11721
  }
@@ -11241,13 +11869,13 @@ namespace Rice4RubyQt6::detail
11241
11869
  {
11242
11870
  std::ostringstream result;
11243
11871
 
11244
- detail::TypeIndexParser typeIndexParserReturn(typeid(Return_T), std::is_fundamental_v<detail::intrinsic_type<Return_T>>);
11245
- result << typeIndexParserReturn.simplifiedName() << " ";
11246
-
11872
+ detail::TypeDetail<Return_T> typeDetailReturn;
11873
+ result << typeDetailReturn.simplifiedName() << " ";
11874
+
11247
11875
  if (!std::is_null_pointer_v<Receiver_T>)
11248
11876
  {
11249
- detail::TypeIndexParser typeIndexParserReceiver(typeid(Receiver_T), std::is_fundamental_v<detail::intrinsic_type<Receiver_T>>);
11250
- result << typeIndexParserReceiver.simplifiedName() << "::";
11877
+ detail::TypeDetail<Receiver_T> typeDetailReceiver;
11878
+ result << typeDetailReceiver.simplifiedName() << "::";
11251
11879
  }
11252
11880
 
11253
11881
  result << this->name();
@@ -11443,13 +12071,13 @@ namespace Rice4RubyQt6::detail
11443
12071
  bool isBuffer = dynamic_cast<ReturnBuffer*>(this->returnInfo_.get()) ? true : false;
11444
12072
  if (isBuffer)
11445
12073
  {
11446
- TypeMapper<Pointer<detail::remove_cv_recursive_t<std::remove_pointer_t<Return_T>>>> typeMapper;
11447
- return typeMapper.rubyKlass();
12074
+ TypeDetail<Pointer<detail::remove_cv_recursive_t<std::remove_pointer_t<Return_T>>>> typeDetail;
12075
+ return typeDetail.rubyKlass();
11448
12076
  }
11449
12077
  else
11450
12078
  {
11451
- TypeMapper<Return_T> typeMapper;
11452
- return typeMapper.rubyKlass();
12079
+ TypeDetail<Return_T> typeDetail;
12080
+ return typeDetail.rubyKlass();
11453
12081
  }
11454
12082
  }
11455
12083
  }
@@ -11637,13 +12265,13 @@ namespace Rice4RubyQt6::detail
11637
12265
  bool isBuffer = dynamic_cast<ReturnBuffer*>(this->returnInfo_.get()) ? true : false;
11638
12266
  if (isBuffer)
11639
12267
  {
11640
- TypeMapper<Pointer<detail::remove_cv_recursive_t<std::remove_pointer_t<Return_T>>>> typeMapper;
11641
- return typeMapper.rubyKlass();
12268
+ TypeDetail<Pointer<detail::remove_cv_recursive_t<std::remove_pointer_t<Return_T>>>> typeDetail;
12269
+ return typeDetail.rubyKlass();
11642
12270
  }
11643
12271
  else
11644
12272
  {
11645
- TypeMapper<Return_T> typeMapper;
11646
- return typeMapper.rubyKlass();
12273
+ TypeDetail<Return_T> typeDetail;
12274
+ return typeDetail.rubyKlass();
11647
12275
  }
11648
12276
  }
11649
12277
  }
@@ -11658,6 +12286,11 @@ namespace Rice4RubyQt6::detail
11658
12286
  template<typename Callback_T>
11659
12287
  class NativeCallback;
11660
12288
 
12289
+ // NativeCallback instances are never freed because there is no way for us to know
12290
+ // when they can be freed. At the same time, the Pin prevents the Ruby proc from being
12291
+ // garbage collected, which is necessary because C code may call the callback
12292
+ // at any time. This supports passing blocks to C callbacks without requiring the Ruby
12293
+ // user to manually hold a reference to a proc.
11661
12294
  template<typename Return_T, typename ...Parameter_Ts>
11662
12295
  class NativeCallback<Return_T(*)(Parameter_Ts...)> : public Native
11663
12296
  {
@@ -11666,8 +12299,6 @@ namespace Rice4RubyQt6::detail
11666
12299
  using NativeCallback_T = NativeCallback<Callback_T>;
11667
12300
  using Tuple_T = std::tuple<Parameter_Ts...>;
11668
12301
 
11669
- static VALUE finalizerCallback(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg);
11670
-
11671
12302
  template<typename ...Arg_Ts>
11672
12303
  static void define(Arg_Ts&& ...args);
11673
12304
 
@@ -11707,7 +12338,7 @@ namespace Rice4RubyQt6::detail
11707
12338
  template<std::size_t... I>
11708
12339
  Return_T callRuby(std::index_sequence<I...>& indices, Parameter_Ts...args);
11709
12340
 
11710
- VALUE proc_;
12341
+ Pin proc_;
11711
12342
  From_Ruby<Return_T> fromRuby_;
11712
12343
 
11713
12344
  #ifdef HAVE_LIBFFI
@@ -11880,18 +12511,10 @@ namespace Rice4RubyQt6::detail
11880
12511
  {
11881
12512
  /* Loop over each value returned from Ruby and convert it to the appropriate C++ type based
11882
12513
  on the arguments (Parameter_Ts) required by the C++ function. Arg_T may have const/volatile while
11883
- the associated From_Ruby<T> template parameter will not. Thus From_Ruby produces non-const values
11884
- which we let the compiler convert to const values as needed. This works except for
11885
- T** -> const T**, see comment in convertToNative method. */
11886
- return std::forward_as_tuple(extractArg<Parameter_Ts>(args[I])...);
11887
- }
11888
-
11889
- template<typename Return_T, typename ...Parameter_Ts>
11890
- VALUE NativeCallback<Return_T(*)(Parameter_Ts...)>::finalizerCallback(VALUE, VALUE callback_arg, int, const VALUE*, VALUE)
11891
- {
11892
- NativeCallback_T* nativeCallback = (NativeCallback_T*)callback_arg;
11893
- delete nativeCallback;
11894
- return Qnil;
12514
+ the associated From_Ruby<T> template parameter will not. Thus From_Ruby produces non-const values
12515
+ which we let the compiler convert to const values as needed. This works except for
12516
+ T** -> const T**, see comment in convertToNative method. */
12517
+ return std::forward_as_tuple(extractArg<Parameter_Ts>(args[I])...);
11895
12518
  }
11896
12519
 
11897
12520
  template<typename Return_T, typename...Parameter_Ts>
@@ -11911,10 +12534,6 @@ namespace Rice4RubyQt6::detail
11911
12534
  Native("callback", copyReturnInfo(), copyParameters()),
11912
12535
  proc_(proc), fromRuby_(returnInfo_.get())
11913
12536
  {
11914
- // Tie the lifetime of the NativeCallback C++ instance to the lifetime of the Ruby proc object
11915
- VALUE finalizer = rb_proc_new(NativeCallback_T::finalizerCallback, (VALUE)this);
11916
- rb_define_finalizer(proc, finalizer);
11917
-
11918
12537
  #ifdef HAVE_LIBFFI
11919
12538
  // First setup description of callback
11920
12539
  if (cif_.bytes == 0)
@@ -11941,8 +12560,6 @@ namespace Rice4RubyQt6::detail
11941
12560
 
11942
12561
  NativeCallback_T::callback_ = nullptr;
11943
12562
  NativeCallback_T::native_ = nullptr;
11944
-
11945
- this->proc_ = Qnil;
11946
12563
  }
11947
12564
 
11948
12565
  template<typename Return_T, typename ...Parameter_Ts>
@@ -11961,7 +12578,7 @@ namespace Rice4RubyQt6::detail
11961
12578
  convertToRuby(args)... };
11962
12579
 
11963
12580
  static Identifier id("call");
11964
- VALUE result = detail::protect(rb_funcallv, this->proc_, id.id(), (int)sizeof...(Parameter_Ts), values.data());
12581
+ VALUE result = detail::protect(rb_funcallv, this->proc_.value(), id.id(), (int)sizeof...(Parameter_Ts), values.data());
11965
12582
  if constexpr (!std::is_void_v<Return_T>)
11966
12583
  {
11967
12584
  return this->fromRuby_.convert(result);
@@ -11994,13 +12611,13 @@ namespace Rice4RubyQt6::detail
11994
12611
  bool isBuffer = dynamic_cast<ReturnBuffer*>(this->returnInfo_.get()) ? true : false;
11995
12612
  if (isBuffer)
11996
12613
  {
11997
- TypeMapper<Pointer<detail::remove_cv_recursive_t<std::remove_pointer_t<Return_T>>>> typeMapper;
11998
- return typeMapper.rubyKlass();
12614
+ TypeDetail<Pointer<detail::remove_cv_recursive_t<std::remove_pointer_t<Return_T>>>> typeDetail;
12615
+ return typeDetail.rubyKlass();
11999
12616
  }
12000
12617
  else
12001
12618
  {
12002
- TypeMapper<Return_T> typeMapper;
12003
- return typeMapper.rubyKlass();
12619
+ TypeDetail<Return_T> typeDetail;
12620
+ return typeDetail.rubyKlass();
12004
12621
  }
12005
12622
  }
12006
12623
  }
@@ -12057,7 +12674,7 @@ namespace Rice4RubyQt6::detail
12057
12674
 
12058
12675
  double is_convertible(VALUE value)
12059
12676
  {
12060
- if (protect(rb_obj_is_proc, value) == Qtrue || protect(rb_proc_lambda_p, value) == Qtrue)
12677
+ if (protect(rb_obj_is_proc, value) == Qtrue)
12061
12678
  {
12062
12679
  return Convertible::Exact;
12063
12680
  }
@@ -12144,31 +12761,45 @@ namespace Rice4RubyQt6
12144
12761
  // ========= Object.ipp =========
12145
12762
  namespace Rice4RubyQt6
12146
12763
  {
12147
- inline const Object Nil(Qnil);
12148
- inline const Object True(Qtrue);
12149
- inline const Object False(Qfalse);
12150
- inline const Object Undef(Qundef);
12764
+ inline Object::Object() : value_(Qnil)
12765
+ {
12766
+ }
12151
12767
 
12152
- // Ruby auto detects VALUEs in the stack, so when an Object gets deleted make sure
12153
- // to clean up in case it is on the stack
12154
- inline Object::~Object()
12768
+ inline Object::Object(VALUE value) : value_(value)
12155
12769
  {
12156
- this->value_ = Qnil;
12157
12770
  }
12158
12771
 
12159
- // Move constructor
12160
- inline Object::Object(Object&& other)
12772
+ inline VALUE Object::value() const
12161
12773
  {
12162
- this->value_ = other.value_;
12163
- other.value_ = Qnil;
12774
+ return this->value_.value();
12164
12775
  }
12165
12776
 
12166
- // Move assignment
12167
- inline Object& Object::operator=(Object&& other)
12777
+ inline Object::operator VALUE() const
12168
12778
  {
12169
- this->value_ = other.value_;
12170
- other.value_ = Qnil;
12171
- return *this;
12779
+ return this->value();
12780
+ }
12781
+
12782
+ inline VALUE Object::validated_value() const
12783
+ {
12784
+ VALUE result = this->value();
12785
+
12786
+ if (result == Qnil)
12787
+ {
12788
+ std::string message = "Rice Object does not wrap a Ruby object";
12789
+ throw std::runtime_error(message.c_str());
12790
+ }
12791
+
12792
+ return result;
12793
+ }
12794
+
12795
+ inline Object::operator bool() const
12796
+ {
12797
+ return RTEST(this->value());
12798
+ }
12799
+
12800
+ inline bool Object::is_nil() const
12801
+ {
12802
+ return NIL_P(this->value());
12172
12803
  }
12173
12804
 
12174
12805
  template<typename ...Parameter_Ts>
@@ -12182,7 +12813,7 @@ namespace Rice4RubyQt6
12182
12813
  easy to duplicate by setting GC.stress to true and calling a constructor
12183
12814
  that takes multiple values like a std::pair wrapper. */
12184
12815
  std::array<VALUE, sizeof...(Parameter_Ts)> values = { detail::To_Ruby<detail::remove_cv_recursive_t<Parameter_Ts>>().convert(std::forward<Parameter_Ts>(args))... };
12185
- return detail::protect(rb_funcallv, value(), id.id(), (int)values.size(), (const VALUE*)values.data());
12816
+ return detail::protect(rb_funcallv, this->validated_value(), id.id(), (int)values.size(), (const VALUE*)values.data());
12186
12817
  }
12187
12818
 
12188
12819
  template<typename ...Parameter_Ts>
@@ -12190,13 +12821,13 @@ namespace Rice4RubyQt6
12190
12821
  {
12191
12822
  /* IMPORTANT - See call() above */
12192
12823
  std::array<VALUE, sizeof...(Parameter_Ts)> values = { detail::To_Ruby<detail::remove_cv_recursive_t<Parameter_Ts>>().convert(args)... };
12193
- return detail::protect(rb_funcallv_kw, value(), id.id(), (int)values.size(), (const VALUE*)values.data(), RB_PASS_KEYWORDS);
12824
+ return detail::protect(rb_funcallv_kw, this->validated_value(), id.id(), (int)values.size(), (const VALUE*)values.data(), RB_PASS_KEYWORDS);
12194
12825
  }
12195
12826
 
12196
12827
  template<typename T>
12197
12828
  inline void Object::iv_set(Identifier name, T const& value)
12198
12829
  {
12199
- detail::protect(rb_ivar_set, this->value(), name.id(), detail::To_Ruby<T>().convert(value));
12830
+ detail::protect(rb_ivar_set, this->validated_value(), name.id(), detail::To_Ruby<T>().convert(value));
12200
12831
  }
12201
12832
 
12202
12833
  inline int Object::compare(Object const& other) const
@@ -12207,87 +12838,99 @@ namespace Rice4RubyQt6
12207
12838
 
12208
12839
  inline bool Object::is_equal(const Object& other) const
12209
12840
  {
12210
- VALUE result = detail::protect(rb_equal, this->value_, other.value_);
12841
+ if (this->is_nil() || other.is_nil())
12842
+ {
12843
+ return this->is_nil() && other.is_nil();
12844
+ }
12845
+
12846
+ VALUE result = detail::protect(rb_equal, this->validated_value(), other.validated_value());
12211
12847
  return RB_TEST(result);
12212
12848
  }
12213
12849
 
12214
12850
  inline bool Object::is_eql(const Object& other) const
12215
12851
  {
12216
- VALUE result = detail::protect(rb_eql, this->value_, other.value_);
12852
+ if (this->is_nil() || other.is_nil())
12853
+ {
12854
+ return this->is_nil() && other.is_nil();
12855
+ }
12856
+
12857
+ VALUE result = detail::protect(rb_eql, this->validated_value(), other.validated_value());
12217
12858
  return RB_TEST(result);
12218
12859
  }
12219
12860
 
12220
12861
  inline void Object::freeze()
12221
12862
  {
12222
- detail::protect(rb_obj_freeze, value());
12863
+ detail::protect(rb_obj_freeze, this->validated_value());
12223
12864
  }
12224
12865
 
12225
12866
  inline bool Object::is_frozen() const
12226
12867
  {
12227
- return RB_OBJ_FROZEN(value());
12868
+ return RB_OBJ_FROZEN(this->validated_value());
12228
12869
  }
12229
12870
 
12230
12871
  inline int Object::rb_type() const
12231
12872
  {
12232
- return ::rb_type(this->value());
12873
+ return ::rb_type(this->validated_value());
12233
12874
  }
12234
12875
 
12235
12876
  inline VALUE Object::object_id() const
12236
12877
  {
12237
- return detail::protect(rb_obj_id, this->value());
12878
+ return detail::protect(rb_obj_id, this->validated_value());
12238
12879
  }
12239
12880
 
12240
12881
  inline bool Object::is_a(Object klass) const
12241
12882
  {
12242
- VALUE result = detail::protect(rb_obj_is_kind_of, this->value(), klass.value());
12883
+ VALUE result = detail::protect(rb_obj_is_kind_of, this->validated_value(), klass.validated_value());
12243
12884
  return RB_TEST(result);
12244
12885
  }
12245
12886
 
12246
12887
  inline void Object::extend(Module const& mod)
12247
12888
  {
12248
- detail::protect(rb_extend_object, this->value(), mod.value());
12889
+ detail::protect(rb_extend_object, this->validated_value(), mod.validated_value());
12249
12890
  }
12250
12891
 
12251
12892
  inline bool Object::respond_to(Identifier id) const
12252
12893
  {
12253
- return bool(rb_respond_to(this->value(), id.id()));
12894
+ return bool(rb_respond_to(this->validated_value(), id.id()));
12254
12895
  }
12255
12896
 
12256
12897
  inline bool Object::is_instance_of(Object klass) const
12257
12898
  {
12258
- VALUE result = detail::protect(rb_obj_is_instance_of, this->value(), klass.value());
12899
+ VALUE result = detail::protect(rb_obj_is_instance_of, this->validated_value(), klass.validated_value());
12259
12900
  return RB_TEST(result);
12260
12901
  }
12261
12902
 
12262
12903
  inline Object Object::iv_get(Identifier name) const
12263
12904
  {
12264
- return detail::protect(rb_ivar_get, this->value(), name.id());
12905
+ return detail::protect(rb_ivar_get, this->validated_value(), name.id());
12265
12906
  }
12266
12907
 
12267
12908
  inline Object Object::attr_get(Identifier name) const
12268
12909
  {
12269
- return detail::protect(rb_attr_get, this->value(), name.id());
12910
+ return detail::protect(rb_attr_get, this->validated_value(), name.id());
12270
12911
  }
12271
12912
 
12272
- inline void Object::set_value(VALUE v)
12913
+ inline void Object::set_value(VALUE value)
12273
12914
  {
12274
- value_ = v;
12915
+ this->value_ = Pin(value);
12275
12916
  }
12276
12917
 
12277
12918
  inline Object Object::const_get(Identifier name) const
12278
12919
  {
12279
- return detail::protect(rb_const_get, this->value(), name.id());
12920
+ return detail::protect(rb_const_get, this->validated_value(), name.id());
12280
12921
  }
12281
12922
 
12282
12923
  inline bool Object::const_defined(Identifier name) const
12283
12924
  {
12284
- size_t result = detail::protect(rb_const_defined, this->value(), name.id());
12925
+ size_t result = detail::protect(rb_const_defined, this->validated_value(), name.id());
12285
12926
  return bool(result);
12286
12927
  }
12287
12928
 
12288
12929
  inline Object Object::const_set(Identifier name, Object value)
12289
12930
  {
12290
- detail::protect(rb_const_set, this->value(), name.id(), value.value());
12931
+ // We will allow setting constants to Qnil, or the decimal value of 4. This happens
12932
+ // in C++ libraries with enums. Thus use value() instead of validated_value
12933
+ detail::protect(rb_const_set, this->validated_value(), name.id(), value.value());
12291
12934
  return value;
12292
12935
  }
12293
12936
 
@@ -12302,13 +12945,12 @@ namespace Rice4RubyQt6
12302
12945
 
12303
12946
  inline void Object::remove_const(Identifier name)
12304
12947
  {
12305
- detail::protect(rb_mod_remove_const, this->value(), name.to_sym());
12948
+ detail::protect(rb_mod_remove_const, this->validated_value(), name.to_sym());
12306
12949
  }
12307
12950
 
12308
12951
  inline bool operator==(Object const& lhs, Object const& rhs)
12309
12952
  {
12310
- VALUE result = detail::protect(rb_equal, lhs.value(), rhs.value());
12311
- return result == Qtrue ? true : false;
12953
+ return lhs.is_equal(rhs);
12312
12954
  }
12313
12955
 
12314
12956
  inline bool operator!=(Object const& lhs, Object const& rhs)
@@ -12319,13 +12961,13 @@ namespace Rice4RubyQt6
12319
12961
  inline bool operator<(Object const& lhs, Object const& rhs)
12320
12962
  {
12321
12963
  Object result = lhs.call("<", rhs);
12322
- return result.test();
12964
+ return result;
12323
12965
  }
12324
12966
 
12325
12967
  inline bool operator>(Object const& lhs, Object const& rhs)
12326
12968
  {
12327
12969
  Object result = lhs.call(">", rhs);
12328
- return result.test();
12970
+ return result;
12329
12971
  }
12330
12972
  }
12331
12973
 
@@ -12415,73 +13057,36 @@ namespace Rice4RubyQt6::detail
12415
13057
  };
12416
13058
  }
12417
13059
 
12418
- // ========= Builtin_Object.ipp =========
12419
- #include <algorithm>
12420
-
12421
- namespace Rice4RubyQt6
12422
- {
12423
- namespace detail
12424
- {
12425
- inline VALUE check_type(Object value, int type)
12426
- {
12427
- detail::protect(rb_check_type, value.value(), type);
12428
- return Qnil;
12429
- }
12430
- }
12431
-
12432
- template<int Builtin_Type>
12433
- inline Builtin_Object<Builtin_Type>::Builtin_Object(Object value) : Object(value)
12434
- {
12435
- detail::check_type(value, Builtin_Type);
12436
- }
12437
-
12438
- template<int Builtin_Type>
12439
- inline RObject& Builtin_Object<Builtin_Type>::operator*() const
12440
- {
12441
- return *ROBJECT(this->value());
12442
- }
12443
-
12444
- template<int Builtin_Type>
12445
- inline RObject* Builtin_Object<Builtin_Type>::operator->() const
12446
- {
12447
- return ROBJECT(this->value());
12448
- }
12449
-
12450
- template<int Builtin_Type>
12451
- inline RObject* Builtin_Object<Builtin_Type>::get() const
12452
- {
12453
- return ROBJECT(this->value());
12454
- }
12455
- } // namespace Rice4RubyQt6
12456
-
12457
13060
  // ========= String.ipp =========
12458
13061
  namespace Rice4RubyQt6
12459
13062
  {
12460
- inline String::String() : Builtin_Object<T_STRING>(detail::protect(rb_str_new2, ""))
13063
+ inline String::String() : Object(detail::protect(rb_str_new2, ""))
12461
13064
  {
12462
13065
  }
12463
13066
 
12464
- inline String::String(VALUE v) : Builtin_Object<T_STRING>(v)
13067
+ inline String::String(VALUE v) : Object(v)
12465
13068
  {
13069
+ detail::protect(rb_check_type, this->value(), T_STRING);
12466
13070
  }
12467
13071
 
12468
- inline String::String(Object v) : Builtin_Object<T_STRING>(v)
13072
+ inline String::String(Object v) : Object(v)
12469
13073
  {
13074
+ detail::protect(rb_check_type, this->value(), T_STRING);
12470
13075
  }
12471
13076
 
12472
- inline String::String(char const* s) : Builtin_Object<T_STRING>(detail::protect(rb_utf8_str_new_cstr, s))
13077
+ inline String::String(char const* s) : Object(detail::protect(rb_utf8_str_new_cstr, s))
12473
13078
  {
12474
13079
  }
12475
13080
 
12476
- inline String::String(std::string const& s) : Builtin_Object<T_STRING>(detail::protect(rb_utf8_str_new, s.data(), (long)s.length()))
13081
+ inline String::String(std::string const& s) : Object(detail::protect(rb_utf8_str_new, s.data(), (long)s.length()))
12477
13082
  {
12478
13083
  }
12479
13084
 
12480
- inline String::String(std::string_view const& s) : Builtin_Object<T_STRING>(detail::protect(rb_utf8_str_new, s.data(), (long)s.length()))
13085
+ inline String::String(std::string_view const& s) : Object(detail::protect(rb_utf8_str_new, s.data(), (long)s.length()))
12481
13086
  {
12482
13087
  }
12483
13088
 
12484
- inline String::String(Identifier id) : Builtin_Object<T_STRING>(detail::protect(rb_utf8_str_new_cstr, id.c_str()))
13089
+ inline String::String(Identifier id) : Object(detail::protect(rb_utf8_str_new_cstr, id.c_str()))
12485
13090
  {
12486
13091
  }
12487
13092
 
@@ -12502,22 +13107,22 @@ namespace Rice4RubyQt6
12502
13107
 
12503
13108
  inline size_t String::length() const
12504
13109
  {
12505
- return RSTRING_LEN(value());
13110
+ return RSTRING_LEN(this->value());
12506
13111
  }
12507
13112
 
12508
13113
  inline char String::operator[](ptrdiff_t index) const
12509
13114
  {
12510
- return RSTRING_PTR(value())[index];
13115
+ return RSTRING_PTR(this->value())[index];
12511
13116
  }
12512
13117
 
12513
13118
  inline char const* String::c_str() const
12514
13119
  {
12515
- return RSTRING_PTR(value());
13120
+ return RSTRING_PTR(this->value());
12516
13121
  }
12517
13122
 
12518
13123
  inline std::string String::str() const
12519
13124
  {
12520
- return std::string(RSTRING_PTR(value()), length());
13125
+ return std::string(RSTRING_PTR(this->value()), length());
12521
13126
  }
12522
13127
 
12523
13128
  template<typename T>
@@ -12598,28 +13203,31 @@ namespace Rice4RubyQt6::detail
12598
13203
  Arg* arg_ = nullptr;
12599
13204
  };
12600
13205
  }
13206
+
12601
13207
  // ========= Array.ipp =========
12602
13208
 
12603
13209
  namespace Rice4RubyQt6
12604
13210
  {
12605
- inline Array::Array() : Builtin_Object<T_ARRAY>(detail::protect(rb_ary_new))
13211
+ inline Array::Array() : Object(detail::protect(rb_ary_new))
12606
13212
  {
12607
13213
  }
12608
13214
 
12609
- inline Array::Array(long capacity) : Builtin_Object<T_ARRAY>(detail::protect(rb_ary_new_capa, capacity))
13215
+ inline Array::Array(long capacity) : Object(detail::protect(rb_ary_new_capa, capacity))
12610
13216
  {
12611
13217
  }
12612
13218
 
12613
- inline Array::Array(Object v) : Builtin_Object<T_ARRAY>(v)
13219
+ inline Array::Array(Object v) : Object(v)
12614
13220
  {
13221
+ detail::protect(rb_check_type, this->value(), T_ARRAY);
12615
13222
  }
12616
13223
 
12617
- inline Array::Array(VALUE v) : Builtin_Object<T_ARRAY>(v)
13224
+ inline Array::Array(VALUE v) : Object(v)
12618
13225
  {
13226
+ detail::protect(rb_check_type, this->value(), T_ARRAY);
12619
13227
  }
12620
13228
 
12621
13229
  template<typename Iter_T>
12622
- inline Array::Array(Iter_T it, Iter_T end) : Builtin_Object<T_ARRAY>(detail::protect(rb_ary_new))
13230
+ inline Array::Array(Iter_T it, Iter_T end) : Object(detail::protect(rb_ary_new))
12623
13231
  {
12624
13232
  for (; it != end; ++it)
12625
13233
  {
@@ -12628,7 +13236,7 @@ namespace Rice4RubyQt6
12628
13236
  }
12629
13237
 
12630
13238
  template<typename T, long n>
12631
- inline Array::Array(T (&a)[n]) : Builtin_Object<T_ARRAY>(detail::protect(rb_ary_new))
13239
+ inline Array::Array(T (&a)[n]) : Object(detail::protect(rb_ary_new))
12632
13240
  {
12633
13241
  for (long j = 0; j < n; ++j)
12634
13242
  {
@@ -12733,9 +13341,9 @@ namespace Rice4RubyQt6
12733
13341
  return detail::protect(rb_ary_entry, array_.value(), index_);
12734
13342
  }
12735
13343
 
12736
- inline Array::Proxy::operator Object() const
13344
+ inline Array::Proxy::operator VALUE() const
12737
13345
  {
12738
- return Object(this->value());
13346
+ return this->value();
12739
13347
  }
12740
13348
 
12741
13349
  template<typename T>
@@ -12793,7 +13401,7 @@ namespace Rice4RubyQt6
12793
13401
  template<typename Array_Ptr_T, typename Value_T>
12794
13402
  inline Object* Array::Iterator<Array_Ptr_T, Value_T>::operator->()
12795
13403
  {
12796
- tmp_ = (*array_)[index_];
13404
+ tmp_ = Object((*array_)[index_]);
12797
13405
  return &tmp_;
12798
13406
  }
12799
13407
 
@@ -13047,12 +13655,13 @@ namespace Rice4RubyQt6::detail
13047
13655
 
13048
13656
  namespace Rice4RubyQt6
13049
13657
  {
13050
- inline Hash::Hash() : Builtin_Object<T_HASH>(detail::protect(rb_hash_new))
13658
+ inline Hash::Hash() : Object(detail::protect(rb_hash_new))
13051
13659
  {
13052
13660
  }
13053
13661
 
13054
- inline Hash::Hash(Object v) : Builtin_Object<T_HASH>(v)
13662
+ inline Hash::Hash(Object v) : Object(v)
13055
13663
  {
13664
+ detail::protect(rb_check_type, this->value(), T_HASH);
13056
13665
  }
13057
13666
 
13058
13667
  inline size_t Hash::size() const
@@ -13064,7 +13673,7 @@ namespace Rice4RubyQt6
13064
13673
  {
13065
13674
  }
13066
13675
 
13067
- inline Hash::Proxy::operator Object() const
13676
+ inline Hash::Proxy::operator VALUE() const
13068
13677
  {
13069
13678
  return value();
13070
13679
  }
@@ -13096,7 +13705,7 @@ namespace Rice4RubyQt6
13096
13705
  inline Value_T Hash::get(Key_T const& key)
13097
13706
  {
13098
13707
  Object ruby_key(detail::To_Ruby<Key_T>().convert(key));
13099
- Object value = operator[](ruby_key);
13708
+ Object value(operator[](ruby_key));
13100
13709
  try
13101
13710
  {
13102
13711
  return detail::From_Ruby<Value_T>().convert(value);
@@ -13200,7 +13809,7 @@ namespace Rice4RubyQt6
13200
13809
  template<typename Hash_Ptr_T, typename Value_T>
13201
13810
  inline Object Hash::Iterator<Hash_Ptr_T, Value_T>::current_key()
13202
13811
  {
13203
- return hash_keys()[current_index_];
13812
+ return Object(hash_keys()[current_index_]);
13204
13813
  }
13205
13814
 
13206
13815
  template<typename Hash_Ptr_T, typename Value_T>
@@ -13467,10 +14076,6 @@ namespace Rice4RubyQt6::detail
13467
14076
 
13468
14077
  namespace Rice4RubyQt6
13469
14078
  {
13470
- inline Module::Module() : Object(rb_cObject)
13471
- {
13472
- }
13473
-
13474
14079
  inline Module::Module(VALUE value) : Object(value)
13475
14080
  {
13476
14081
  if (::rb_type(value) != T_CLASS && ::rb_type(value) != T_MODULE)
@@ -13482,7 +14087,7 @@ namespace Rice4RubyQt6
13482
14087
  }
13483
14088
  }
13484
14089
 
13485
- //! Construct a Module from an string that references a Module
14090
+ //! Construct a Module from a string that references a Module
13486
14091
  inline Module::Module(std::string name, Object under)
13487
14092
  {
13488
14093
  VALUE result = under.const_get(name);
@@ -13808,7 +14413,7 @@ namespace Rice4RubyQt6
13808
14413
 
13809
14414
  //! An instance of a Struct
13810
14415
  //! \sa Struct
13811
- class Struct::Instance : public Builtin_Object<T_STRUCT>
14416
+ class Struct::Instance : public Object
13812
14417
  {
13813
14418
  public:
13814
14419
  //! Create a new Instance of a Struct.
@@ -13859,7 +14464,7 @@ namespace Rice4RubyQt6
13859
14464
 
13860
14465
  inline Struct& Struct::define_member(Identifier name)
13861
14466
  {
13862
- if (value() != rb_cObject)
14467
+ if (!this->is_nil())
13863
14468
  {
13864
14469
  throw std::runtime_error("struct is already initialized");
13865
14470
  }
@@ -13871,7 +14476,7 @@ namespace Rice4RubyQt6
13871
14476
 
13872
14477
  inline Array Struct::members() const
13873
14478
  {
13874
- if (value() == rb_cObject)
14479
+ if (this->is_nil())
13875
14480
  {
13876
14481
  // Struct is not yet defined
13877
14482
  return Array(members_.begin(), members_.end());
@@ -13890,13 +14495,15 @@ namespace Rice4RubyQt6
13890
14495
  }
13891
14496
 
13892
14497
  inline Struct::Instance::Instance(Struct const& type, Array args) :
13893
- Builtin_Object<T_STRUCT>(type.new_instance(args)), type_(type)
14498
+ Object(type.new_instance(args)), type_(type)
13894
14499
  {
14500
+ detail::protect(rb_check_type, this->value(), T_STRUCT);
13895
14501
  }
13896
14502
 
13897
14503
  inline Struct::Instance::Instance(Struct const& type, Object s) :
13898
- Builtin_Object<T_STRUCT>(s), type_(type)
14504
+ Object(s), type_(type)
13899
14505
  {
14506
+ detail::protect(rb_check_type, this->value(), T_STRUCT);
13900
14507
  }
13901
14508
 
13902
14509
  inline Struct define_struct()
@@ -13940,162 +14547,6 @@ namespace Rice4RubyQt6::detail
13940
14547
  };
13941
14548
  }
13942
14549
 
13943
- // ========= Address_Registration_Guard.hpp =========
13944
-
13945
- namespace Rice4RubyQt6
13946
- {
13947
- //! A guard to register a given address with the GC.
13948
- /*! Calls rb_gc_register_address upon construction and
13949
- * rb_gc_unregister_address upon destruction.
13950
- * For example:
13951
- * \code
13952
- * Class Foo
13953
- * {
13954
- * public:
13955
- * Foo()
13956
- * : string_(rb_str_new2())
13957
- * , guard_(&string_);
13958
- *
13959
- * private:
13960
- * VALUE string_;
13961
- * Address_Registration_Guard guard_;
13962
- * };
13963
- * \endcode
13964
- */
13965
- class Address_Registration_Guard
13966
- {
13967
- public:
13968
- //! Register an address with the GC.
13969
- /* \param address The address to register with the GC. The address
13970
- * must point to a valid ruby object (RObject).
13971
- */
13972
- Address_Registration_Guard(VALUE* address);
13973
-
13974
- //! Register an Object with the GC.
13975
- /*! \param object The Object to register with the GC. The object must
13976
- * not be destroyed before the Address_Registration_Guard is
13977
- * destroyed.
13978
- */
13979
- Address_Registration_Guard(Object* object);
13980
-
13981
- //! Unregister an address/Object with the GC.
13982
- /*! Destruct an Address_Registration_Guard. The address registered
13983
- * with the Address_Registration_Guard when it was constructed will
13984
- * be unregistered from the GC.
13985
- */
13986
- ~Address_Registration_Guard();
13987
-
13988
- // Disable copying
13989
- Address_Registration_Guard(Address_Registration_Guard const& other) = delete;
13990
- Address_Registration_Guard& operator=(Address_Registration_Guard const& other) = delete;
13991
-
13992
- // Enable moving
13993
- Address_Registration_Guard(Address_Registration_Guard&& other);
13994
- Address_Registration_Guard& operator=(Address_Registration_Guard&& other);
13995
-
13996
- //! Get the address that is registered with the GC.
13997
- VALUE* address() const;
13998
-
13999
- /** Called during Ruby's exit process since we should not call
14000
- * rb_gc unregister_address there
14001
- */
14002
- static void disable();
14003
-
14004
- private:
14005
- inline static bool enabled = true;
14006
- inline static bool exit_handler_registered = false;
14007
- static void registerExitHandler();
14008
-
14009
- private:
14010
- void registerAddress() const;
14011
- void unregisterAddress();
14012
-
14013
- VALUE* address_ = nullptr;
14014
- };
14015
- } // namespace Rice4RubyQt6
14016
-
14017
-
14018
- // ========= Address_Registration_Guard.ipp =========
14019
- namespace Rice4RubyQt6
14020
- {
14021
- inline Address_Registration_Guard::Address_Registration_Guard(VALUE* address) : address_(address)
14022
- {
14023
- registerExitHandler();
14024
- registerAddress();
14025
- }
14026
-
14027
- inline Address_Registration_Guard::Address_Registration_Guard(Object* object)
14028
- : address_(const_cast<VALUE*>(&object->value()))
14029
- {
14030
- registerExitHandler();
14031
- registerAddress();
14032
- }
14033
-
14034
- inline Address_Registration_Guard::~Address_Registration_Guard()
14035
- {
14036
- unregisterAddress();
14037
- }
14038
-
14039
- inline Address_Registration_Guard::Address_Registration_Guard(Address_Registration_Guard&& other)
14040
- {
14041
- // We don't use the constructor because we don't want to double register this address
14042
- address_ = other.address_;
14043
- other.address_ = nullptr;
14044
- }
14045
-
14046
- inline Address_Registration_Guard& Address_Registration_Guard::operator=(Address_Registration_Guard&& other)
14047
- {
14048
- this->unregisterAddress();
14049
-
14050
- this->address_ = other.address_;
14051
- other.address_ = nullptr;
14052
- return *this;
14053
- }
14054
-
14055
- inline void Address_Registration_Guard::registerAddress() const
14056
- {
14057
- if (enabled)
14058
- {
14059
- detail::protect(rb_gc_register_address, address_);
14060
- }
14061
- }
14062
-
14063
- inline void Address_Registration_Guard::unregisterAddress()
14064
- {
14065
- if (enabled && address_)
14066
- {
14067
- detail::protect(rb_gc_unregister_address, address_);
14068
- }
14069
-
14070
- address_ = nullptr;
14071
- }
14072
-
14073
- inline VALUE* Address_Registration_Guard::address() const
14074
- {
14075
- return address_;
14076
- }
14077
-
14078
- static void disable_all_guards(VALUE)
14079
- {
14080
- Address_Registration_Guard::disable();
14081
- }
14082
-
14083
- inline void Address_Registration_Guard::registerExitHandler()
14084
- {
14085
- if (exit_handler_registered)
14086
- {
14087
- return;
14088
- }
14089
-
14090
- detail::protect(rb_set_end_proc, &disable_all_guards, Qnil);
14091
- exit_handler_registered = true;
14092
- }
14093
-
14094
- inline void Address_Registration_Guard::disable()
14095
- {
14096
- enabled = false;
14097
- }
14098
- } // Rice
14099
14550
  // ========= global_function.hpp =========
14100
14551
 
14101
14552
  namespace Rice4RubyQt6
@@ -14166,9 +14617,7 @@ namespace Rice4RubyQt6
14166
14617
  public:
14167
14618
  //! Construct new Director. Needs the Ruby object so that the
14168
14619
  // proxy class can call methods on that object.
14169
- Director(Object self) : self_(self)
14170
- {
14171
- }
14620
+ Director(Object self);
14172
14621
 
14173
14622
  virtual ~Director() = default;
14174
14623
 
@@ -14177,13 +14626,10 @@ namespace Rice4RubyQt6
14177
14626
  * method, use this method to throw an exception in this case.
14178
14627
  */
14179
14628
  [[noreturn]]
14180
- void raisePureVirtual() const
14181
- {
14182
- rb_raise(rb_eNotImpError, "Cannot call super() into a pure-virtual C++ method");
14183
- }
14629
+ void raisePureVirtual() const;
14184
14630
 
14185
14631
  //! Get the Ruby object linked to this C++ instance
14186
- Object getSelf() const { return self_; }
14632
+ Object getSelf() const;
14187
14633
 
14188
14634
  private:
14189
14635
 
@@ -14193,6 +14639,25 @@ namespace Rice4RubyQt6
14193
14639
  };
14194
14640
  }
14195
14641
 
14642
+ // ========= Director.ipp =========
14643
+
14644
+ namespace Rice4RubyQt6
14645
+ {
14646
+ inline Director::Director(Object self) : self_(self)
14647
+ {
14648
+ }
14649
+
14650
+ inline void Director::raisePureVirtual() const
14651
+ {
14652
+ rb_raise(rb_eNotImpError, "Cannot call super() into a pure-virtual C++ method");
14653
+ }
14654
+
14655
+ inline Object Director::getSelf() const
14656
+ {
14657
+ return self_;
14658
+ }
14659
+ }
14660
+
14196
14661
  // ========= Data_Type.ipp =========
14197
14662
  #include <stdexcept>
14198
14663
 
@@ -14201,16 +14666,13 @@ namespace Rice4RubyQt6
14201
14666
  template<typename T>
14202
14667
  inline void ruby_mark_internal(detail::WrapperBase* wrapper)
14203
14668
  {
14204
- detail::cpp_protect([&]
14205
- {
14206
- // Tell the wrapper to mark the objects its keeping alive
14207
- wrapper->ruby_mark();
14669
+ // Tell the wrapper to mark the objects its keeping alive
14670
+ wrapper->ruby_mark();
14208
14671
 
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
- });
14672
+ // Get the underlying data and call custom mark function (if any)
14673
+ // Use the wrapper's stored rb_data_type to avoid type mismatch
14674
+ T* data = static_cast<T*>(wrapper->get(Data_Type<T>::ruby_data_type()));
14675
+ ruby_mark<T>(data);
14214
14676
  }
14215
14677
 
14216
14678
  template<typename T>
@@ -14223,7 +14685,14 @@ namespace Rice4RubyQt6
14223
14685
  template<typename T>
14224
14686
  inline size_t ruby_size_internal(const T*)
14225
14687
  {
14226
- return sizeof(T);
14688
+ if constexpr (detail::is_complete_v<T>)
14689
+ {
14690
+ return sizeof(T);
14691
+ }
14692
+ else
14693
+ {
14694
+ return 0;
14695
+ }
14227
14696
  }
14228
14697
 
14229
14698
  template<>
@@ -14234,17 +14703,19 @@ namespace Rice4RubyQt6
14234
14703
 
14235
14704
  template<typename T>
14236
14705
  template <typename Base_T>
14237
- inline Data_Type<T> Data_Type<T>::bind(const Module& klass)
14706
+ inline Data_Type<T> Data_Type<T>::bind(const Module& klass, rb_data_type_t *data_type)
14238
14707
  {
14239
14708
  if (is_bound())
14240
14709
  {
14241
- detail::TypeIndexParser typeIndexParser(typeid(T), std::is_fundamental_v<detail::intrinsic_type<T>>);
14242
- std::string message = "Type " + typeIndexParser.name() + " is already bound to a different type";
14710
+ std::string message = "Type " + detail::TypeDetail<T>().name() + " is already bound to a different type";
14243
14711
  throw std::runtime_error(message.c_str());
14244
14712
  }
14245
14713
 
14246
14714
  klass_ = klass;
14247
14715
 
14716
+ if (data_type) {
14717
+ rb_data_type_ = data_type;
14718
+ } else {
14248
14719
  rb_data_type_ = new rb_data_type_t();
14249
14720
  rb_data_type_->wrap_struct_name = strdup(Rice4RubyQt6::detail::protect(rb_class2name, klass_));
14250
14721
  rb_data_type_->function.dmark = reinterpret_cast<void(*)(void*)>(&Rice4RubyQt6::ruby_mark_internal<T>);
@@ -14257,6 +14728,7 @@ namespace Rice4RubyQt6
14257
14728
  {
14258
14729
  rb_data_type_->parent = Data_Type<Base_T>::ruby_data_type();
14259
14730
  }
14731
+ }
14260
14732
 
14261
14733
  auto instances = unbound_instances();
14262
14734
  for (auto instance: instances)
@@ -14270,15 +14742,32 @@ namespace Rice4RubyQt6
14270
14742
 
14271
14743
  // Add a method to get the source C++ class name from Ruby
14272
14744
  Data_Type<T> dataType;
14273
- dataType.define_singleton_method("cpp_class", [](VALUE) -> VALUE
14745
+ if constexpr (detail::is_complete_v<T>)
14274
14746
  {
14275
- detail::TypeIndexParser typeIndexParser(typeid(T), std::is_fundamental_v<detail::intrinsic_type<T>>);
14276
- std::string cppClassName = typeIndexParser.simplifiedName();
14277
- Return returnInfo;
14278
- returnInfo.takeOwnership();
14279
- return detail::To_Ruby<char*>(&returnInfo).convert(cppClassName.c_str());
14280
- }, Arg("klass").setValue(), Return().setValue());
14747
+ dataType.define_singleton_method("cpp_class", [](VALUE) -> VALUE
14748
+ {
14749
+ Return returnInfo;
14750
+ returnInfo.takeOwnership();
14751
+
14752
+ detail::TypeDetail<T> typeDetail;
14753
+ std::string cppClassName = typeDetail.simplifiedName();
14754
+
14755
+ return detail::To_Ruby<char*>(&returnInfo).convert(cppClassName.c_str());
14756
+ }, Arg("klass").setValue(), Return().setValue());
14757
+ }
14758
+ else
14759
+ {
14760
+ VALUE klass_value = klass.value();
14761
+ dataType.define_singleton_method("cpp_class", [klass_value](VALUE) -> VALUE
14762
+ {
14763
+ Return returnInfo;
14764
+ returnInfo.takeOwnership();
14281
14765
 
14766
+ Rice4RubyQt6::String cppClassName = detail::protect(rb_class_path, klass_value);
14767
+
14768
+ return detail::To_Ruby<char*>(&returnInfo).convert(cppClassName.c_str());
14769
+ }, Arg("klass").setValue(), Return().setValue());
14770
+ }
14282
14771
  return dataType;
14283
14772
  }
14284
14773
 
@@ -14368,8 +14857,23 @@ namespace Rice4RubyQt6
14368
14857
 
14369
14858
  if constexpr (Constructor_T::isCopyConstructor())
14370
14859
  {
14371
- // Define initialize_copy that will copy the C++ object
14372
- this->define_method("initialize_copy", &Constructor_T::initialize_copy, args...);
14860
+ // Define initialize_copy that will copy the C++ object and its keepAlive references.
14861
+ // We use setValue() so Rice passes the raw VALUE without conversion - this gives
14862
+ // initialize_copy access to both wrappers so it can copy the keepAlive list.
14863
+ using Rice_Arg_Tuple = std::tuple<Rice_Arg_Ts...>;
14864
+ constexpr std::size_t arg_index = detail::tuple_element_index_v<Rice_Arg_Tuple, Arg>;
14865
+
14866
+ if constexpr (arg_index < sizeof...(Rice_Arg_Ts))
14867
+ {
14868
+ // User provided an Arg - extract it and ensure setValue is set
14869
+ Arg arg = std::get<arg_index>(std::forward_as_tuple(args...));
14870
+ arg.setValue();
14871
+ this->define_method("initialize_copy", &Constructor_T::initialize_copy, arg);
14872
+ }
14873
+ else
14874
+ {
14875
+ this->define_method("initialize_copy", &Constructor_T::initialize_copy, Arg("other").setValue());
14876
+ }
14373
14877
  }
14374
14878
  else if constexpr (Constructor_T::isMoveConstructor())
14375
14879
  {
@@ -14384,14 +14888,6 @@ namespace Rice4RubyQt6
14384
14888
  return *this;
14385
14889
  }
14386
14890
 
14387
- template<typename T>
14388
- template<typename Function_T>
14389
- inline Data_Type<T>& Data_Type<T>::define(Function_T func)
14390
- {
14391
- func(*this);
14392
- return *this;
14393
- }
14394
-
14395
14891
  template<typename T>
14396
14892
  template<typename Director_T>
14397
14893
  inline Data_Type<T>& Data_Type<T>::define_director()
@@ -14431,8 +14927,7 @@ namespace Rice4RubyQt6
14431
14927
  {
14432
14928
  if (!is_bound())
14433
14929
  {
14434
- detail::TypeIndexParser typeIndexParser(typeid(T), std::is_fundamental_v<detail::intrinsic_type<T>>);
14435
- std::string message = "Type is not defined with Rice: " + typeIndexParser.name();
14930
+ std::string message = "Type is not defined with Rice: " + detail::TypeDetail<T>().name();
14436
14931
  throw std::invalid_argument(message.c_str());
14437
14932
  }
14438
14933
  }
@@ -14469,7 +14964,7 @@ namespace Rice4RubyQt6
14469
14964
  }
14470
14965
  else
14471
14966
  {
14472
- // This gives a chance for to auto-register classes such as std::exception
14967
+ // This gives a chance to auto-register classes such as std::exception
14473
14968
  detail::verifyType<Base_T>();
14474
14969
  result = Data_Type<Base_T>::klass();
14475
14970
  }
@@ -14514,6 +15009,19 @@ namespace Rice4RubyQt6
14514
15009
  return Data_Type<T>::template bind<Base_T>(klass);
14515
15010
  }
14516
15011
 
15012
+ template<typename T, typename Base_T>
15013
+ Data_Type<T> declare_class_under(Object parent, char const* name, rb_data_type_t *data_type)
15014
+ {
15015
+ Identifier id(name);
15016
+ if (Rice4RubyQt6::Data_Type<T>::check_defined(id.str(), parent))
15017
+ {
15018
+ return Data_Type<T>();
15019
+ }
15020
+
15021
+ Class klass = parent.const_get(id).value();
15022
+ return Data_Type<T>::template bind<Base_T>(klass, data_type);
15023
+ }
15024
+
14517
15025
  template<typename T>
14518
15026
  template<typename Iterator_Func_T>
14519
15027
  inline Data_Type<T>& Data_Type<T>::define_iterator(Iterator_Func_T begin, Iterator_Func_T end, std::string name)
@@ -14528,61 +15036,34 @@ namespace Rice4RubyQt6
14528
15036
  }
14529
15037
 
14530
15038
  template <typename T>
14531
- template <typename Attribute_T, typename...Arg_Ts>
14532
- inline Data_Type<T>& Data_Type<T>::define_attr(std::string name, Attribute_T attribute, AttrAccess access, const Arg_Ts&...args)
15039
+ template <typename Attribute_T, typename Access_T, typename...Arg_Ts>
15040
+ inline Data_Type<T>& Data_Type<T>::define_attr(std::string name, Attribute_T attribute, Access_T access, const Arg_Ts&...args)
14533
15041
  {
14534
- return this->define_attr_internal<Attribute_T>(this->klass_, name, std::forward<Attribute_T>(attribute), access, args...);
15042
+ return this->define_attr_internal<Attribute_T, Access_T>(this->klass_, name, std::forward<Attribute_T>(attribute), access, args...);
14535
15043
  }
14536
15044
 
14537
15045
  template <typename T>
14538
- template <typename Attribute_T, typename...Arg_Ts>
14539
- inline Data_Type<T>& Data_Type<T>::define_singleton_attr(std::string name, Attribute_T attribute, AttrAccess access, const Arg_Ts&...args)
15046
+ template <typename Attribute_T, typename Access_T, typename...Arg_Ts>
15047
+ inline Data_Type<T>& Data_Type<T>::define_singleton_attr(std::string name, Attribute_T attribute, Access_T access, const Arg_Ts&...args)
14540
15048
  {
14541
- VALUE singleton = detail::protect(rb_singleton_class, this->value());
14542
- return this->define_attr_internal<Attribute_T>(singleton, name, std::forward<Attribute_T>(attribute), access, args...);
15049
+ VALUE singleton = detail::protect(rb_singleton_class, this->validated_value());
15050
+ return this->define_attr_internal<Attribute_T, Access_T>(singleton, name, std::forward<Attribute_T>(attribute), access, args...);
14543
15051
  }
14544
15052
 
14545
15053
  template <typename T>
14546
- template <typename Attribute_T, typename...Arg_Ts>
14547
- inline Data_Type<T>& Data_Type<T>::define_attr_internal(VALUE klass, std::string name, Attribute_T attribute, AttrAccess access, const Arg_Ts&...args)
15054
+ template <typename Attribute_T, typename Access_T, typename...Arg_Ts>
15055
+ inline Data_Type<T>& Data_Type<T>::define_attr_internal(VALUE klass, std::string name, Attribute_T attribute, Access_T, const Arg_Ts&...args)
14548
15056
  {
14549
- using Attr_T = typename detail::attribute_traits<Attribute_T>::attr_type;
14550
-
14551
15057
  // Define attribute getter
14552
- if (access == AttrAccess::ReadWrite || access == AttrAccess::Read)
15058
+ if constexpr (std::is_same_v<Access_T, AttrAccess::ReadWriteType> || std::is_same_v<Access_T, AttrAccess::ReadType>)
14553
15059
  {
14554
15060
  detail::NativeAttributeGet<Attribute_T>::define(klass, name, std::forward<Attribute_T>(attribute), args...);
14555
15061
  }
14556
15062
 
14557
15063
  // Define attribute setter
14558
- // Define attribute setter
14559
- if (access == AttrAccess::ReadWrite || access == AttrAccess::Write)
15064
+ if constexpr (std::is_same_v<Access_T, AttrAccess::ReadWriteType> || std::is_same_v<Access_T, AttrAccess::WriteType>)
14560
15065
  {
14561
- // This seems super hacky - must be a better way?
14562
- constexpr bool checkWriteAccess = !std::is_reference_v<Attr_T> &&
14563
- !std::is_pointer_v<Attr_T> &&
14564
- !std::is_fundamental_v<Attr_T> &&
14565
- !std::is_enum_v<Attr_T>;
14566
-
14567
- if constexpr (std::is_const_v<Attr_T>)
14568
- {
14569
- throw std::runtime_error("Cannot define attribute writer for a const attribute: " + name);
14570
- }
14571
- // Attributes are set using assignment operator like this:
14572
- // myInstance.attribute = newvalue
14573
- else if constexpr (checkWriteAccess && !std::is_assignable_v<Attr_T, Attr_T>)
14574
- {
14575
- throw std::runtime_error("Cannot define attribute writer for a non assignable attribute: " + name);
14576
- }
14577
- // From_Ruby returns a copy of the value for non-reference and non-pointers, thus needs to be copy constructable
14578
- else if constexpr (checkWriteAccess && !std::is_copy_constructible_v<Attr_T>)
14579
- {
14580
- throw std::runtime_error("Cannot define attribute writer for a non copy constructible attribute: " + name);
14581
- }
14582
- else
14583
- {
14584
- detail::NativeAttributeSet<Attribute_T>::define(klass, name, std::forward<Attribute_T>(attribute), args...);
14585
- }
15066
+ detail::NativeAttributeSet<Attribute_T>::define(klass, name, std::forward<Attribute_T>(attribute), args...);
14586
15067
  }
14587
15068
 
14588
15069
  return *this;
@@ -14655,11 +15136,14 @@ namespace Rice4RubyQt6
14655
15136
  detail::wrapConstructed<T>(self, Data_Type<T>::ruby_data_type(), data);
14656
15137
  }
14657
15138
 
14658
- static void initialize_copy(VALUE self, const T& other)
15139
+ // Takes VALUE (via Arg.setValue()) instead of const T& so we have access to
15140
+ // both Ruby wrappers and can copy the keepAlive list from the original to the clone.
15141
+ static VALUE initialize_copy(VALUE self, VALUE other)
14659
15142
  {
14660
- // Call C++ copy constructor
14661
- T* data = new T(other);
14662
- detail::wrapConstructed<T>(self, Data_Type<T>::ruby_data_type(), data);
15143
+ T* otherData = detail::unwrap<T>(other, Data_Type<T>::ruby_data_type(), false);
15144
+ T* data = new T(*otherData);
15145
+ detail::wrapConstructed<T>(self, Data_Type<T>::ruby_data_type(), data, other);
15146
+ return self;
14663
15147
  }
14664
15148
 
14665
15149
  };
@@ -14733,9 +15217,8 @@ namespace Rice4RubyQt6
14733
15217
  }
14734
15218
  else
14735
15219
  {
14736
- detail::TypeIndexParser typeIndexParser(typeid(T), std::is_fundamental_v<detail::intrinsic_type<T>>);
14737
15220
  return Exception(rb_eTypeError, "Wrong argument type. Expected %s. Received %s.",
14738
- typeIndexParser.simplifiedName().c_str(),
15221
+ detail::TypeDetail<T>().name().c_str(),
14739
15222
  detail::protect(rb_obj_classname, value));
14740
15223
  }
14741
15224
  }
@@ -14804,7 +15287,7 @@ namespace Rice4RubyQt6
14804
15287
  template<typename T>
14805
15288
  inline T* Data_Object<T>::get() const
14806
15289
  {
14807
- if (this->value() == Qnil)
15290
+ if (this->is_nil())
14808
15291
  {
14809
15292
  return nullptr;
14810
15293
  }
@@ -15234,10 +15717,14 @@ namespace Rice4RubyQt6::detail
15234
15717
  {
15235
15718
  return Convertible::Exact;
15236
15719
  }
15237
- else if (Data_Type<Pointer_T>::is_descendant(value))
15720
+ else if (Data_Type<Pointer_T>::is_descendant(value) && isBuffer)
15238
15721
  {
15239
15722
  return Convertible::Exact;
15240
15723
  }
15724
+ else if (Data_Type<Pointer_T>::is_descendant(value) && !isBuffer)
15725
+ {
15726
+ return Convertible::Exact * 0.99;
15727
+ }
15241
15728
  [[fallthrough]];
15242
15729
  default:
15243
15730
  return Convertible::None;
@@ -15354,15 +15841,13 @@ namespace Rice4RubyQt6::detail
15354
15841
 
15355
15842
  double is_convertible(VALUE value)
15356
15843
  {
15357
- bool isBuffer = dynamic_cast<ArgBuffer*>(this->arg_) ? true : false;
15358
-
15359
15844
  switch (rb_type(value))
15360
15845
  {
15361
15846
  case RUBY_T_NIL:
15362
15847
  return Convertible::Exact;
15363
15848
  break;
15364
15849
  case RUBY_T_DATA:
15365
- if (Data_Type<Pointer_T>::is_descendant(value) && isBuffer)
15850
+ if (Data_Type<Pointer_T>::is_descendant(value))
15366
15851
  {
15367
15852
  return Convertible::Exact;
15368
15853
  }
@@ -15375,7 +15860,6 @@ namespace Rice4RubyQt6::detail
15375
15860
  T** convert(VALUE value)
15376
15861
  {
15377
15862
  bool isOwner = this->arg_ && this->arg_->isOwner();
15378
- bool isBuffer = dynamic_cast<ArgBuffer*>(this->arg_) ? true : false;
15379
15863
 
15380
15864
  switch (rb_type(value))
15381
15865
  {
@@ -15386,7 +15870,7 @@ namespace Rice4RubyQt6::detail
15386
15870
  }
15387
15871
  case RUBY_T_DATA:
15388
15872
  {
15389
- if (Data_Type<Pointer_T>::is_descendant(value) && isBuffer)
15873
+ if (Data_Type<Pointer_T>::is_descendant(value))
15390
15874
  {
15391
15875
  T** result = detail::unwrap<Intrinsic_T*>(value, Data_Type<Pointer_T>::ruby_data_type(), isOwner);
15392
15876
  return result;
@@ -15395,14 +15879,7 @@ namespace Rice4RubyQt6::detail
15395
15879
  }
15396
15880
  default:
15397
15881
  {
15398
- if (isBuffer)
15399
- {
15400
- throw create_type_exception<Pointer_T>(value);
15401
- }
15402
- else
15403
- {
15404
- throw create_type_exception<T**>(value);
15405
- }
15882
+ throw create_type_exception<Pointer_T>(value);
15406
15883
  }
15407
15884
  }
15408
15885
  }
@@ -15721,7 +16198,7 @@ namespace Rice4RubyQt6
15721
16198
  // These methods cannot be defined where they are declared due to circular dependencies
15722
16199
  inline Class Object::class_of() const
15723
16200
  {
15724
- return detail::protect(rb_class_of, value_);
16201
+ return detail::protect(rb_class_of, this->value());
15725
16202
  }
15726
16203
 
15727
16204
  inline String Object::to_s() const
@@ -15801,86 +16278,6 @@ namespace Rice4RubyQt6
15801
16278
  }
15802
16279
  }
15803
16280
 
15804
- // Dependent on Module, Array, Symbol - used by stl smart pointers
15805
-
15806
- // ========= Forwards.hpp =========
15807
-
15808
- namespace Rice4RubyQt6::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 Rice4RubyQt6::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
-
15884
16281
  // For now include libc support - maybe should be separate header file someday
15885
16282
 
15886
16283
  // ========= file.hpp =========