rice 4.8.0 → 4.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -1
  3. data/CMakePresets.json +77 -50
  4. data/FindRuby.cmake +1 -1
  5. data/bin/rice-doc.rb +2 -0
  6. data/include/rice/api.hpp +14 -1
  7. data/include/rice/rice.hpp +351 -132
  8. data/include/rice/stl.hpp +319 -256
  9. data/lib/rice/doc/config.rb +57 -57
  10. data/lib/rice/doc/cpp_reference.rb +158 -158
  11. data/lib/rice/doc/doxygen.rb +289 -289
  12. data/lib/rice/doc/mkdocs.rb +332 -332
  13. data/lib/rice/doc/rice.rb +48 -47
  14. data/lib/rice/doc/ruby.rb +26 -26
  15. data/lib/rice/native.rb +15 -15
  16. data/lib/rice/native_registry.rb +12 -17
  17. data/lib/rice/parameter.rb +5 -5
  18. data/lib/rice/rbs.rb +72 -72
  19. data/lib/rice/version.rb +1 -1
  20. data/lib/rubygems/builder.rb +9 -9
  21. data/lib/rubygems_plugin.rb +8 -8
  22. data/rice/Data_Type.ipp +12 -7
  23. data/rice/cpp_api/Class.hpp +5 -0
  24. data/rice/cpp_api/Class.ipp +5 -0
  25. data/rice/cpp_api/Object.hpp +6 -0
  26. data/rice/cpp_api/Object.ipp +5 -0
  27. data/rice/detail/Forwards.hpp +18 -0
  28. data/rice/detail/Forwards.ipp +60 -0
  29. data/rice/detail/Native.ipp +2 -4
  30. data/rice/detail/NativeAttributeGet.ipp +1 -1
  31. data/rice/detail/NativeAttributeSet.hpp +5 -3
  32. data/rice/detail/NativeAttributeSet.ipp +41 -33
  33. data/rice/detail/NativeMethod.ipp +25 -22
  34. data/rice/detail/NativeRegistry.hpp +4 -2
  35. data/rice/detail/NativeRegistry.ipp +42 -9
  36. data/rice/detail/Parameter.ipp +3 -4
  37. data/rice/detail/Type.ipp +4 -0
  38. data/rice/detail/Wrapper.hpp +17 -12
  39. data/rice/detail/Wrapper.ipp +95 -36
  40. data/rice/rice.hpp +3 -0
  41. data/rice/rice_api/NativeRegistry.ipp +14 -1
  42. data/rice/stl/exception.ipp +1 -1
  43. data/rice/stl/filesystem.ipp +1 -1
  44. data/rice/stl/map.ipp +13 -11
  45. data/rice/stl/multimap.ipp +13 -11
  46. data/rice/stl/pair.ipp +14 -8
  47. data/rice/stl/set.ipp +16 -16
  48. data/rice/stl/shared_ptr.hpp +16 -0
  49. data/rice/stl/shared_ptr.ipp +74 -37
  50. data/rice/stl/type_index.ipp +1 -1
  51. data/rice/stl/unique_ptr.hpp +9 -3
  52. data/rice/stl/unique_ptr.ipp +80 -124
  53. data/rice/stl/unordered_map.ipp +14 -12
  54. data/rice/stl/vector.ipp +67 -31
  55. data/test/test_Attribute.cpp +72 -0
  56. data/test/test_Callback.cpp +3 -0
  57. data/test/test_Inheritance.cpp +14 -14
  58. data/test/test_Keep_Alive_No_Wrapper.cpp +6 -2
  59. data/test/test_Stl_Map.cpp +46 -0
  60. data/test/test_Stl_Multimap.cpp +46 -0
  61. data/test/test_Stl_Set.cpp +34 -0
  62. data/test/test_Stl_SharedPtr.cpp +160 -45
  63. data/test/test_Stl_UniquePtr.cpp +48 -3
  64. data/test/test_Stl_Unordered_Map.cpp +46 -0
  65. data/test/test_Stl_Variant.cpp +10 -14
  66. data/test/test_Stl_Vector.cpp +140 -13
  67. data/test/test_Tracking.cpp +3 -0
  68. metadata +3 -1
@@ -23,7 +23,7 @@ namespace Rice::detail
23
23
  // matches or calls function pointer. Instead Ruby can call the static call method defined on
24
24
  // this class (&NativeAttribute_T::get).
25
25
  Identifier identifier(name);
26
- detail::Registries::instance.natives.add(klass, identifier.id(), native);
26
+ detail::Registries::instance.natives.replace(klass, identifier.id(), native);
27
27
  }
28
28
 
29
29
  template<typename Attribute_T>
@@ -15,8 +15,9 @@ namespace Rice
15
15
  using Receiver_T = typename attribute_traits<Attribute_T>::class_type;
16
16
 
17
17
  public:
18
- // Register attribute getter/setter with Ruby
19
- static void define(VALUE klass, std::string name, Attribute_T attribute);
18
+ // Register attribute setter with Ruby
19
+ template<typename...Arg_Ts>
20
+ static void define(VALUE klass, std::string name, Attribute_T attribute, Arg_Ts&...args);
20
21
 
21
22
  public:
22
23
  // Disallow creating/copying/moving
@@ -34,11 +35,12 @@ namespace Rice
34
35
  VALUE returnKlass() override;
35
36
 
36
37
  protected:
37
- NativeAttributeSet(VALUE klass, std::string name, Attribute_T attr);
38
+ NativeAttributeSet(VALUE klass, std::string name, Attribute_T attr, std::unique_ptr<Parameter<T_Unqualified>> parameter);
38
39
 
39
40
  private:
40
41
  VALUE klass_;
41
42
  Attribute_T attribute_;
43
+ std::unique_ptr<Parameter<T_Unqualified>> parameter_;
42
44
  };
43
45
  } // detail
44
46
  } // Rice
@@ -5,28 +5,46 @@
5
5
  namespace Rice::detail
6
6
  {
7
7
  template<typename Attribute_T>
8
- void NativeAttributeSet<Attribute_T>::define(VALUE klass, std::string name, Attribute_T attribute)
8
+ template<typename...Arg_Ts>
9
+ void NativeAttributeSet<Attribute_T>::define(VALUE klass, std::string name, Attribute_T attribute, Arg_Ts&...args)
9
10
  {
10
- // Create a NativeAttributeSet that Ruby will call to read/write C++ variables
11
- NativeAttribute_T* nativeAttribute = new NativeAttribute_T(klass, name, std::forward<Attribute_T>(attribute));
11
+ // Extract Arg from Arg_Ts if present, otherwise create default
12
+ using Arg_Tuple = std::tuple<Arg_Ts...>;
13
+ constexpr std::size_t index = tuple_element_index_v<Arg_Tuple, Arg, ArgBuffer>;
14
+
15
+ std::unique_ptr<Arg> arg;
16
+ if constexpr (index < std::tuple_size_v<Arg_Tuple>)
17
+ {
18
+ using Arg_T_Local = std::decay_t<std::tuple_element_t<index, Arg_Tuple>>;
19
+ const Arg_T_Local& argInfo = std::get<index>(std::forward_as_tuple(std::forward<Arg_Ts>(args)...));
20
+ arg = std::make_unique<Arg_T_Local>(argInfo);
21
+ }
22
+ else
23
+ {
24
+ arg = std::make_unique<Arg>("value");
25
+ }
26
+
27
+ // Create the parameter
28
+ auto parameter = std::make_unique<Parameter<T_Unqualified>>(std::move(arg));
29
+
30
+ // Create a NativeAttributeSet that Ruby will call to write C++ variables
31
+ NativeAttribute_T* nativeAttribute = new NativeAttribute_T(klass, name, std::forward<Attribute_T>(attribute), std::move(parameter));
12
32
  std::unique_ptr<Native> native(nativeAttribute);
13
33
 
14
34
  // Define the write method name
15
35
  std::string setter = name + "=";
16
36
 
17
- // Tell Ruby to invoke the static method write to get the attribute value
37
+ // Tell Ruby to invoke the static method resolve to set the attribute value
18
38
  detail::protect(rb_define_method, klass, setter.c_str(), (RUBY_METHOD_FUNC)&Native::resolve, -1);
19
39
 
20
- // Add to native registry. Since attributes cannot be overridden, there is no need to set the
21
- // matches or calls function pointer. Instead Ruby can call the static call method defined on
22
- // this class (&NativeAttribute_T::set).
40
+ // Add to native registry
23
41
  Identifier identifier(setter);
24
- detail::Registries::instance.natives.add(klass, identifier.id(), native);
42
+ detail::Registries::instance.natives.replace(klass, identifier.id(), native);
25
43
  }
26
44
 
27
45
  template<typename Attribute_T>
28
- NativeAttributeSet<Attribute_T>::NativeAttributeSet(VALUE klass, std::string name, Attribute_T attribute)
29
- : Native(name), klass_(klass), attribute_(attribute)
46
+ NativeAttributeSet<Attribute_T>::NativeAttributeSet(VALUE klass, std::string name, Attribute_T attribute, std::unique_ptr<Parameter<T_Unqualified>> parameter)
47
+ : Native(name), klass_(klass), attribute_(attribute), parameter_(std::move(parameter))
30
48
  {
31
49
  }
32
50
 
@@ -47,25 +65,25 @@ namespace Rice::detail
47
65
  throw std::runtime_error("Incorrect number of parameters for setting attribute. Attribute: " + this->name_);
48
66
  }
49
67
 
68
+ // Get the Ruby value and convert to native
50
69
  VALUE value = values.begin()->second;
70
+ std::optional<VALUE> valueOpt(value);
71
+ T_Unqualified nativeValue = this->parameter_->convertToNative(valueOpt);
51
72
 
52
73
  if constexpr (!std::is_null_pointer_v<Receiver_T>)
53
74
  {
54
75
  Receiver_T* nativeSelf = From_Ruby<Receiver_T*>().convert(self);
55
-
56
- // Deal with pointers to pointes, see Parameter::convertToNative commment
57
- if constexpr (is_pointer_pointer_v<Attr_T> && !std::is_convertible_v<remove_cv_recursive_t<Attr_T>, Attr_T>)
58
- {
59
- nativeSelf->*attribute_ = (Attr_T)From_Ruby<T_Unqualified>().convert(value);
60
- }
61
- else
62
- {
63
- nativeSelf->*attribute_ = From_Ruby<T_Unqualified>().convert(value);
64
- }
76
+ nativeSelf->*attribute_ = (Attr_T)nativeValue;
65
77
  }
66
78
  else
67
79
  {
68
- *attribute_ = From_Ruby<T_Unqualified>().convert(value);
80
+ *attribute_ = nativeValue;
81
+ }
82
+
83
+ // Check if we need to prevent the value from being garbage collected
84
+ if (this->parameter_->arg()->isKeepAlive())
85
+ {
86
+ WrapperBase::addKeepAlive(self, value);
69
87
  }
70
88
 
71
89
  return value;
@@ -86,17 +104,7 @@ namespace Rice::detail
86
104
  template<typename Attribute_T>
87
105
  inline VALUE NativeAttributeSet<Attribute_T>::returnKlass()
88
106
  {
89
- // Check if an array is being returned
90
- bool isBuffer = dynamic_cast<ReturnBuffer*>(this->returnInfo_.get()) ? true : false;
91
- if (isBuffer)
92
- {
93
- TypeMapper<Pointer<detail::remove_cv_recursive_t<std::remove_pointer_t<Attr_T>>>> typeMapper;
94
- return typeMapper.rubyKlass();
95
- }
96
- else
97
- {
98
- TypeMapper<Attr_T> typeMapper;
99
- return typeMapper.rubyKlass();
100
- }
107
+ TypeMapper<Attr_T> typeMapper;
108
+ return typeMapper.rubyKlass();
101
109
  }
102
110
  }
@@ -107,30 +107,33 @@ namespace Rice::detail
107
107
  {
108
108
  return self;
109
109
  }
110
- /* This case happens when a class wrapped by Rice is calling a method
111
- defined on an ancestor class. For example, the std::map size method
112
- is defined on _Tree not map. Rice needs to know the actual type
113
- that was wrapped so it can correctly extract the C++ object from
114
- the Ruby object. */
115
- else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
116
- std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
117
- std::is_pointer_v<Receiver_T>)
118
- {
119
- Class_T* instance = From_Ruby<Class_T*>().convert(self);
120
- return dynamic_cast<Receiver_T>(instance);
121
- }
122
- else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
123
- std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
124
- std::is_reference_v<Receiver_T>)
125
- {
126
- Class_T& instance = From_Ruby<Class_T&>().convert(self);
127
- return dynamic_cast<Receiver_T>(instance);
128
- }
129
- // Self parameter could be derived from Object or it is an C++ instance and
130
- // needs to be unwrapped from Ruby
131
110
  else
132
111
  {
133
- return From_Ruby<Receiver_T>().convert(self);
112
+ /* When a class wrapped by Rice calls a method defined on an ancestor class
113
+ (e.g., std::map calling a method from _Tree), we need to unwrap as Class_T
114
+ and dynamic_cast to the base class. Otherwise unwrap directly as Receiver_T. */
115
+ constexpr bool isDerived = !std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
116
+ std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T>;
117
+
118
+ if constexpr (isDerived)
119
+ {
120
+ if constexpr (std::is_pointer_v<Receiver_T>)
121
+ {
122
+ Class_T* instance = From_Ruby<Class_T*>().convert(self);
123
+ return dynamic_cast<Receiver_T>(instance);
124
+ }
125
+ else if constexpr (std::is_reference_v<Receiver_T>)
126
+ {
127
+ Class_T& instance = From_Ruby<Class_T&>().convert(self);
128
+ return dynamic_cast<Receiver_T>(instance);
129
+ }
130
+ }
131
+ else
132
+ {
133
+ // Note GCC has a false warning: function may return address of local variable [-Wreturn-local-addr].
134
+ // From_Ruby returns a reference to data in the Ruby object, not the temporary.
135
+ return From_Ruby<Receiver_T>().convert(self);
136
+ }
134
137
  }
135
138
  }
136
139
 
@@ -31,10 +31,12 @@ namespace Rice::detail
31
31
  NativeRegistry& operator=(const NativeRegistry& other) = delete;
32
32
 
33
33
  void add(VALUE klass, ID methodId, std::unique_ptr<Native>& native);
34
+ void replace(VALUE klass, ID methodId, std::unique_ptr<Native>& native);
34
35
  void reset(VALUE klass);
35
36
 
36
- const std::vector<Native*> lookup(VALUE klass);
37
- const std::vector<std::unique_ptr<Native>>& lookup(VALUE klass, ID methodId);
37
+ std::vector<Native*> lookup(VALUE klass);
38
+ std::vector<std::unique_ptr<Native>>& lookup(VALUE klass, ID methodId);
39
+ std::vector<Native*> lookup(VALUE klass, NativeKind kind);
38
40
 
39
41
  private:
40
42
  // Key - Ruby klass/method
@@ -3,17 +3,21 @@ namespace Rice::detail
3
3
  {
4
4
  inline void NativeRegistry::add(VALUE klass, ID methodId, std::unique_ptr<Native>& native)
5
5
  {
6
- if (rb_type(klass) == T_ICLASS)
7
- {
8
- klass = detail::protect(rb_class_of, klass);
9
- }
6
+ // Lookup items for method
7
+ std::vector<std::unique_ptr<Native>>& natives = NativeRegistry::lookup(klass, methodId);
10
8
 
11
- // Create the key
12
- std::pair<VALUE, ID> key = std::make_pair(klass, methodId);
9
+ // Add new native
10
+ natives.push_back(std::move(native));
11
+ }
13
12
 
13
+ inline void NativeRegistry::replace(VALUE klass, ID methodId, std::unique_ptr<Native>& native)
14
+ {
14
15
  // Lookup items for method
15
- std::vector<std::unique_ptr<Native>>& natives = this->natives_[key];
16
+ std::vector<std::unique_ptr<Native>>& natives = NativeRegistry::lookup(klass, methodId);
16
17
 
18
+ // Clear existing natives
19
+ natives.clear();
20
+ // Add new native
17
21
  natives.push_back(std::move(native));
18
22
  }
19
23
 
@@ -33,7 +37,7 @@ namespace Rice::detail
33
37
  }
34
38
  }
35
39
 
36
- inline const std::vector<Native*> NativeRegistry::lookup(VALUE klass)
40
+ inline std::vector<Native*> NativeRegistry::lookup(VALUE klass)
37
41
  {
38
42
  std::vector<Native*> result;
39
43
 
@@ -59,7 +63,7 @@ namespace Rice::detail
59
63
  return result;
60
64
  }
61
65
 
62
- inline const std::vector<std::unique_ptr<Native>>& NativeRegistry::lookup(VALUE klass, ID methodId)
66
+ inline std::vector<std::unique_ptr<Native>>& NativeRegistry::lookup(VALUE klass, ID methodId)
63
67
  {
64
68
  if (rb_type(klass) == T_ICLASS)
65
69
  {
@@ -72,4 +76,33 @@ namespace Rice::detail
72
76
  // Lookup items for method
73
77
  return this->natives_[key];
74
78
  }
79
+
80
+ inline std::vector<Native*> NativeRegistry::lookup(VALUE klass, NativeKind kind)
81
+ {
82
+ std::vector<Native*> result;
83
+
84
+ if (rb_type(klass) == T_ICLASS)
85
+ {
86
+ klass = detail::protect(rb_class_of, klass);
87
+ }
88
+
89
+ for (auto& pair : this->natives_)
90
+ {
91
+ const std::pair<VALUE, ID>& key = pair.first;
92
+
93
+ if (klass == key.first)
94
+ {
95
+ const std::vector<std::unique_ptr<Native>>& natives = pair.second;
96
+ for (auto& native : natives)
97
+ {
98
+ if (native->kind() == kind)
99
+ {
100
+ result.push_back(native.get());
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ return result;
107
+ }
75
108
  }
@@ -43,17 +43,16 @@ namespace Rice::detail
43
43
  // One caveat - procs are also RUBY_T_DATA so don't check if this is a function type
44
44
  if (result == Convertible::Exact && rb_type(value) == RUBY_T_DATA && !std::is_function_v<std::remove_pointer_t<T>>)
45
45
  {
46
- // Check the constness of the Ruby wrapped value and the parameter
47
- WrapperBase* wrapper = getWrapper(value);
46
+ bool isConst = WrapperBase::isConst(value);
48
47
 
49
48
  // Do not send a const value to a non-const parameter
50
- if (wrapper->isConst() && !is_const_any_v<T>)
49
+ if (isConst && !is_const_any_v<T>)
51
50
  {
52
51
  result = Convertible::None;
53
52
  }
54
53
  // It is ok to send a non-const value to a const parameter but
55
54
  // prefer non-const to non-const by slightly decreasing the score
56
- else if (!wrapper->isConst() && is_const_any_v<T>)
55
+ else if (!isConst && is_const_any_v<T>)
57
56
  {
58
57
  result = Convertible::ConstMismatch;
59
58
  }
data/rice/detail/Type.ipp CHANGED
@@ -209,6 +209,10 @@ namespace Rice::detail
209
209
  std::regex equalRegex(R"(,\s*std::equal_to)");
210
210
  removeGroup(base, equalRegex);
211
211
 
212
+ // Remove default_delete (std::unique_ptr)
213
+ std::regex defaultDeleteRegex(R"(,\s*std::default_delete)");
214
+ removeGroup(base, defaultDeleteRegex);
215
+
212
216
  // Remove spaces before pointers
213
217
  std::regex ptrRegex = std::regex(R"(\s+\*)");
214
218
  base = std::regex_replace(base, ptrRegex, "*");
@@ -6,9 +6,13 @@ namespace Rice::detail
6
6
  class WrapperBase
7
7
  {
8
8
  public:
9
- WrapperBase() = default;
9
+ static void addKeepAlive(VALUE object, VALUE keepAlive);
10
+ static bool isConst(VALUE object);
11
+
12
+ public:
13
+ WrapperBase(rb_data_type_t* rb_data_type);
10
14
  virtual ~WrapperBase() = default;
11
- virtual void* get() = 0;
15
+ virtual void* get(rb_data_type_t* requestedType) = 0;
12
16
  bool isConst();
13
17
 
14
18
  void ruby_mark();
@@ -16,6 +20,7 @@ namespace Rice::detail
16
20
  void setOwner(bool value);
17
21
 
18
22
  protected:
23
+ rb_data_type_t* rb_data_type_;
19
24
  bool isOwner_ = false;
20
25
  bool isConst_ = false;
21
26
 
@@ -30,10 +35,10 @@ namespace Rice::detail
30
35
  class Wrapper : public WrapperBase
31
36
  {
32
37
  public:
33
- Wrapper(T& data);
34
- Wrapper(T&& data);
38
+ Wrapper(rb_data_type_t* rb_data_type, T& data);
39
+ Wrapper(rb_data_type_t* rb_data_type, T&& data);
35
40
  ~Wrapper();
36
- void* get() override;
41
+ void* get(rb_data_type_t* requestedType) override;
37
42
 
38
43
  private:
39
44
  T data_;
@@ -43,9 +48,9 @@ namespace Rice::detail
43
48
  class Wrapper<T&> : public WrapperBase
44
49
  {
45
50
  public:
46
- Wrapper(T& data);
51
+ Wrapper(rb_data_type_t* rb_data_type, T& data);
47
52
  ~Wrapper();
48
- void* get() override;
53
+ void* get(rb_data_type_t* requestedType) override;
49
54
 
50
55
  private:
51
56
  T& data_;
@@ -55,9 +60,9 @@ namespace Rice::detail
55
60
  class Wrapper<T*> : public WrapperBase
56
61
  {
57
62
  public:
58
- Wrapper(T* data, bool isOwner);
63
+ Wrapper(rb_data_type_t* rb_data_type, T* data, bool isOwner);
59
64
  ~Wrapper();
60
- void* get() override;
65
+ void* get(rb_data_type_t* requestedType) override;
61
66
 
62
67
  private:
63
68
  T* data_ = nullptr;
@@ -67,9 +72,9 @@ namespace Rice::detail
67
72
  class Wrapper<T**> : public WrapperBase
68
73
  {
69
74
  public:
70
- Wrapper(T** data, bool isOwner);
75
+ Wrapper(rb_data_type_t* rb_data_type, T** data, bool isOwner);
71
76
  ~Wrapper();
72
- void* get() override;
77
+ void* get(rb_data_type_t* requestedType) override;
73
78
 
74
79
  private:
75
80
  T** data_ = nullptr;
@@ -77,7 +82,7 @@ namespace Rice::detail
77
82
 
78
83
  // ---- Helper Functions ---------
79
84
  template <typename T>
80
- void wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data);
85
+ Wrapper<T*>* wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data);
81
86
 
82
87
  template <typename T>
83
88
  VALUE wrap(VALUE klass, rb_data_type_t* rb_data_type, T& data, bool isOwner);