rice 4.6.0 → 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 (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -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 +5221 -4009
  8. data/include/rice/stl.hpp +822 -295
  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 +77 -28
  26. data/rice/Buffer.ipp +500 -183
  27. data/rice/Data_Object.ipp +101 -82
  28. data/rice/Data_Type.hpp +7 -6
  29. data/rice/Data_Type.ipp +77 -47
  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 +73 -8
  59. data/rice/detail/NativeAttributeSet.hpp +4 -0
  60. data/rice/detail/NativeAttributeSet.ipp +33 -23
  61. data/rice/detail/NativeCallbackFFI.ipp +3 -2
  62. data/rice/detail/NativeCallbackSimple.ipp +1 -1
  63. data/rice/detail/NativeFunction.hpp +11 -49
  64. data/rice/detail/NativeFunction.ipp +83 -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 +150 -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 +182 -104
  126. data/rice/stl.hpp +1 -0
  127. data/rice/traits/attribute_traits.hpp +6 -6
  128. data/rice/traits/function_traits.hpp +2 -2
  129. data/rice/traits/method_traits.hpp +5 -16
  130. data/rice/traits/rice_traits.hpp +36 -4
  131. data/rice.gemspec +11 -22
  132. data/test/embed_ruby.cpp +0 -3
  133. data/test/test_Array.cpp +38 -38
  134. data/test/test_Attribute.cpp +244 -10
  135. data/test/test_Buffer.cpp +344 -13
  136. data/test/test_Callback.cpp +2 -3
  137. data/test/test_Class.cpp +5 -5
  138. data/test/test_Data_Object.cpp +0 -55
  139. data/test/test_Data_Type.cpp +19 -30
  140. data/test/test_Enum.cpp +4 -46
  141. data/test/test_From_Ruby.cpp +89 -82
  142. data/test/test_GVL.cpp +109 -0
  143. data/test/test_Iterator.cpp +1 -1
  144. data/test/test_Keep_Alive_No_Wrapper.cpp +5 -3
  145. data/test/test_Module.cpp +8 -9
  146. data/test/test_Object.cpp +1 -1
  147. data/test/test_Overloads.cpp +3 -3
  148. data/test/test_Stl_Map.cpp +8 -8
  149. data/test/test_Stl_Multimap.cpp +4 -4
  150. data/test/test_Stl_Pair.cpp +5 -3
  151. data/test/test_Stl_SharedPtr.cpp +24 -12
  152. data/test/test_Stl_Tuple.cpp +1 -1
  153. data/test/test_Stl_UniquePtr.cpp +8 -0
  154. data/test/test_Stl_Unordered_Map.cpp +9 -9
  155. data/test/test_Stl_Variant.cpp +9 -3
  156. data/test/test_Stl_Vector.cpp +118 -13
  157. data/test/test_To_Ruby.cpp +35 -28
  158. data/test/test_Type.cpp +256 -53
  159. data/test/unittest.hpp +35 -0
  160. metadata +66 -34
  161. data/rice/Init.hpp +0 -8
  162. data/rice/Init.ipp +0 -8
  163. data/rice/detail/RubyFunction.hpp +0 -31
  164. data/rice/detail/RubyFunction.ipp +0 -76
  165. data/sample/callbacks/extconf.rb +0 -5
  166. data/sample/callbacks/sample_callbacks.cpp +0 -35
  167. data/sample/callbacks/test.rb +0 -28
  168. data/sample/enum/extconf.rb +0 -5
  169. data/sample/enum/sample_enum.cpp +0 -40
  170. data/sample/enum/test.rb +0 -8
  171. data/sample/inheritance/animals.cpp +0 -82
  172. data/sample/inheritance/extconf.rb +0 -5
  173. data/sample/inheritance/test.rb +0 -7
  174. data/sample/map/extconf.rb +0 -5
  175. data/sample/map/map.cpp +0 -73
  176. data/sample/map/test.rb +0 -7
  177. data/test/ext/t1/Foo.hpp +0 -10
  178. data/test/ext/t1/extconf.rb +0 -4
  179. data/test/ext/t1/t1.cpp +0 -13
  180. data/test/ext/t2/extconf.rb +0 -4
  181. data/test/ext/t2/t2.cpp +0 -11
  182. data/test/ruby/test_callbacks_sample.rb +0 -28
  183. data/test/ruby/test_multiple_extensions.rb +0 -18
  184. data/test/ruby/test_multiple_extensions_same_class.rb +0 -14
  185. data/test/ruby/test_multiple_extensions_with_inheritance.rb +0 -20
  186. /data/test/{test_Stl_Type.cpp → test_Stl_Type_Info.cpp} +0 -0
@@ -2,12 +2,18 @@
2
2
  #include <array>
3
3
  #include <stdexcept>
4
4
  #include <sstream>
5
+ #include <tuple>
5
6
 
6
7
  namespace Rice::detail
7
8
  {
8
- template<typename Class_T, typename Function_T, bool IsMethod>
9
- void NativeFunction<Class_T, Function_T, IsMethod>::define(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
9
+ template<typename Function_T>
10
+ void NativeFunction<Function_T>::define(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
10
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
+
11
17
  // Have we defined this method yet in Ruby?
12
18
  Identifier identifier(method_name);
13
19
  const std::vector<std::unique_ptr<Native>>& natives = Registries::instance.natives.lookup(klass, identifier.id());
@@ -24,68 +30,33 @@ namespace Rice::detail
24
30
  detail::Registries::instance.natives.add(klass, identifier.id(), native);
25
31
  }
26
32
 
27
- // Ruby calls this method when invoking a proc that was defined as a C++ function
28
- template<typename Class_T, typename Function_T, bool IsMethod>
29
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::procEntry(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg)
30
- {
31
- return cpp_protect([&]
32
- {
33
- NativeFunction_T * native = (NativeFunction_T*)callback_arg;
34
- return (*native)(argc, argv, Qnil);
35
- });
36
- }
37
-
38
- // Ruby calls this method if an instance f a NativeFunction is owned by a Ruby proc. That happens when C++
39
- // returns a function back to Ruby
40
- template<typename Class_T, typename Function_T, bool IsMethod>
41
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::finalizerCallback(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg)
33
+ template<typename Function_T>
34
+ NativeFunction<Function_T>::NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
35
+ : Native(Native::create_parameters<Arg_Ts>(methodInfo)),
36
+ klass_(klass), method_name_(method_name), function_(function), methodInfo_(methodInfo),
37
+ toRuby_(methodInfo->returnInfo())
42
38
  {
43
- NativeFunction_T* native = (NativeFunction_T*)callback_arg;
44
- delete native;
45
- return Qnil;
46
- }
47
-
48
- template<typename Class_T, typename Function_T, bool IsMethod>
49
- NativeFunction<Class_T, Function_T, IsMethod>::NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
50
- : klass_(klass), method_name_(method_name), function_(function), methodInfo_(methodInfo)
51
- {
52
- // Create a tuple of NativeArgs that will convert the Ruby values to native values. For
53
- // builtin types NativeArgs will keep a copy of the native value so that it
54
- // can be passed by reference or pointer to the native function. For non-builtin types
55
- // it will just pass the value through.
56
- auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
57
- this->fromRubys_ = this->createFromRuby(indices);
58
-
59
- this->toRuby_ = this->createToRuby();
60
39
  }
61
40
 
62
- template<typename Class_T, typename Function_T, bool IsMethod>
63
- NativeFunction<Class_T, Function_T, IsMethod>::NativeFunction(Function_T function)
64
- : NativeFunction(Qnil, "", function, new MethodInfo(arity))
65
- {
66
- }
67
-
68
- template<typename Class_T, typename Function_T, bool IsMethod>
41
+ template<typename Function_T>
69
42
  template<std::size_t... I>
70
- std::vector<std::string> NativeFunction<Class_T, Function_T, IsMethod>::argTypeNames(std::ostringstream& stream, std::index_sequence<I...>& indices)
43
+ std::vector<std::string> NativeFunction<Function_T>::argTypeNames(std::ostringstream& stream, std::index_sequence<I...>& indices)
71
44
  {
72
- std::vector<std::string> typeNames;
73
- (typeNames.push_back(cppClassName(typeName(typeid(typename std::tuple_element<I, Arg_Ts>::type)))), ...);
74
- return typeNames;
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;
75
51
  }
76
52
 
77
- template<typename Class_T, typename Function_T, bool IsMethod>
78
- std::string NativeFunction<Class_T, Function_T, IsMethod>::toString()
53
+ template<typename Function_T>
54
+ std::string NativeFunction<Function_T>::toString()
79
55
  {
80
56
  std::ostringstream result;
81
57
 
82
- result << cppClassName(typeName(typeid(Return_T))) << " ";
83
-
84
- if (!std::is_null_pointer_v<Receiver_T>)
85
- {
86
- result << cppClassName(typeName(typeid(Receiver_T))) << "::";
87
- }
88
-
58
+ detail::TypeMapper<Return_T> typeMapper;
59
+ result << typeMapper.simplifiedName() << " ";
89
60
  result << this->method_name_;
90
61
 
91
62
  result << "(";
@@ -102,294 +73,24 @@ namespace Rice::detail
102
73
  return result.str();
103
74
  }
104
75
 
105
- template<typename Class_T, typename Function_T, bool IsMethod>
106
- To_Ruby<typename NativeFunction<Class_T, Function_T, IsMethod>::To_Ruby_T> NativeFunction<Class_T, Function_T, IsMethod>::createToRuby()
107
- {
108
- // Does the From_Ruby instantiation work with ReturnInfo?
109
- if constexpr (std::is_constructible_v<To_Ruby<To_Ruby_T>, Return*>)
110
- {
111
- return To_Ruby<To_Ruby_T>(&this->methodInfo_->returnInfo);
112
- }
113
- else
114
- {
115
- return To_Ruby<To_Ruby_T>();
116
- }
117
- }
118
-
119
- template<typename Class_T, typename Function_T, bool IsMethod>
120
- template<typename T, std::size_t I>
121
- From_Ruby<T> NativeFunction<Class_T, Function_T, IsMethod>::createFromRuby()
122
- {
123
- // Does the From_Ruby instantiation work with Arg?
124
- if constexpr (std::is_constructible_v<From_Ruby<T>, Arg*>)
125
- {
126
- return From_Ruby<T>(this->methodInfo_->arg(I));
127
- }
128
- else
129
- {
130
- return From_Ruby<T>();
131
- }
132
- }
133
-
134
- template<typename Class_T, typename Function_T, bool IsMethod>
135
- template<std::size_t... I>
136
- typename NativeFunction<Class_T, Function_T, IsMethod>::From_Ruby_Args_Ts NativeFunction<Class_T, Function_T, IsMethod>::createFromRuby(std::index_sequence<I...>& indices)
137
- {
138
- return std::make_tuple(createFromRuby<remove_cv_recursive_t<typename std::tuple_element<I, Arg_Ts>::type>, I>()...);
139
- }
140
-
141
- template<typename Class_T, typename Function_T, bool IsMethod>
142
- template<int I>
143
- Convertible NativeFunction<Class_T, Function_T, IsMethod>::matchParameter(std::vector<std::optional<VALUE>>& values)
144
- {
145
- Convertible result = Convertible::None;
146
- MethodInfo* methodInfo = this->methodInfo_.get();
147
- const Arg* arg = methodInfo->arg(I);
148
- std::optional<VALUE> value = values[I];
149
-
150
- // Is a VALUE being passed directly to C++ ?
151
- if (value.has_value())
152
- {
153
- if (arg->isValue())
154
- {
155
- result = Convertible::Exact;
156
- }
157
- // If index is less than argc then check with FromRuby if the VALUE is convertible
158
- // to C++.
159
- else
160
- {
161
- VALUE value = values[I].value();
162
- auto fromRuby = std::get<I>(this->fromRubys_);
163
- result = fromRuby.is_convertible(value);
164
-
165
- // If this is an exact match check if the const-ness of the value and the parameter match
166
- if (result == Convertible::Exact && rb_type(value) == RUBY_T_DATA)
167
- {
168
- // Check the constness of the Ruby wrapped value and the parameter
169
- WrapperBase* wrapper = getWrapper(value);
170
- using Parameter_T = std::tuple_element_t<I, Arg_Ts>;
171
-
172
- // Do not send a const value to a non-const parameter
173
- if (wrapper->isConst() && !is_const_any_v<Parameter_T>)
174
- {
175
- result = Convertible::None;
176
- }
177
- // It is ok to send a non-const value to a const parameter but
178
- // prefer non-const to non-const by slighly decreasing the convertible value
179
- else if (!wrapper->isConst() && is_const_any_v<Parameter_T>)
180
- {
181
- result = Convertible::Const;
182
- }
183
- }
184
- }
185
- }
186
- // Last check if a default value has been set
187
- else if (arg->hasDefaultValue())
188
- {
189
- result = Convertible::Exact;
190
- }
191
-
192
- return result;
193
- }
194
-
195
- template<typename Class_T, typename Function_T, bool IsMethod>
196
- template<std::size_t... I>
197
- Convertible NativeFunction<Class_T, Function_T, IsMethod>::matchParameters(std::vector<std::optional<VALUE>>& values,
198
- std::index_sequence<I...>& indices)
199
- {
200
- Convertible result = Convertible::Exact;
201
- ((result = result & this->matchParameter<I>(values)), ...);
202
- return result;
203
- }
204
-
205
- template<typename Class_T, typename Function_T, bool IsMethod>
206
- Resolved NativeFunction<Class_T, Function_T, IsMethod>::matches(size_t argc, const VALUE* argv, VALUE self)
207
- {
208
- // Return false if Ruby provided more arguments than the C++ method takes
209
- if (argc > arity)
210
- return Resolved{ Convertible::None, 0, this };
211
-
212
- Resolved result { Convertible::Exact, 1, this };
213
-
214
- std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(argc, argv, false);
215
- auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
216
- result.convertible = this->matchParameters(rubyValues, indices);
217
-
218
- if constexpr (arity > 0)
219
- {
220
- int providedValues = std::count_if(rubyValues.begin(), rubyValues.end(), [](std::optional<VALUE>& value)
221
- {
222
- return value.has_value();
223
- });
224
-
225
- result.parameterMatch = providedValues / (double)arity;
226
- }
227
- return result;
228
- }
229
-
230
- template<typename Class_T, typename Function_T, bool IsMethod>
231
- std::vector<std::optional<VALUE>> NativeFunction<Class_T, Function_T, IsMethod>::getRubyValues(size_t argc, const VALUE* argv, bool validate)
232
- {
233
- #undef max
234
- int size = std::max((size_t)arity, (size_t)argc);
235
- std::vector<std::optional<VALUE>> result(size);
236
-
237
- // Keyword handling
238
- if (rb_keyword_given_p())
239
- {
240
- // Keywords are stored in the last element in a hash
241
- int actualArgc = argc - 1;
242
-
243
- VALUE value = argv[actualArgc];
244
- Hash keywords(value);
245
-
246
- // Copy over leading non-keyword arguments
247
- for (int i = 0; i < actualArgc; i++)
248
- {
249
- result[i] = argv[i];
250
- }
251
-
252
- // Copy over keyword arguments
253
- for (auto pair : keywords)
254
- {
255
- Symbol key(pair.first);
256
- const Arg* arg = this->methodInfo_->arg(key.str());
257
- if (!arg)
258
- {
259
- throw std::invalid_argument("Unknown keyword: " + key.str());
260
- }
261
- result[arg->position] = pair.second.value();
262
- }
263
- }
264
- else
265
- {
266
- std::copy(argv, argv + argc, result.begin());
267
- }
268
-
269
- // Block handling. If we find a block and the last parameter is missing then
270
- // set it to the block
271
- if (rb_block_given_p() && result.size() > 0 && !result.back().has_value())
272
- {
273
- VALUE proc = rb_block_proc();
274
- result.back() = proc;
275
- }
276
-
277
- if (validate)
278
- {
279
- // Protect against user sending too many arguments
280
- if (argc > arity)
281
- {
282
- std::string message = "wrong number of arguments (given " +
283
- std::to_string(argc) + ", expected " + std::to_string(arity) + ")";
284
- throw std::invalid_argument(message);
285
- }
286
-
287
- for (size_t i=0; i<result.size(); i++)
288
- {
289
- std::optional<VALUE> value = result[i];
290
- Arg* arg = this->methodInfo_->arg(i);
291
-
292
- if (!arg->hasDefaultValue() && !value.has_value())
293
- {
294
- std::string message;
295
- message = "Missing argument. Name: " + arg->name + ". Index: " + std::to_string(arg->position) + ".";
296
- throw std::invalid_argument(message);
297
- }
298
- }
299
- }
300
-
301
- return result;
302
- }
303
-
304
- template<typename Class_T, typename Function_T, bool IsMethod>
305
- template<typename Arg_T, int I>
306
- Arg_T NativeFunction<Class_T, Function_T, IsMethod>::getNativeValue(std::vector<std::optional<VALUE>>& values)
307
- {
308
- /* In general the compiler will convert T to const T, but that does not work for converting
309
- T** to const T** (see see https://isocpp.org/wiki/faq/const-correctness#constptrptr-conversion)
310
- which comes up in the OpenCV bindings.
311
-
312
- An alternative solution is updating From_Ruby#convert to become a templated function that specifies
313
- the return type. That works but requires a lot more code changes for this one case and is not
314
- backwards compatible. */
315
-
316
- std::optional<VALUE> value = values[I];
317
- Arg* arg = this->methodInfo_->arg(I);
318
-
319
- if constexpr (is_pointer_pointer_v<Arg_T> && !std::is_convertible_v<remove_cv_recursive_t<Arg_T>, Arg_T>)
320
- {
321
- return (Arg_T)std::get<I>(this->fromRubys_).convert(value.value());
322
- }
323
- else if (value.has_value())
324
- {
325
- return std::get<I>(this->fromRubys_).convert(value.value());
326
- }
327
- else if constexpr (std::is_constructible_v<std::remove_cv_t<Arg_T>, std::remove_cv_t<std::remove_reference_t<Arg_T>>&>)
328
- {
329
- if (arg->hasDefaultValue())
330
- {
331
- return arg->defaultValue<Arg_T>();
332
- }
333
- }
334
-
335
- throw std::invalid_argument("Could not convert Rubyy value");
336
- }
337
-
338
- template<typename Class_T, typename Function_T, bool IsMethod>
76
+ template<typename Function_T>
339
77
  template<std::size_t... I>
340
- typename NativeFunction<Class_T, Function_T, IsMethod>::Arg_Ts NativeFunction<Class_T, Function_T, IsMethod>::getNativeValues(std::vector<std::optional<VALUE>>& values,
78
+ typename NativeFunction<Function_T>::Arg_Ts NativeFunction<Function_T>::getNativeValues(std::vector<std::optional<VALUE>>& values,
341
79
  std::index_sequence<I...>& indices)
342
80
  {
343
81
  /* Loop over each value returned from Ruby and convert it to the appropriate C++ type based
344
82
  on the arguments (Arg_Ts) required by the C++ function. Arg_T may have const/volatile while
345
83
  the associated From_Ruby<T> template parameter will not. Thus From_Ruby produces non-const values
346
84
  which we let the compiler convert to const values as needed. This works except for
347
- T** -> const T**, see comment in getNativeValue method. */
348
- return std::forward_as_tuple(this->getNativeValue<std::tuple_element_t<I, Arg_Ts>, I>(values)...);
349
- }
350
-
351
- template<typename Class_T, typename Function_T, bool IsMethod>
352
- typename NativeFunction<Class_T, Function_T, IsMethod>::Receiver_T NativeFunction<Class_T, Function_T, IsMethod>::getReceiver(VALUE self)
353
- {
354
- // There is no self parameter
355
- if constexpr (std::is_same_v<Receiver_T, std::nullptr_t>)
356
- {
357
- return nullptr;
358
- }
359
- // Self parameter is a Ruby VALUE so no conversion is needed
360
- else if constexpr (std::is_same_v<Receiver_T, VALUE>)
361
- {
362
- return self;
363
- }
364
- /* This case happens when a class wrapped by Rice is calling a method
365
- defined on an ancestor class. For example, the std::map size method
366
- is defined on _Tree not map. Rice needs to know the actual type
367
- that was wrapped so it can correctly extract the C++ object from
368
- the Ruby object. */
369
- else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
370
- std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
371
- std::is_pointer_v<Receiver_T>)
372
- {
373
- Class_T* instance = From_Ruby<Class_T*>().convert(self);
374
- return dynamic_cast<Receiver_T>(instance);
375
- }
376
- else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
377
- std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
378
- std::is_reference_v<Receiver_T>)
379
- {
380
- Class_T& instance = From_Ruby<Class_T&>().convert(self);
381
- return dynamic_cast<Receiver_T>(instance);
382
- }
383
- // Self parameter could be derived from Object or it is an C++ instance and
384
- // needs to be unwrapped from Ruby
385
- else
386
- {
387
- return From_Ruby<Receiver_T>().convert(self);
388
- }
85
+ T** -> const T**, see comment in convertToNative method. */
86
+ //return std::forward_as_tuple(this->getNativeValue<std::tuple_element_t<I, Arg_Ts>, I>(values)...);
87
+ return std::forward_as_tuple(
88
+ (dynamic_cast<Parameter<std::tuple_element_t<I, Arg_Ts>>*>(this->parameters_[I].get()))->
89
+ convertToNative(values[I])...);
389
90
  }
390
91
 
391
- template<typename Class_T, typename Function_T, bool IsMethod>
392
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::invokeNativeFunction(Arg_Ts&& nativeArgs)
92
+ template<typename Function_T>
93
+ VALUE NativeFunction<Function_T>::invoke(Arg_Ts&& nativeArgs)
393
94
  {
394
95
  if constexpr (std::is_void_v<Return_T>)
395
96
  {
@@ -402,57 +103,30 @@ namespace Rice::detail
402
103
  Return_T nativeResult = std::apply(this->function_, std::forward<Arg_Ts>(nativeArgs));
403
104
 
404
105
  // Return the result
405
- return this->toRuby_.convert(std::forward<Return_T>(nativeResult));
106
+ return this->toRuby_.convert(nativeResult);
406
107
  }
407
108
  }
408
109
 
409
- template<typename Class_T, typename Function_T, bool IsMethod>
410
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::invokeNativeMethod(VALUE self, Arg_Ts&& nativeArgs)
110
+ template<typename Function_T>
111
+ VALUE NativeFunction<Function_T>::invokeNoGVL(Arg_Ts&& nativeArgs)
411
112
  {
412
- Receiver_T receiver = this->getReceiver(self);
413
- auto selfAndNativeArgs = std::tuple_cat(std::forward_as_tuple(receiver), std::forward<Arg_Ts>(nativeArgs));
414
-
415
113
  if constexpr (std::is_void_v<Return_T>)
416
114
  {
417
- std::apply(this->function_, std::forward<decltype(selfAndNativeArgs)>(selfAndNativeArgs));
115
+ no_gvl(this->function_, std::forward<Arg_Ts>(nativeArgs));
418
116
  return Qnil;
419
117
  }
420
118
  else
421
119
  {
422
- Return_T nativeResult = std::apply(this->function_, std::forward<decltype(selfAndNativeArgs)>(selfAndNativeArgs));
423
-
424
- // Special handling if the method returns self. If so we do not want
425
- // to create a new Ruby wrapper object and instead return self.
426
- if constexpr (std::is_same_v<intrinsic_type<Return_T>, intrinsic_type<Receiver_T>>)
427
- {
428
- if constexpr (std::is_pointer_v<Return_T> && std::is_pointer_v<Receiver_T>)
429
- {
430
- if (nativeResult == receiver)
431
- return self;
432
- }
433
- else if constexpr (std::is_pointer_v<Return_T> && std::is_reference_v<Receiver_T>)
434
- {
435
- if (nativeResult == &receiver)
436
- return self;
437
- }
438
- else if constexpr (std::is_reference_v<Return_T> && std::is_pointer_v<Receiver_T>)
439
- {
440
- if (&nativeResult == receiver)
441
- return self;
442
- }
443
- else if constexpr (std::is_reference_v<Return_T> && std::is_reference_v<Receiver_T>)
444
- {
445
- if (&nativeResult == &receiver)
446
- return self;
447
- }
448
- }
120
+ // Call the native method and get the result
121
+ Return_T nativeResult = no_gvl(this->function_, std::forward<Arg_Ts>(nativeArgs));
449
122
 
450
- return this->toRuby_.convert(std::forward<Return_T>(nativeResult));
123
+ // Return the result
124
+ return this->toRuby_.convert(nativeResult);
451
125
  }
452
126
  }
453
127
 
454
- template<typename Class_T, typename Function_T, bool IsMethod>
455
- void NativeFunction<Class_T, Function_T, IsMethod>::noWrapper(const VALUE klass, const std::string& wrapper)
128
+ template<typename Function_T>
129
+ void NativeFunction<Function_T>::noWrapper(const VALUE klass, const std::string& wrapper)
456
130
  {
457
131
  std::stringstream message;
458
132
 
@@ -467,8 +141,8 @@ namespace Rice::detail
467
141
  throw std::runtime_error(message.str());
468
142
  }
469
143
 
470
- template<typename Class_T, typename Function_T, bool IsMethod>
471
- void NativeFunction<Class_T, Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<std::optional<VALUE>>& rubyValues)
144
+ template<typename Function_T>
145
+ void NativeFunction<Function_T>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<std::optional<VALUE>>& rubyValues)
472
146
  {
473
147
  // Self will be Qnil for wrapped procs
474
148
  if (self == Qnil)
@@ -492,7 +166,7 @@ namespace Rice::detail
492
166
  }
493
167
 
494
168
  // Check return value
495
- if (this->methodInfo_->returnInfo.isKeepAlive())
169
+ if (this->methodInfo_->returnInfo()->isKeepAlive())
496
170
  {
497
171
  if (selfWrapper == nullptr)
498
172
  {
@@ -509,8 +183,8 @@ namespace Rice::detail
509
183
  }
510
184
  }
511
185
 
512
- template<typename Class_T, typename Function_T, bool IsMethod>
513
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::operator()(size_t argc, const VALUE* argv, VALUE self)
186
+ template<typename Function_T>
187
+ VALUE NativeFunction<Function_T>::operator()(size_t argc, const VALUE* argv, VALUE self)
514
188
  {
515
189
  // Get the ruby values and make sure we have the correct number
516
190
  std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(argc, argv, true);
@@ -520,15 +194,17 @@ namespace Rice::detail
520
194
  // Convert the Ruby values to native values
521
195
  Arg_Ts nativeValues = this->getNativeValues(rubyValues, indices);
522
196
 
523
- // Now call the native method
197
+ bool noGvl = this->methodInfo_->function()->isNoGvl();
198
+
524
199
  VALUE result = Qnil;
525
- if constexpr (std::is_same_v<Receiver_T, std::nullptr_t>)
200
+
201
+ if (noGvl)
526
202
  {
527
- result = this->invokeNativeFunction(std::forward<Arg_Ts>(nativeValues));
203
+ result = this->invokeNoGVL(std::forward<Arg_Ts>(nativeValues));
528
204
  }
529
205
  else
530
206
  {
531
- result = this->invokeNativeMethod(self, std::forward<Arg_Ts>(nativeValues));
207
+ result = this->invoke(std::forward<Arg_Ts>(nativeValues));
532
208
  }
533
209
 
534
210
  // Check if any function arguments or return values need to have their lifetimes tied to the receiver
@@ -536,4 +212,32 @@ namespace Rice::detail
536
212
 
537
213
  return result;
538
214
  }
215
+
216
+ template<typename Function_T>
217
+ inline std::string NativeFunction<Function_T>::name()
218
+ {
219
+ return this->method_name_;
220
+ }
221
+
222
+ template<typename Function_T>
223
+ inline NativeKind NativeFunction<Function_T>::kind()
224
+ {
225
+ return NativeKind::Function;
226
+ }
227
+
228
+ template<typename Function_T>
229
+ inline VALUE NativeFunction<Function_T>::returnKlass()
230
+ {
231
+ // Check if an array is being returned
232
+ if (this->methodInfo_->returnInfo()->isBuffer())
233
+ {
234
+ TypeMapper<Pointer<Return_T>> typeMapper;
235
+ return typeMapper.rubyKlass();
236
+ }
237
+ else
238
+ {
239
+ TypeMapper<Return_T> typeMapper;
240
+ return typeMapper.rubyKlass();
241
+ }
242
+ }
539
243
  }
@@ -0,0 +1,74 @@
1
+ #ifndef Rice__detail__native_invoker__hpp_
2
+ #define Rice__detail__native_invoker__hpp_
3
+
4
+ #include <optional>
5
+
6
+ namespace Rice::detail
7
+ {
8
+ template<typename Return_T>
9
+ class ResultWrapper
10
+ {
11
+ public:
12
+ std::exception_ptr exception;
13
+ Return_T getResult();
14
+ void setResult(Return_T&& value);
15
+ private:
16
+ std::optional<Return_T> result_;
17
+ };
18
+
19
+ template<typename Return_T>
20
+ class ResultWrapper<Return_T&>
21
+ {
22
+ public:
23
+ std::exception_ptr exception;
24
+ Return_T& getResult();
25
+ void setResult(Return_T& value);
26
+ private:
27
+ std::optional<std::reference_wrapper<Return_T>> result_;
28
+ };
29
+
30
+ template<typename Return_T>
31
+ class ResultWrapper<Return_T*>
32
+ {
33
+ public:
34
+ std::exception_ptr exception;
35
+
36
+ Return_T* getResult();
37
+ void setResult(Return_T* value);
38
+ private:
39
+ Return_T* result_;
40
+ };
41
+
42
+ template<>
43
+ class ResultWrapper<void>
44
+ {
45
+ public:
46
+ std::exception_ptr exception;
47
+ };
48
+
49
+ template<typename Return_T, typename Function_T, typename Tuple_T>
50
+ class NativeInvoker
51
+ {
52
+ public:
53
+ const Function_T func;
54
+ Tuple_T args;
55
+ public:
56
+ NativeInvoker(const Function_T func, Tuple_T&& args);
57
+ void invoke();
58
+ Return_T result();
59
+ std::exception_ptr exception();
60
+ private:
61
+ ResultWrapper<Return_T> resultWrapper;
62
+ };
63
+
64
+ template<typename Function_T, typename ...Arg_Ts>
65
+ auto protect(Function_T func, Arg_Ts...args);
66
+
67
+ template<typename Function_T, typename ...Arg_Ts>
68
+ typename function_traits<Function_T>::return_type no_gvl(Function_T func, Arg_Ts...args);
69
+
70
+ template<typename Function_T, typename Tuple_T>
71
+ typename function_traits<Function_T>::return_type no_gvl(Function_T func, Tuple_T&& args);
72
+ }
73
+
74
+ #endif // Rice__detail__native_invoker__hpp_