rice 4.6.1 → 4.7.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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/CMakeLists.txt +0 -4
  4. data/Rakefile +2 -8
  5. data/bin/rice-doc.rb +212 -0
  6. data/bin/rice-rbs.rb +93 -0
  7. data/include/rice/rice.hpp +4972 -4015
  8. data/include/rice/stl.hpp +822 -294
  9. data/lib/rice/doc/cpp_reference.rb +166 -0
  10. data/lib/rice/doc/doxygen.rb +294 -0
  11. data/lib/rice/doc/mkdocs.rb +298 -0
  12. data/lib/rice/doc/rice.rb +29 -0
  13. data/lib/rice/doc/ruby.rb +37 -0
  14. data/lib/rice/doc.rb +5 -0
  15. data/lib/{make_rice_headers.rb → rice/make_rice_headers.rb} +3 -0
  16. data/lib/rice/native.rb +18 -0
  17. data/lib/rice/native_registry.rb +21 -0
  18. data/lib/rice/parameter.rb +7 -0
  19. data/lib/rice/rbs.rb +104 -0
  20. data/lib/rice/version.rb +1 -1
  21. data/lib/rice.rb +4 -0
  22. data/lib/rubygems/cmake_builder.rb +24 -27
  23. data/rice/Arg.hpp +4 -4
  24. data/rice/Arg.ipp +4 -4
  25. data/rice/Buffer.hpp +32 -28
  26. data/rice/Buffer.ipp +306 -178
  27. data/rice/Data_Object.ipp +101 -82
  28. data/rice/Data_Type.hpp +5 -7
  29. data/rice/Data_Type.ipp +48 -29
  30. data/rice/Enum.ipp +15 -21
  31. data/rice/Function.hpp +17 -0
  32. data/rice/Function.ipp +13 -0
  33. data/rice/Pointer.hpp +15 -0
  34. data/rice/Pointer.ipp +49 -0
  35. data/rice/Return.hpp +1 -1
  36. data/rice/Return.ipp +2 -2
  37. data/rice/api.hpp +30 -0
  38. data/rice/cpp_api/Array.hpp +2 -2
  39. data/rice/cpp_api/Array.ipp +50 -5
  40. data/rice/cpp_api/Class.hpp +0 -5
  41. data/rice/cpp_api/Class.ipp +19 -0
  42. data/rice/cpp_api/Hash.ipp +20 -0
  43. data/rice/cpp_api/Module.hpp +6 -3
  44. data/rice/cpp_api/Module.ipp +49 -11
  45. data/rice/cpp_api/Object.ipp +31 -2
  46. data/rice/cpp_api/String.hpp +1 -2
  47. data/rice/cpp_api/String.ipp +21 -1
  48. data/rice/cpp_api/Struct.ipp +5 -0
  49. data/rice/cpp_api/Symbol.ipp +34 -0
  50. data/rice/cpp_api/shared_methods.hpp +12 -12
  51. data/rice/detail/MethodInfo.hpp +4 -2
  52. data/rice/detail/MethodInfo.ipp +19 -3
  53. data/rice/detail/ModuleRegistry.hpp +18 -0
  54. data/rice/detail/ModuleRegistry.ipp +25 -0
  55. data/rice/detail/Native.hpp +45 -2
  56. data/rice/detail/Native.ipp +196 -2
  57. data/rice/detail/NativeAttributeGet.hpp +9 -4
  58. data/rice/detail/NativeAttributeGet.ipp +65 -11
  59. data/rice/detail/NativeAttributeSet.hpp +4 -0
  60. data/rice/detail/NativeAttributeSet.ipp +30 -2
  61. data/rice/detail/NativeCallbackFFI.ipp +2 -2
  62. data/rice/detail/NativeCallbackSimple.ipp +1 -1
  63. data/rice/detail/NativeFunction.hpp +11 -49
  64. data/rice/detail/NativeFunction.ipp +82 -379
  65. data/rice/detail/NativeInvoker.hpp +74 -0
  66. data/rice/detail/NativeInvoker.ipp +197 -0
  67. data/rice/detail/NativeIterator.hpp +4 -0
  68. data/rice/detail/NativeIterator.ipp +19 -0
  69. data/rice/detail/NativeMethod.hpp +97 -0
  70. data/rice/detail/NativeMethod.ipp +332 -0
  71. data/rice/detail/NativeProc.hpp +51 -0
  72. data/rice/detail/NativeProc.ipp +133 -0
  73. data/rice/detail/NativeRegistry.hpp +8 -0
  74. data/rice/detail/NativeRegistry.ipp +27 -0
  75. data/rice/detail/Parameter.hpp +47 -0
  76. data/rice/detail/Parameter.ipp +105 -0
  77. data/rice/detail/Proc.ipp +14 -13
  78. data/rice/detail/Registries.hpp +1 -0
  79. data/rice/detail/RubyType.hpp +0 -2
  80. data/rice/detail/RubyType.ipp +15 -33
  81. data/rice/detail/Type.hpp +44 -8
  82. data/rice/detail/Type.ipp +151 -49
  83. data/rice/detail/TypeRegistry.hpp +3 -0
  84. data/rice/detail/TypeRegistry.ipp +17 -27
  85. data/rice/detail/Types.ipp +430 -0
  86. data/rice/detail/Wrapper.hpp +12 -0
  87. data/rice/detail/Wrapper.ipp +45 -2
  88. data/rice/detail/from_ruby.ipp +567 -1073
  89. data/rice/detail/ruby.hpp +1 -0
  90. data/rice/detail/to_ruby.ipp +4 -635
  91. data/rice/libc/file.ipp +3 -6
  92. data/rice/rice.hpp +22 -12
  93. data/rice/rice_api/Arg.hpp +7 -0
  94. data/rice/rice_api/Arg.ipp +9 -0
  95. data/rice/rice_api/ModuleRegistry.hpp +7 -0
  96. data/rice/rice_api/ModuleRegistry.ipp +10 -0
  97. data/rice/rice_api/Native.hpp +7 -0
  98. data/rice/rice_api/Native.ipp +52 -0
  99. data/rice/rice_api/NativeRegistry.hpp +7 -0
  100. data/rice/rice_api/NativeRegistry.ipp +21 -0
  101. data/rice/rice_api/Parameter.hpp +7 -0
  102. data/rice/rice_api/Parameter.ipp +11 -0
  103. data/rice/rice_api/Registries.hpp +6 -0
  104. data/rice/rice_api/Registries.ipp +12 -0
  105. data/rice/rice_api/TypeRegistry.hpp +7 -0
  106. data/rice/rice_api/TypeRegistry.ipp +10 -0
  107. data/rice/stl/complex.ipp +35 -0
  108. data/rice/stl/exception.ipp +20 -7
  109. data/rice/stl/filesystem.hpp +6 -0
  110. data/rice/stl/filesystem.ipp +34 -0
  111. data/rice/stl/map.ipp +13 -21
  112. data/rice/stl/monostate.ipp +37 -1
  113. data/rice/stl/multimap.ipp +17 -24
  114. data/rice/stl/optional.ipp +47 -2
  115. data/rice/stl/pair.ipp +23 -58
  116. data/rice/stl/reference_wrapper.ipp +22 -1
  117. data/rice/stl/set.ipp +17 -9
  118. data/rice/stl/shared_ptr.ipp +44 -17
  119. data/rice/stl/string.ipp +175 -7
  120. data/rice/stl/string_view.ipp +5 -0
  121. data/rice/stl/tuple.ipp +38 -9
  122. data/rice/stl/unique_ptr.ipp +46 -2
  123. data/rice/stl/unordered_map.ipp +13 -21
  124. data/rice/stl/variant.ipp +47 -11
  125. data/rice/stl/vector.ipp +183 -104
  126. data/rice/stl.hpp +1 -0
  127. data/rice/traits/function_traits.hpp +2 -2
  128. data/rice/traits/method_traits.hpp +5 -16
  129. data/rice/traits/rice_traits.hpp +24 -4
  130. data/rice.gemspec +11 -22
  131. data/test/embed_ruby.cpp +0 -3
  132. data/test/test_Array.cpp +38 -38
  133. data/test/test_Attribute.cpp +187 -2
  134. data/test/test_Buffer.cpp +302 -26
  135. data/test/test_Callback.cpp +2 -3
  136. data/test/test_Class.cpp +5 -5
  137. data/test/test_Data_Object.cpp +0 -55
  138. data/test/test_Data_Type.cpp +19 -30
  139. data/test/test_Enum.cpp +4 -46
  140. data/test/test_From_Ruby.cpp +88 -81
  141. data/test/test_GVL.cpp +109 -0
  142. data/test/test_Iterator.cpp +1 -1
  143. data/test/test_Keep_Alive_No_Wrapper.cpp +5 -3
  144. data/test/test_Module.cpp +8 -9
  145. data/test/test_Object.cpp +1 -1
  146. data/test/test_Overloads.cpp +3 -3
  147. data/test/test_Stl_Map.cpp +8 -8
  148. data/test/test_Stl_Multimap.cpp +4 -4
  149. data/test/test_Stl_Pair.cpp +5 -3
  150. data/test/test_Stl_SharedPtr.cpp +24 -12
  151. data/test/test_Stl_Tuple.cpp +1 -1
  152. data/test/test_Stl_UniquePtr.cpp +8 -0
  153. data/test/test_Stl_Unordered_Map.cpp +9 -9
  154. data/test/test_Stl_Variant.cpp +9 -3
  155. data/test/test_Stl_Vector.cpp +118 -13
  156. data/test/test_To_Ruby.cpp +35 -28
  157. data/test/test_Type.cpp +256 -53
  158. data/test/unittest.hpp +35 -0
  159. metadata +66 -34
  160. data/rice/Init.hpp +0 -8
  161. data/rice/Init.ipp +0 -8
  162. data/rice/detail/RubyFunction.hpp +0 -31
  163. data/rice/detail/RubyFunction.ipp +0 -77
  164. data/sample/callbacks/extconf.rb +0 -5
  165. data/sample/callbacks/sample_callbacks.cpp +0 -35
  166. data/sample/callbacks/test.rb +0 -28
  167. data/sample/enum/extconf.rb +0 -5
  168. data/sample/enum/sample_enum.cpp +0 -40
  169. data/sample/enum/test.rb +0 -8
  170. data/sample/inheritance/animals.cpp +0 -82
  171. data/sample/inheritance/extconf.rb +0 -5
  172. data/sample/inheritance/test.rb +0 -7
  173. data/sample/map/extconf.rb +0 -5
  174. data/sample/map/map.cpp +0 -73
  175. data/sample/map/test.rb +0 -7
  176. data/test/ext/t1/Foo.hpp +0 -10
  177. data/test/ext/t1/extconf.rb +0 -4
  178. data/test/ext/t1/t1.cpp +0 -13
  179. data/test/ext/t2/extconf.rb +0 -4
  180. data/test/ext/t2/t2.cpp +0 -11
  181. data/test/ruby/test_callbacks_sample.rb +0 -28
  182. data/test/ruby/test_multiple_extensions.rb +0 -18
  183. data/test/ruby/test_multiple_extensions_same_class.rb +0 -14
  184. data/test/ruby/test_multiple_extensions_with_inheritance.rb +0 -20
  185. /data/test/{test_Stl_Type.cpp → test_Stl_Type_Info.cpp} +0 -0
@@ -0,0 +1,332 @@
1
+ #include <algorithm>
2
+ #include <array>
3
+ #include <stdexcept>
4
+ #include <sstream>
5
+ #include <tuple>
6
+
7
+ namespace Rice::detail
8
+ {
9
+ template<typename Class_T, typename Method_T>
10
+ void NativeMethod<Class_T, Method_T>::define(VALUE klass, std::string method_name, Method_T method, MethodInfo* methodInfo)
11
+ {
12
+ // Verify return and argument types
13
+ Native::verify_type<Return_T>(methodInfo->returnInfo()->isBuffer());
14
+ auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
15
+ Native::verify_args<Arg_Ts>(methodInfo, indices);
16
+
17
+ // Have we defined this method yet in Ruby?
18
+ Identifier identifier(method_name);
19
+ const std::vector<std::unique_ptr<Native>>& natives = Registries::instance.natives.lookup(klass, identifier.id());
20
+ if (natives.empty())
21
+ {
22
+ // Tell Ruby to invoke the static resolved method defined above
23
+ detail::protect(rb_define_method, klass, method_name.c_str(), (RUBY_METHOD_FUNC)&Native::resolve, -1);
24
+ }
25
+
26
+ // Create a NativeMethod instance and save it to the NativeRegistry. There may be multiple
27
+ // NativeMethod instances for a specific method because C++ supports method overloading.
28
+ NativeMethod_T* NativeMethod = new NativeMethod_T(klass, method_name, std::forward<Method_T>(method), methodInfo);
29
+ std::unique_ptr<Native> native(NativeMethod);
30
+ detail::Registries::instance.natives.add(klass, identifier.id(), native);
31
+ }
32
+
33
+ template<typename Class_T, typename Method_T>
34
+ NativeMethod<Class_T, Method_T>::NativeMethod(VALUE klass, std::string method_name, Method_T method, MethodInfo* methodInfo)
35
+ : Native(Native::create_parameters<Arg_Ts>(methodInfo)),
36
+ klass_(klass), method_name_(method_name), method_(method), methodInfo_(methodInfo),
37
+ toRuby_(methodInfo->returnInfo())
38
+ {
39
+ }
40
+
41
+ template<typename Class_T, typename Method_T>
42
+ template<std::size_t... I>
43
+ std::vector<std::string> NativeMethod<Class_T, Method_T>::argTypeNames(std::ostringstream& stream, std::index_sequence<I...>& indices)
44
+ {
45
+ std::vector<std::string> result;
46
+ for (std::unique_ptr<ParameterAbstract>& parameter : this->parameters_)
47
+ {
48
+ result.push_back(parameter->cppTypeName());
49
+ }
50
+ return result;
51
+ }
52
+
53
+ template<typename Class_T, typename Method_T>
54
+ std::string NativeMethod<Class_T, Method_T>::toString()
55
+ {
56
+ std::ostringstream result;
57
+
58
+ detail::TypeMapper<Return_T> typeReturnMapper;
59
+ result << typeReturnMapper.simplifiedName() << " ";
60
+
61
+ if (!std::is_null_pointer_v<Receiver_T>)
62
+ {
63
+ detail::TypeMapper<Receiver_T> typeReceiverMapper;
64
+ result << typeReceiverMapper.simplifiedName() << "::";
65
+ }
66
+
67
+ result << this->method_name_;
68
+
69
+ result << "(";
70
+
71
+ auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
72
+ std::vector<std::string> argTypeNames = this->argTypeNames(result, indices);
73
+ for (size_t i = 0; i < argTypeNames.size(); i++)
74
+ {
75
+ result << argTypeNames[i];
76
+ if (i < argTypeNames.size() - 1)
77
+ result << ", ";
78
+ }
79
+ result << ")";
80
+ return result.str();
81
+ }
82
+
83
+ template<typename Class_T, typename Method_T>
84
+ template<std::size_t... I>
85
+ typename NativeMethod<Class_T, Method_T>::Apply_Args_T NativeMethod<Class_T, Method_T>::getNativeValues(VALUE self, std::vector<std::optional<VALUE>>& values, std::index_sequence<I...>& indices)
86
+ {
87
+ /* Loop over each value returned from Ruby and convert it to the appropriate C++ type based
88
+ on the arguments (Arg_Ts) required by the C++ method. Arg_T may have const/volatile while
89
+ the associated From_Ruby<T> template parameter will not. Thus From_Ruby produces non-const values
90
+ which we let the compiler convert to const values as needed. This works except for
91
+ T** -> const T**, see comment in convertToNative method. */
92
+ return std::forward_as_tuple(this->getReceiver(self),
93
+ (dynamic_cast<Parameter<std::tuple_element_t<I, Arg_Ts>>*>(this->parameters_[I].get()))->
94
+ convertToNative(values[I])...);
95
+ }
96
+
97
+ template<typename Class_T, typename Method_T>
98
+ typename NativeMethod<Class_T, Method_T>::Receiver_T NativeMethod<Class_T, Method_T>::getReceiver(VALUE self)
99
+ {
100
+ // Self parameter is a Ruby VALUE so no conversion is needed
101
+ if constexpr (std::is_same_v<Receiver_T, VALUE>)
102
+ {
103
+ return self;
104
+ }
105
+ /* This case happens when a class wrapped by Rice is calling a method
106
+ defined on an ancestor class. For example, the std::map size method
107
+ is defined on _Tree not map. Rice needs to know the actual type
108
+ that was wrapped so it can correctly extract the C++ object from
109
+ the Ruby object. */
110
+ else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
111
+ std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
112
+ std::is_pointer_v<Receiver_T>)
113
+ {
114
+ Class_T* instance = From_Ruby<Class_T*>().convert(self);
115
+ return dynamic_cast<Receiver_T>(instance);
116
+ }
117
+ else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
118
+ std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
119
+ std::is_reference_v<Receiver_T>)
120
+ {
121
+ Class_T& instance = From_Ruby<Class_T&>().convert(self);
122
+ return dynamic_cast<Receiver_T>(instance);
123
+ }
124
+ // Self parameter could be derived from Object or it is an C++ instance and
125
+ // needs to be unwrapped from Ruby
126
+ else
127
+ {
128
+ return From_Ruby<Receiver_T>().convert(self);
129
+ }
130
+ }
131
+
132
+ template<typename Class_T, typename Method_T>
133
+ inline VALUE NativeMethod<Class_T, Method_T>::invoke(VALUE self, Apply_Args_T&& nativeArgs)
134
+ {
135
+ if constexpr (std::is_void_v<Return_T>)
136
+ {
137
+ std::apply(this->method_, std::forward<Apply_Args_T>(nativeArgs));
138
+ return Qnil;
139
+ }
140
+ else
141
+ {
142
+ Return_T nativeResult = std::apply(this->method_, std::forward<Apply_Args_T>(nativeArgs));
143
+
144
+ // Special handling if the method returns self. If so we do not want
145
+ // to create a new Ruby wrapper object and instead return self.
146
+ if constexpr (std::is_same_v<intrinsic_type<Return_T>, intrinsic_type<Receiver_T>>)
147
+ {
148
+ Receiver_T receiver = std::get<0>(nativeArgs);
149
+
150
+ if constexpr (std::is_pointer_v<Return_T> && std::is_pointer_v<Receiver_T>)
151
+ {
152
+ if (nativeResult == receiver)
153
+ return self;
154
+ }
155
+ else if constexpr (std::is_pointer_v<Return_T> && std::is_reference_v<Receiver_T>)
156
+ {
157
+ if (nativeResult == &receiver)
158
+ return self;
159
+ }
160
+ else if constexpr (std::is_reference_v<Return_T> && std::is_pointer_v<Receiver_T>)
161
+ {
162
+ if (&nativeResult == receiver)
163
+ return self;
164
+ }
165
+ else if constexpr (std::is_reference_v<Return_T> && std::is_reference_v<Receiver_T>)
166
+ {
167
+ if (&nativeResult == &receiver)
168
+ return self;
169
+ }
170
+ }
171
+
172
+ return this->toRuby_.convert(nativeResult);
173
+ }
174
+ }
175
+
176
+ template<typename Class_T, typename Method_T>
177
+ inline VALUE NativeMethod<Class_T, Method_T>::invokeNoGVL(VALUE self, Apply_Args_T&& nativeArgs)
178
+ {
179
+ if constexpr (std::is_void_v<Return_T>)
180
+ {
181
+ no_gvl(this->method_, std::forward<Apply_Args_T>(nativeArgs));
182
+ return Qnil;
183
+ }
184
+ else
185
+ {
186
+ Return_T nativeResult = no_gvl(this->method_, std::forward<Apply_Args_T>(nativeArgs));
187
+
188
+ // Special handling if the method returns self. If so we do not want
189
+ // to create a new Ruby wrapper object and instead return self.
190
+ if constexpr (std::is_same_v<intrinsic_type<Return_T>, intrinsic_type<Receiver_T>>)
191
+ {
192
+ Receiver_T receiver = std::get<0>(nativeArgs);
193
+
194
+ if constexpr (std::is_pointer_v<Return_T> && std::is_pointer_v<Receiver_T>)
195
+ {
196
+ if (nativeResult == receiver)
197
+ return self;
198
+ }
199
+ else if constexpr (std::is_pointer_v<Return_T> && std::is_reference_v<Receiver_T>)
200
+ {
201
+ if (nativeResult == &receiver)
202
+ return self;
203
+ }
204
+ else if constexpr (std::is_reference_v<Return_T> && std::is_pointer_v<Receiver_T>)
205
+ {
206
+ if (&nativeResult == receiver)
207
+ return self;
208
+ }
209
+ else if constexpr (std::is_reference_v<Return_T> && std::is_reference_v<Receiver_T>)
210
+ {
211
+ if (&nativeResult == &receiver)
212
+ return self;
213
+ }
214
+ }
215
+
216
+ return this->toRuby_.convert(nativeResult);
217
+ }
218
+ }
219
+
220
+ template<typename Class_T, typename Method_T>
221
+ inline void NativeMethod<Class_T, Method_T>::noWrapper(const VALUE klass, const std::string& wrapper)
222
+ {
223
+ std::stringstream message;
224
+
225
+ message << "When calling the method `";
226
+ message << this->method_name_;
227
+ message << "' we could not find the wrapper for the '";
228
+ message << rb_obj_classname(klass);
229
+ message << "' ";
230
+ message << wrapper;
231
+ message << " type. You should not use keepAlive() on a Return or Arg that is a builtin Rice type.";
232
+
233
+ throw std::runtime_error(message.str());
234
+ }
235
+
236
+ template<typename Class_T, typename Method_T>
237
+ void NativeMethod<Class_T, Method_T>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<std::optional<VALUE>>& rubyValues)
238
+ {
239
+ // Self will be Qnil for wrapped procs
240
+ if (self == Qnil)
241
+ return;
242
+
243
+ // selfWrapper will be nullptr if this(self) is a builtin type and not an external(wrapped) type
244
+ // it is highly unlikely that keepAlive is used in this case but we check anyway
245
+ WrapperBase* selfWrapper = getWrapper(self);
246
+
247
+ // Check method arguments
248
+ for (const Arg& arg : (*this->methodInfo_))
249
+ {
250
+ if (arg.isKeepAlive())
251
+ {
252
+ if (selfWrapper == nullptr)
253
+ {
254
+ noWrapper(self, "self");
255
+ }
256
+ selfWrapper->addKeepAlive(rubyValues[arg.position].value());
257
+ }
258
+ }
259
+
260
+ // Check return value
261
+ if (this->methodInfo_->returnInfo()->isKeepAlive())
262
+ {
263
+ if (selfWrapper == nullptr)
264
+ {
265
+ noWrapper(self, "self");
266
+ }
267
+
268
+ // returnWrapper will be nullptr if returnValue is a built-in type and not an external(wrapped) type
269
+ WrapperBase* returnWrapper = getWrapper(returnValue);
270
+ if (returnWrapper == nullptr)
271
+ {
272
+ noWrapper(returnValue, "return");
273
+ }
274
+ returnWrapper->addKeepAlive(self);
275
+ }
276
+ }
277
+
278
+ template<typename Class_T, typename Method_T>
279
+ VALUE NativeMethod<Class_T, Method_T>::operator()(size_t argc, const VALUE* argv, VALUE self)
280
+ {
281
+ // Get the ruby values and make sure we have the correct number
282
+ std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(argc, argv, true);
283
+ auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
284
+ Apply_Args_T nativeArgs = this->getNativeValues(self, rubyValues, indices);
285
+
286
+ bool noGvl = this->methodInfo_->function()->isNoGvl();
287
+
288
+ VALUE result = Qnil;
289
+
290
+ if (noGvl)
291
+ {
292
+ result = this->invokeNoGVL(self, std::forward<Apply_Args_T>(nativeArgs));
293
+ }
294
+ else
295
+ {
296
+ result = this->invoke(self, std::forward<Apply_Args_T>(nativeArgs));
297
+ }
298
+
299
+ // Check if any method arguments or return values need to have their lifetimes tied to the receiver
300
+ this->checkKeepAlive(self, result, rubyValues);
301
+
302
+ return result;
303
+ }
304
+
305
+ template<typename Class_T, typename Method_T>
306
+ inline std::string NativeMethod<Class_T, Method_T>::name()
307
+ {
308
+ return this->method_name_;
309
+ }
310
+
311
+ template<typename Class_T, typename Method_T>
312
+ inline NativeKind NativeMethod<Class_T, Method_T>::kind()
313
+ {
314
+ return NativeKind::Method;
315
+ }
316
+
317
+ template<typename Class_T, typename Method_T>
318
+ inline VALUE NativeMethod<Class_T, Method_T>::returnKlass()
319
+ {
320
+ // Check if an array is being returned
321
+ if (this->methodInfo_->returnInfo()->isBuffer())
322
+ {
323
+ TypeMapper<Pointer<Return_T>> typeMapper;
324
+ return typeMapper.rubyKlass();
325
+ }
326
+ else
327
+ {
328
+ TypeMapper<Return_T> typeMapper;
329
+ return typeMapper.rubyKlass();
330
+ }
331
+ }
332
+ }
@@ -0,0 +1,51 @@
1
+ #ifndef Rice__detail__Native_Proc__hpp_
2
+ #define Rice__detail__Native_Proc__hpp_
3
+
4
+ namespace Rice::detail
5
+ {
6
+ template<typename Proc_T>
7
+ class NativeProc: Native
8
+ {
9
+ public:
10
+ using NativeProc_T = NativeProc<Proc_T>;
11
+
12
+ // We remove const to avoid an explosion of To_Ruby specializations and Ruby doesn't
13
+ // have the concept of constants anyways
14
+ using Return_T = typename function_traits<Proc_T>::return_type;
15
+ using Arg_Ts = typename function_traits<Proc_T>::arg_types;
16
+ using To_Ruby_T = remove_cv_recursive_t<Return_T>;
17
+
18
+ // Define a new Ruby Proc to wrap a C++ function
19
+ static VALUE createRubyProc(Proc_T proc);
20
+ static NativeProc<Proc_T>* define(Proc_T proc);
21
+
22
+ // This is the method Ruby calls when invoking the proc
23
+ static VALUE resolve(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg);
24
+
25
+ public:
26
+ NativeProc(Proc_T proc, MethodInfo* methodInfo);
27
+ VALUE operator()(size_t argc, const VALUE* argv, VALUE self) override;
28
+ std::string toString() override;
29
+
30
+ std::string name() override;
31
+ NativeKind kind() override;
32
+ VALUE returnKlass() override;
33
+
34
+ private:
35
+ static VALUE finalizerCallback(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg);
36
+
37
+ // Convert Ruby values to C++ values
38
+ template<typename std::size_t...I>
39
+ Arg_Ts getNativeValues(std::vector<std::optional<VALUE>>& values, std::index_sequence<I...>& indices);
40
+
41
+ // Call the underlying C++ function
42
+ VALUE invoke(Arg_Ts&& nativeArgs);
43
+
44
+ private:
45
+ Proc_T proc_;
46
+ std::unique_ptr<MethodInfo> methodInfo_;
47
+ To_Ruby<To_Ruby_T> toRuby_;
48
+ };
49
+ }
50
+
51
+ #endif // Rice__detail__Native_Proc__hpp_
@@ -0,0 +1,133 @@
1
+ #include <algorithm>
2
+ #include <array>
3
+ #include <stdexcept>
4
+ #include <sstream>
5
+ #include <tuple>
6
+
7
+ namespace Rice::detail
8
+ {
9
+ template<typename Proc_T>
10
+ NativeProc<Proc_T>* NativeProc<Proc_T>::define(Proc_T proc)
11
+ {
12
+ MethodInfo* methodInfo = new MethodInfo(detail::function_traits<Proc_T>::arity);
13
+ return new NativeProc_T(std::forward<Proc_T>(proc), methodInfo);
14
+ }
15
+
16
+ template<typename Proc_T>
17
+ VALUE NativeProc<Proc_T>::createRubyProc(Proc_T proc)
18
+ {
19
+ NativeProc_T* nativeProc = NativeProc_T::define(std::forward<Proc_T>(proc));
20
+
21
+ // Create a Ruby proc to wrap it and pass the NativeProc as a callback parameter
22
+ VALUE result = rb_proc_new(NativeProc_T::resolve, (VALUE)nativeProc);
23
+
24
+ // Tie the lifetime of the NativeProc to the Ruby Proc
25
+ VALUE finalizer = rb_proc_new(NativeProc_T::finalizerCallback, (VALUE)nativeProc);
26
+ rb_define_finalizer(result, finalizer);
27
+
28
+ return result;
29
+ }
30
+
31
+ // Ruby calls this method when invoking a proc that was defined as a C++ function
32
+ template<typename Proc_T>
33
+ VALUE NativeProc<Proc_T>::resolve(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg)
34
+ {
35
+ return cpp_protect([&]
36
+ {
37
+ NativeProc_T * native = (NativeProc_T*)callback_arg;
38
+ return (*native)(argc, argv, Qnil);
39
+ });
40
+ }
41
+
42
+ // Ruby calls this method if an instance of a NativeProc is owned by a Ruby proc. That happens when C++
43
+ // returns a function back to Ruby
44
+ template<typename Proc_T>
45
+ VALUE NativeProc<Proc_T>::finalizerCallback(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg)
46
+ {
47
+ NativeProc_T* native = (NativeProc_T*)callback_arg;
48
+ delete native;
49
+ return Qnil;
50
+ }
51
+
52
+ template<typename Proc_T>
53
+ NativeProc<Proc_T>::NativeProc(Proc_T proc, MethodInfo* methodInfo) : Native(Native::create_parameters<Arg_Ts>(methodInfo)),
54
+ proc_(proc), methodInfo_(methodInfo)
55
+ {
56
+ }
57
+
58
+ template<typename Proc_T>
59
+ std::string NativeProc<Proc_T>::toString()
60
+ {
61
+ return "Proc";
62
+ }
63
+
64
+ template<typename Proc_T>
65
+ template<std::size_t... I>
66
+ typename NativeProc<Proc_T>::Arg_Ts NativeProc<Proc_T>::getNativeValues(std::vector<std::optional<VALUE>>& values,
67
+ std::index_sequence<I...>& indices)
68
+ {
69
+ /* Loop over each value returned from Ruby and convert it to the appropriate C++ type based
70
+ on the arguments (Arg_Ts) required by the C++ function. Arg_T may have const/volatile while
71
+ the associated From_Ruby<T> template parameter will not. Thus From_Ruby produces non-const values
72
+ which we let the compiler convert to const values as needed. This works except for
73
+ T** -> const T**, see comment in convertToNative method. */
74
+ //return std::forward_as_tuple(this->getNativeValue<std::tuple_element_t<I, Arg_Ts>, I>(values)...);
75
+ return std::forward_as_tuple(
76
+ (dynamic_cast<Parameter<std::tuple_element_t<I, Arg_Ts>>*>(this->parameters_[I].get()))->
77
+ convertToNative(values[I])...);
78
+ }
79
+
80
+ template<typename Proc_T>
81
+ VALUE NativeProc<Proc_T>::invoke(Arg_Ts&& nativeArgs)
82
+ {
83
+ if constexpr (std::is_void_v<Return_T>)
84
+ {
85
+ std::apply(this->proc_, std::forward<Arg_Ts>(nativeArgs));
86
+ return Qnil;
87
+ }
88
+ else
89
+ {
90
+ // Call the native method and get the result
91
+ Return_T nativeResult = std::apply(this->proc_, std::forward<Arg_Ts>(nativeArgs));
92
+
93
+ // Return the result
94
+ return this->toRuby_.convert(std::forward<Return_T>(nativeResult));
95
+ }
96
+ }
97
+
98
+ template<typename Proc_T>
99
+ VALUE NativeProc<Proc_T>::operator()(size_t argc, const VALUE* argv, VALUE self)
100
+ {
101
+ // Get the ruby values and make sure we have the correct number
102
+ std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(argc, argv, true);
103
+
104
+ auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
105
+
106
+ // Convert the Ruby values to native values
107
+ Arg_Ts nativeValues = this->getNativeValues(rubyValues, indices);
108
+
109
+ // Now call the native method
110
+ VALUE result = this->invoke(std::forward<Arg_Ts>(nativeValues));
111
+
112
+ return result;
113
+ }
114
+
115
+ template<typename Proc_T>
116
+ inline std::string NativeProc< Proc_T>::name()
117
+ {
118
+ return "proc";
119
+ }
120
+
121
+ template<typename Proc_T>
122
+ inline NativeKind NativeProc< Proc_T>::kind()
123
+ {
124
+ return NativeKind::Proc;
125
+ }
126
+
127
+ template<typename Proc_T>
128
+ inline VALUE NativeProc<Proc_T>::returnKlass()
129
+ {
130
+ TypeMapper<Return_T> typeMapper;
131
+ return typeMapper.rubyKlass();
132
+ }
133
+ }
@@ -25,8 +25,16 @@ namespace Rice::detail
25
25
  class NativeRegistry
26
26
  {
27
27
  public:
28
+ // std::is_copy_constructible returns true for std::vector<std::unique_ptr> - so we need
29
+ // to force the issue
30
+ NativeRegistry() = default;
31
+ NativeRegistry(const NativeRegistry& other) = delete;
32
+ NativeRegistry& operator=(const NativeRegistry& other) = delete;
33
+
28
34
  void add(VALUE klass, ID methodId, std::unique_ptr<Native>& native);
29
35
  void reset(VALUE klass);
36
+
37
+ const std::vector<Native*> lookup(VALUE klass);
30
38
  const std::vector<std::unique_ptr<Native>>& lookup(VALUE klass, ID methodId);
31
39
 
32
40
  private:
@@ -33,6 +33,33 @@ namespace Rice::detail
33
33
  }
34
34
  }
35
35
 
36
+ inline const std::vector<Native*> NativeRegistry::lookup(VALUE klass)
37
+ {
38
+ std::vector<Native*> result;
39
+
40
+ if (rb_type(klass) == T_ICLASS)
41
+ {
42
+ klass = detail::protect(rb_class_of, klass);
43
+ }
44
+
45
+ for (auto& pair : this->natives_)
46
+ {
47
+ const std::pair<VALUE, ID>& key = pair.first;
48
+
49
+ if (klass == key.first)
50
+ {
51
+ const std::vector<std::unique_ptr<Native>>& value = pair.second;
52
+ for (auto& native : value)
53
+ {
54
+ result.push_back(native.get());
55
+ }
56
+ }
57
+ }
58
+
59
+ return result;
60
+ }
61
+
62
+
36
63
  inline const std::vector<std::unique_ptr<Native>>& NativeRegistry::lookup(VALUE klass, ID methodId)
37
64
  {
38
65
  if (rb_type(klass) == T_ICLASS)
@@ -0,0 +1,47 @@
1
+ #ifndef Rice__detail__Parameter__hpp_
2
+ #define Rice__detail__Parameter__hpp_
3
+
4
+ #include <optional>
5
+
6
+ namespace Rice::detail
7
+ {
8
+ class ParameterAbstract
9
+ {
10
+ public:
11
+ ParameterAbstract() = default;
12
+ ParameterAbstract(Arg* arg);
13
+ virtual ~ParameterAbstract() = default;
14
+
15
+ ParameterAbstract(ParameterAbstract&& other) = default;
16
+ ParameterAbstract& operator=(ParameterAbstract&& other) = default;
17
+
18
+ virtual Convertible matches(std::optional<VALUE>& valueOpt) = 0;
19
+ virtual std::string cppTypeName() = 0;
20
+ virtual VALUE klass() = 0;
21
+
22
+ public:
23
+ Arg* arg = nullptr;
24
+ };
25
+
26
+ template<typename T>
27
+ class Parameter: public ParameterAbstract
28
+ {
29
+ public:
30
+ using Type = T;
31
+
32
+ Parameter() = default;
33
+ Parameter(Arg* arg);
34
+ Parameter(Parameter&& other) = default;
35
+ Parameter& operator=(Parameter&& other) = default;
36
+
37
+ T convertToNative(std::optional<VALUE>& valueOpt);
38
+ Convertible matches(std::optional<VALUE>& valueOpt) override;
39
+ std::string cppTypeName() override;
40
+ VALUE klass() override;
41
+
42
+ // std::string typeName() override;
43
+ private:
44
+ From_Ruby<remove_cv_recursive_t<T>> fromRuby_;
45
+ };
46
+ }
47
+ #endif // Rice__detail__Parameter__hpp_