rice 4.7.1 → 4.8.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 (153) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -1
  3. data/CMakeLists.txt +14 -22
  4. data/CMakePresets.json +203 -75
  5. data/FindRuby.cmake +358 -123
  6. data/bin/rice-doc.rb +56 -141
  7. data/include/rice/api.hpp +248 -0
  8. data/include/rice/rice.hpp +2237 -1657
  9. data/include/rice/stl.hpp +346 -443
  10. data/lib/rice/doc/config.rb +70 -0
  11. data/lib/rice/doc/cpp_reference.rb +1 -4
  12. data/lib/rice/doc/mkdocs.rb +58 -20
  13. data/lib/rice/doc/rice.rb +20 -0
  14. data/lib/rice/doc.rb +1 -0
  15. data/lib/rice/make_rice_headers.rb +7 -0
  16. data/lib/rice/native_registry.rb +2 -2
  17. data/lib/rice/rbs.rb +2 -2
  18. data/lib/rice/version.rb +1 -1
  19. data/lib/rubygems_plugin.rb +12 -9
  20. data/rice/Arg.hpp +12 -6
  21. data/rice/Arg.ipp +14 -7
  22. data/rice/Buffer.ipp +44 -40
  23. data/rice/Callback.hpp +1 -1
  24. data/rice/Callback.ipp +2 -7
  25. data/rice/Constructor.hpp +1 -1
  26. data/rice/Constructor.ipp +11 -11
  27. data/rice/Data_Object.ipp +15 -15
  28. data/rice/Data_Type.hpp +9 -10
  29. data/rice/Data_Type.ipp +22 -25
  30. data/rice/Director.hpp +1 -0
  31. data/rice/Enum.ipp +58 -39
  32. data/rice/Exception.hpp +4 -4
  33. data/rice/Exception.ipp +7 -7
  34. data/rice/NoGVL.hpp +13 -0
  35. data/rice/Reference.hpp +56 -0
  36. data/rice/Reference.ipp +96 -0
  37. data/rice/Return.hpp +4 -1
  38. data/rice/Return.ipp +0 -6
  39. data/rice/cpp_api/Array.hpp +41 -4
  40. data/rice/cpp_api/Array.ipp +105 -9
  41. data/rice/cpp_api/Class.hpp +2 -2
  42. data/rice/cpp_api/Class.ipp +4 -4
  43. data/rice/cpp_api/Hash.ipp +7 -4
  44. data/rice/cpp_api/Module.hpp +4 -4
  45. data/rice/cpp_api/Module.ipp +12 -10
  46. data/rice/cpp_api/Object.hpp +4 -4
  47. data/rice/cpp_api/Object.ipp +15 -12
  48. data/rice/cpp_api/String.hpp +2 -2
  49. data/rice/cpp_api/String.ipp +11 -8
  50. data/rice/cpp_api/Symbol.ipp +7 -7
  51. data/rice/cpp_api/shared_methods.hpp +5 -9
  52. data/rice/detail/InstanceRegistry.hpp +0 -2
  53. data/rice/detail/Native.hpp +31 -21
  54. data/rice/detail/Native.ipp +282 -130
  55. data/rice/detail/NativeAttributeGet.hpp +5 -7
  56. data/rice/detail/NativeAttributeGet.ipp +26 -26
  57. data/rice/detail/NativeAttributeSet.hpp +2 -4
  58. data/rice/detail/NativeAttributeSet.ipp +20 -16
  59. data/rice/detail/NativeCallback.hpp +77 -0
  60. data/rice/detail/NativeCallback.ipp +280 -0
  61. data/rice/detail/NativeFunction.hpp +11 -21
  62. data/rice/detail/NativeFunction.ipp +58 -119
  63. data/rice/detail/NativeInvoker.hpp +4 -4
  64. data/rice/detail/NativeInvoker.ipp +7 -7
  65. data/rice/detail/NativeIterator.hpp +2 -4
  66. data/rice/detail/NativeIterator.ipp +18 -14
  67. data/rice/detail/NativeMethod.hpp +10 -20
  68. data/rice/detail/NativeMethod.ipp +54 -114
  69. data/rice/detail/NativeProc.hpp +5 -7
  70. data/rice/detail/NativeProc.ipp +39 -28
  71. data/rice/detail/NativeRegistry.hpp +0 -1
  72. data/rice/detail/Parameter.hpp +15 -8
  73. data/rice/detail/Parameter.ipp +102 -43
  74. data/rice/detail/Proc.ipp +14 -28
  75. data/rice/detail/RubyType.ipp +2 -53
  76. data/rice/detail/Type.hpp +23 -7
  77. data/rice/detail/Type.ipp +73 -93
  78. data/rice/detail/TypeRegistry.ipp +5 -4
  79. data/rice/detail/Wrapper.hpp +1 -1
  80. data/rice/detail/Wrapper.ipp +18 -10
  81. data/rice/detail/from_ruby.hpp +8 -6
  82. data/rice/detail/from_ruby.ipp +306 -173
  83. data/rice/detail/ruby.hpp +23 -0
  84. data/rice/libc/file.hpp +4 -4
  85. data/rice/rice.hpp +6 -8
  86. data/rice/rice_api/Native.ipp +5 -1
  87. data/rice/rice_api/Parameter.ipp +1 -1
  88. data/rice/ruby_mark.hpp +2 -1
  89. data/rice/stl/complex.ipp +12 -8
  90. data/rice/stl/map.ipp +27 -22
  91. data/rice/stl/monostate.ipp +16 -12
  92. data/rice/stl/multimap.hpp +0 -2
  93. data/rice/stl/multimap.ipp +27 -22
  94. data/rice/stl/optional.ipp +27 -11
  95. data/rice/stl/pair.ipp +5 -5
  96. data/rice/stl/reference_wrapper.ipp +5 -4
  97. data/rice/stl/set.ipp +16 -16
  98. data/rice/stl/shared_ptr.hpp +0 -16
  99. data/rice/stl/shared_ptr.ipp +34 -190
  100. data/rice/stl/string.ipp +18 -18
  101. data/rice/stl/string_view.ipp +1 -1
  102. data/rice/stl/tuple.ipp +15 -36
  103. data/rice/stl/unique_ptr.ipp +18 -8
  104. data/rice/stl/unordered_map.ipp +20 -15
  105. data/rice/stl/variant.ipp +37 -21
  106. data/rice/stl/vector.ipp +41 -36
  107. data/rice/traits/function_traits.hpp +19 -19
  108. data/rice/traits/method_traits.hpp +4 -4
  109. data/rice/traits/rice_traits.hpp +162 -39
  110. data/rice.gemspec +1 -3
  111. data/test/test_Array.cpp +261 -3
  112. data/test/test_Attribute.cpp +6 -3
  113. data/test/test_Buffer.cpp +6 -42
  114. data/test/test_Callback.cpp +77 -23
  115. data/test/test_Data_Object.cpp +1 -1
  116. data/test/test_Data_Type.cpp +21 -22
  117. data/test/test_Director.cpp +2 -4
  118. data/test/test_Enum.cpp +34 -5
  119. data/test/test_File.cpp +9 -5
  120. data/test/test_From_Ruby.cpp +4 -3
  121. data/test/test_GVL.cpp +3 -3
  122. data/test/test_Hash.cpp +1 -1
  123. data/test/test_Iterator.cpp +54 -22
  124. data/test/test_Keep_Alive.cpp +1 -1
  125. data/test/test_Keep_Alive_No_Wrapper.cpp +1 -1
  126. data/test/test_Module.cpp +5 -5
  127. data/test/test_Overloads.cpp +345 -48
  128. data/test/test_Proc.cpp +54 -0
  129. data/test/test_Reference.cpp +181 -0
  130. data/test/test_Self.cpp +2 -2
  131. data/test/test_Stl_Set.cpp +6 -6
  132. data/test/test_Stl_SharedPtr.cpp +54 -30
  133. data/test/test_Stl_String_View.cpp +4 -2
  134. data/test/test_Stl_Tuple.cpp +1 -1
  135. data/test/test_Stl_Variant.cpp +6 -14
  136. data/test/test_Stl_Vector.cpp +61 -30
  137. data/test/test_String.cpp +4 -2
  138. data/test/test_Struct.cpp +1 -1
  139. data/test/test_Symbol.cpp +1 -1
  140. data/test/test_To_Ruby.cpp +1 -0
  141. data/test/test_Type.cpp +36 -35
  142. data/test/test_global_functions.cpp +1 -1
  143. data/test/unittest.cpp +1 -1
  144. data/test/unittest.hpp +5 -5
  145. metadata +10 -10
  146. data/rice/Function.hpp +0 -17
  147. data/rice/Function.ipp +0 -13
  148. data/rice/detail/MethodInfo.hpp +0 -48
  149. data/rice/detail/MethodInfo.ipp +0 -99
  150. data/rice/detail/NativeCallbackFFI.hpp +0 -55
  151. data/rice/detail/NativeCallbackFFI.ipp +0 -152
  152. data/rice/detail/NativeCallbackSimple.hpp +0 -30
  153. data/rice/detail/NativeCallbackSimple.ipp +0 -29
@@ -1,58 +1,41 @@
1
-
2
1
  namespace Rice::detail
3
2
  {
4
3
  inline bool Resolved::operator<(Resolved other)
5
4
  {
6
- if (this->convertible != other.convertible)
7
- {
8
- return this->convertible < other.convertible;
9
- }
10
- else
11
- {
12
- return this->parameterMatch < other.parameterMatch;
13
- }
5
+ return this->score < other.score;
14
6
  }
15
7
 
16
8
  inline bool Resolved::operator>(Resolved other)
17
9
  {
18
- if (this->convertible != other.convertible)
19
- {
20
- return this->convertible > other.convertible;
21
- }
22
- else
23
- {
24
- return this->parameterMatch > other.parameterMatch;
25
- }
10
+ return this->score > other.score;
26
11
  }
27
12
 
28
13
  inline VALUE Native::resolve(int argc, VALUE* argv, VALUE self)
29
14
  {
30
15
  /* This method is called from Ruby and is responsible for determining the correct
31
- Native object (ie, NativeFunction, NativeIterator, NativeAttributeGet and
32
- NativeAttributeSet) that shoudl be used to invoke the underlying C++ code.
16
+ Native object (ie, NativeFunction, NativeIterator, NativeAttributeGet and
17
+ NativeAttributeSet) that should be used to invoke the underlying C++ code.
33
18
  Most of the time there will be a single Native object registered for a C++ function,
34
- method, constructor, iterator or attribute. However, there can be multiple Natives
35
- when a C++ function/method/construtor is overloaded.
19
+ method, constructor, iterator or attribute. However, there can be multiple Natives
20
+ when a C++ function/method/constructor is overloaded.
36
21
 
37
22
  In that case, the code iterates over each Native and calls its matches method. The matches
38
- method returns a Resolved object which includes a Convertible field and parameterMatch field.
39
- The Convertible field is an enum that specifies if the types of the values supplied by Ruby
40
- match the types of the C++ function parameters. Allowed values include can be Exact (example Ruby into to C++ int),
41
- TypeCast (example Ruby into to C++ float) or None (cannot be converted).
23
+ method returns a Resolved object with a numeric score (0.0 to 1.0). The score is computed as:
42
24
 
43
- The parameterMatch field is simply the number or arguments provided by Ruby divided by the
44
- number of arguments required by C++. These numbers can be different because C++ method
45
- parameters can have default values.
25
+ score = minParameterScore * parameterMatch
46
26
 
47
- Taking these two values into account, the method sorts the Natives and picks the one with the
48
- highest score (Convertible::Exact and 1.0 for parameterMatch). Thus given these two C++ functions:
27
+ where minParameterScore is the minimum score across all passed parameters (using precision-based
28
+ scoring for numeric types), and parameterMatch applies a small penalty (0.99) for each default
29
+ parameter used. If not enough arguments are provided and missing parameters don't have defaults,
30
+ the method returns 0 (not viable).
31
+
32
+ The method sorts the Natives and picks the one with the highest score. Given these two C++ functions:
49
33
 
50
34
  void some_method(int a);
51
- void some_mtehod(int a, float b = 2.0).
35
+ void some_method(int a, float b = 2.0);
52
36
 
53
- A call from ruby of some_method(1) will exactly match both signatures, but the first one
54
- will be chosen because the parameterMatch will be 1.0 for the first overload but 0.5
55
- for the second. */
37
+ A call from ruby of some_method(1) will match both signatures, but the first one
38
+ will be chosen because parameterMatch = 1.0 for the first overload but 0.99 for the second. */
56
39
 
57
40
  Native* native = nullptr;
58
41
 
@@ -64,8 +47,10 @@ namespace Rice::detail
64
47
  }
65
48
 
66
49
  // Execute the function but make sure to catch any C++ exceptions!
67
- return cpp_protect([&]
50
+ return cpp_protect([&]()
68
51
  {
52
+ std::map<std::string, VALUE> values = readRubyArgs(argc, argv);
53
+
69
54
  const std::vector<std::unique_ptr<Native>>& natives = Registries::instance.natives.lookup(klass, methodId);
70
55
 
71
56
  if (natives.size() == 1)
@@ -87,7 +72,7 @@ namespace Rice::detail
87
72
  std::back_inserter(resolves),
88
73
  [&](const std::unique_ptr<Native>& native)
89
74
  {
90
- return native->matches(argc, argv, self);
75
+ return native->matches(values);
91
76
  });
92
77
 
93
78
  // Now sort from best to worst
@@ -118,7 +103,7 @@ namespace Rice::detail
118
103
  }*/
119
104
 
120
105
  // Did it match?
121
- if (resolved.convertible != Convertible::None)
106
+ if (resolved.score > Convertible::None)
122
107
  {
123
108
  native = resolved.native;
124
109
  }
@@ -147,19 +132,34 @@ namespace Rice::detail
147
132
  }
148
133
 
149
134
  // Call the C++ function
150
- return (*native)(argc, argv, self);
135
+ return (*native)(values, self);
151
136
  });
152
137
  }
153
138
 
154
- inline Native::Native(std::vector<std::unique_ptr<ParameterAbstract>>&& parameters) : parameters_(std::move(parameters))
139
+ inline Native::Native(std::string name) :
140
+ name_(name)
155
141
  {
156
142
  }
157
143
 
144
+ inline Native::Native(std::string name,std::unique_ptr<Return>&& returnInfo) :
145
+ name_(name), returnInfo_(std::move(returnInfo))
146
+ {
147
+ }
148
+
149
+ inline Native::Native(std::string name, std::unique_ptr<Return>&& returnInfo, std::vector<std::unique_ptr<ParameterAbstract>>&& parameters) :
150
+ name_(name), returnInfo_(std::move(returnInfo)), parameters_(std::move(parameters))
151
+ {
152
+ }
153
+
154
+ inline std::string Native::name()
155
+ {
156
+ return this->name_;
157
+ }
158
158
  inline ParameterAbstract* Native::getParameterByName(std::string name)
159
159
  {
160
160
  for (std::unique_ptr<ParameterAbstract>& parameter : this->parameters_)
161
161
  {
162
- if (parameter->arg->name == name)
162
+ if (parameter->arg()->name == name)
163
163
  {
164
164
  return parameter.get();
165
165
  }
@@ -168,168 +168,320 @@ namespace Rice::detail
168
168
  return nullptr;
169
169
  }
170
170
 
171
- // ----------- Helpers ----------------
172
- template<typename T>
173
- inline void Native::verify_type(bool isBuffer)
171
+ inline void Native::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<std::optional<VALUE>>& rubyValues)
174
172
  {
175
- using Base_T = std::remove_pointer_t<remove_cv_recursive_t<T>>;
176
-
177
- detail::verifyType<T>();
178
-
179
- if constexpr (std::is_pointer_v<T> && std::is_fundamental_v<std::remove_pointer_t<T>>)
173
+ // Check function arguments
174
+ for (size_t i = 0; i < this->parameters_.size(); i++)
180
175
  {
181
- Type<Pointer<Base_T>>::verify();
182
- Type<Buffer<Base_T>>::verify();
176
+ Arg* arg = parameters_[i]->arg();
177
+ if (arg->isKeepAlive())
178
+ {
179
+ static WrapperBase* selfWrapper = getWrapper(self);
180
+ selfWrapper->addKeepAlive(rubyValues[i].value());
181
+ }
183
182
  }
184
- else if constexpr (std::is_array_v<T>)
183
+
184
+ // Check return value
185
+ if (this->returnInfo_->isKeepAlive())
185
186
  {
186
- Type<Pointer<std::remove_extent_t<remove_cv_recursive_t<T>>>>::verify();
187
- Type<Buffer<std::remove_extent_t<remove_cv_recursive_t<T>>>>::verify();
187
+ WrapperBase* returnWrapper = getWrapper(returnValue);
188
+ returnWrapper->addKeepAlive(self);
188
189
  }
189
- else if (isBuffer)
190
+ }
191
+
192
+ // ----------- Type Checking ----------------
193
+ template<typename T, bool isBuffer>
194
+ inline void Native::verify_type()
195
+ {
196
+ detail::verifyType<T>();
197
+
198
+ if constexpr (std::is_pointer_v<T>)
190
199
  {
191
- if constexpr (std::is_pointer_v<T> && !std::is_function_v<Base_T> && !std::is_abstract_v<Base_T>)
200
+ using Base_T = std::remove_pointer_t<remove_cv_recursive_t<T>>;
201
+
202
+ if constexpr (std::is_fundamental_v<Base_T> || std::is_pointer_v<Base_T> || isBuffer)
192
203
  {
193
204
  Type<Pointer<Base_T>>::verify();
194
- Type<Buffer<Base_T>>::verify();
195
205
  }
196
- else
206
+ }
207
+ else if constexpr (std::is_reference_v<T>)
208
+ {
209
+ using Base_T = std::remove_reference_t<remove_cv_recursive_t<T>>;
210
+
211
+ if constexpr (std::is_fundamental_v<Base_T>)
197
212
  {
198
- static_assert(true, "Only pointer types can be marked as buffers");
213
+ Type<Reference<Base_T>>::verify();
199
214
  }
200
215
  }
216
+ else if constexpr (std::is_array_v<T>)
217
+ {
218
+ using Base_T = std::remove_extent_t<remove_cv_recursive_t<T>>;
219
+
220
+ Type<Pointer<Base_T>>::verify();
221
+ }
201
222
  }
202
223
 
203
- template<typename Tuple_T, std::size_t ...Indices>
204
- inline void Native::verify_args(MethodInfo* methodInfo, std::index_sequence<Indices...> indices)
224
+ template<typename Parameter_Tuple, typename Arg_Tuple, size_t I>
225
+ inline void Native::verify_parameter()
205
226
  {
206
- (Native::verify_type<std::tuple_element_t<Indices, Tuple_T>>(methodInfo->arg(Indices)->isBuffer()), ...);
207
- }
227
+ using Param_T = std::tuple_element_t<I, Parameter_Tuple>;
228
+ using Arg_T = std::tuple_element_t<I, Arg_Tuple>;
229
+ if constexpr (std::is_same_v<ArgBuffer, std::decay_t<Arg_T>>)
230
+ {
231
+ verify_type<Param_T, true>();
232
+ }
233
+ else
234
+ {
235
+ verify_type<Param_T, false>();
236
+ }
237
+ };
208
238
 
209
- template<typename Tuple_T, std::size_t ...Indices>
210
- inline void Native::create_parameters_impl(std::vector<std::unique_ptr<ParameterAbstract>>& parameters, MethodInfo* methodInfo, std::index_sequence<Indices...> indices)
239
+ template<typename Parameter_Tuple, typename Arg_Tuple, std::size_t ...Indices>
240
+ inline void Native::create_parameters_impl(std::vector<std::unique_ptr<ParameterAbstract>>& parameters, std::index_sequence<Indices...>, std::vector<std::unique_ptr<Arg>>&& args)
211
241
  {
212
- (parameters.push_back(std::move(std::make_unique<Parameter<std::tuple_element_t<Indices, Tuple_T>>>(methodInfo->arg(Indices)))), ...);
242
+ // Verify parameter types
243
+ (verify_parameter<Parameter_Tuple, Arg_Tuple, Indices>(), ...);
244
+
245
+ // Create parameters
246
+ (parameters.push_back(std::move(std::make_unique<
247
+ Parameter<std::tuple_element_t<Indices, Parameter_Tuple>>>(std::move(args[Indices])))), ...);
213
248
  }
214
249
 
215
- template<typename Tuple_T>
216
- inline std::vector<std::unique_ptr<ParameterAbstract>> Native::create_parameters(MethodInfo* methodInfo)
250
+ template<typename Parameter_Tuple, typename... Arg_Ts>
251
+ inline std::vector<std::unique_ptr<ParameterAbstract>> Native::create_parameters(Arg_Ts&& ...args)
217
252
  {
218
253
  std::vector<std::unique_ptr<ParameterAbstract>> result;
219
- auto indices = std::make_index_sequence<std::tuple_size_v<Tuple_T>>{};
220
- Native::create_parameters_impl<Tuple_T>(result, methodInfo, indices);
254
+
255
+ // Extract Arg and ArgBuffer from Arg_Ts and then pad Arg to match the size of Parameter_Tuple
256
+ using ArgsBaseTuple = tuple_filter_types_t<std::tuple<Arg_Ts...>, Arg, ArgBuffer>;
257
+
258
+ // Diff can be less than zero so it has to be signed! This happens when define_method is called with a self
259
+ // parameter and specifies one or more Args (usually to call Arg("self).setValue()).
260
+ // In that case the self parameter is considered Class_T and there are no arguments.
261
+ constexpr long diff = (long)std::tuple_size_v<Parameter_Tuple> - (long)std::tuple_size_v<ArgsBaseTuple>;
262
+ using ArgsTuple = tuple_pad_type_t<ArgsBaseTuple, Arg, diff < 0 ? 0 : diff>;
263
+
264
+ // Now play the same game but with the tuple values instead of types
265
+ std::vector<std::unique_ptr<Arg>> argsVector;
266
+
267
+ // Loop over each arg with an anonymous lambda
268
+ ([&]
269
+ {
270
+ using Arg_T = std::decay_t<Arg_Ts>;
271
+
272
+ if constexpr (std::is_same_v<Arg, Arg_T> || std::is_same_v<ArgBuffer, Arg_T>)
273
+ {
274
+ argsVector.emplace_back(std::make_unique<Arg_T>(args));
275
+ }
276
+ }(), ...);
277
+
278
+ // Fill in missing args
279
+ for (size_t i = argsVector.size(); i < std::tuple_size_v<Parameter_Tuple>; i++)
280
+ {
281
+ std::string argName = "arg_" + std::to_string(i);
282
+ argsVector.emplace_back(std::make_unique<Arg>(argName));
283
+ }
284
+
285
+ auto indices = std::make_index_sequence<std::tuple_size_v<Parameter_Tuple>>{};
286
+ Native::create_parameters_impl<Parameter_Tuple, ArgsTuple>(result, indices, std::move(argsVector));
221
287
  return result;
222
288
  }
223
289
 
224
- inline std::vector<std::optional<VALUE>> Native::getRubyValues(size_t argc, const VALUE* argv, bool validate)
290
+ template<typename... Arg_Ts>
291
+ inline std::unique_ptr<Return> Native::create_return(Arg_Ts& ...args)
225
292
  {
226
- #undef max
227
- int size = std::max(this->parameters_.size(), argc);
228
- std::vector<std::optional<VALUE>> result(size);
293
+ using Arg_Tuple = std::tuple<Arg_Ts...>;
294
+
295
+ constexpr std::size_t index = tuple_element_index_v<Arg_Tuple, Return, ReturnBuffer>;
296
+
297
+ std::unique_ptr<Return> result;
298
+
299
+ if constexpr (index < std::tuple_size_v<Arg_Tuple>)
300
+ {
301
+ using Return_T_Local = std::decay_t<std::tuple_element_t<index, Arg_Tuple>>;
302
+ const Return_T_Local& returnInfo = std::get<index>(std::forward_as_tuple(std::forward<Arg_Ts>(args)...));
303
+ result = std::make_unique<Return_T_Local>(returnInfo);
304
+ }
305
+ else
306
+ {
307
+ result = std::make_unique<Return>();
308
+ }
309
+
310
+ return result;
311
+ }
312
+
313
+ inline std::map<std::string, VALUE> Native::readRubyArgs(size_t argc, const VALUE* argv)
314
+ {
315
+ std::map<std::string, VALUE> result;
229
316
 
230
317
  // Keyword handling
231
318
  if (rb_keyword_given_p())
232
319
  {
233
320
  // Keywords are stored in the last element in a hash
234
- int actualArgc = argc - 1;
235
-
236
- VALUE value = argv[actualArgc];
237
- Hash keywords(value);
321
+ size_t actualArgc = argc - 1;
238
322
 
239
323
  // Copy over leading non-keyword arguments
240
- for (int i = 0; i < actualArgc; i++)
324
+ for (size_t i = 0; i < actualArgc; i++)
241
325
  {
242
- result[i] = argv[i];
326
+ std::string key = "arg_" + std::to_string(i);
327
+ result[key] = argv[i];
243
328
  }
244
329
 
330
+ VALUE value = argv[actualArgc];
331
+ Hash keywords(value);
332
+
245
333
  // Copy over keyword arguments
246
334
  for (auto pair : keywords)
247
335
  {
248
- Symbol key(pair.first);
249
- ParameterAbstract* parameter = this->getParameterByName(key.str());
250
- if (!parameter)
251
- {
252
- throw std::invalid_argument("Unknown keyword: " + key.str());
253
- }
254
-
255
- const Arg* arg = parameter->arg;
256
-
257
- result[arg->position] = pair.second.value();
336
+ result[pair.first.to_s().str()] = pair.second.value();
258
337
  }
259
338
  }
260
339
  else
261
340
  {
262
- std::copy(argv, argv + argc, result.begin());
341
+ // Copy over leading non-keyword arguments
342
+ for (size_t i = 0; i < argc; i++)
343
+ {
344
+ std::string key = "arg_" + std::to_string(i);
345
+ result[key] = argv[i];
346
+ }
263
347
  }
264
348
 
265
- // Block handling. If we find a block and the last parameter is missing then
266
- // set it to the block
267
- if (rb_block_given_p() && result.size() > 0 && !result.back().has_value())
349
+ return result;
350
+ }
351
+
352
+ inline std::vector<std::optional<VALUE>> Native::getRubyValues(std::map<std::string, VALUE> values, bool validate)
353
+ {
354
+ // !!!NOTE!!! We copied the values parameter because we are going to modify it!
355
+
356
+ // Protect against user sending too many arguments
357
+ if (values.size() > this->parameters_.size())
268
358
  {
269
- VALUE proc = rb_block_proc();
270
- result.back() = proc;
359
+ std::string message = "wrong number of arguments (given " +
360
+ std::to_string(values.size()) + ", expected " + std::to_string(this->parameters_.size()) + ")";
361
+ throw std::invalid_argument(message);
271
362
  }
272
363
 
273
- if (validate)
364
+ std::vector<std::optional<VALUE>> result(this->parameters_.size());
365
+
366
+ for (size_t i=0; i< this->parameters_.size(); i++)
274
367
  {
275
- // Protect against user sending too many arguments
276
- if (argc > this->parameters_.size())
368
+ std::unique_ptr<ParameterAbstract>& parameter = this->parameters_[i];
369
+ Arg* arg = parameter->arg();
370
+
371
+ // If using keywords arguments, then the value key will be arg->name(). If using positional
372
+ // arguments then they key will be "arg_<position>"
373
+ std::string keywordKey = arg->name;
374
+ std::string positionKey = "arg_" + std::to_string(i);
375
+
376
+ auto iter = values.find(keywordKey);
377
+ if (iter == values.end() && keywordKey != positionKey)
277
378
  {
278
- std::string message = "wrong number of arguments (given " +
279
- std::to_string(argc) + ", expected " + std::to_string(this->parameters_.size()) + ")";
280
- throw std::invalid_argument(message);
379
+ iter = values.find(positionKey);
281
380
  }
282
381
 
283
- for (size_t i = 0; i < result.size(); i++)
382
+ if (iter != values.end())
284
383
  {
285
- std::optional<VALUE> value = result[i];
286
- ParameterAbstract* parameter = this->parameters_[i].get();
384
+ result[i] = iter->second;
385
+ // Remove the value
386
+ values.erase(iter);
387
+ }
388
+ else if (arg->hasDefaultValue())
389
+ {
390
+ result[i] = parameter->defaultValueRuby();
391
+ }
392
+ else if (arg->isBlock() && rb_block_given_p())
393
+ {
394
+ result[i] = protect(rb_block_proc);
395
+ }
396
+ else if (validate)
397
+ {
398
+ std::string message = "Missing argument. Name: " + arg->name + ". Index: " + std::to_string(i) + ".";
399
+ throw std::invalid_argument(message);
400
+ }
401
+ else
402
+ {
403
+ // No point in continuing - this native is not going to match
404
+ return result;
405
+ }
406
+ }
287
407
 
288
- if (!parameter->arg->hasDefaultValue() && !value.has_value())
289
- {
290
- std::string message;
291
- message = "Missing argument. Name: " + parameter->arg->name + ". Index: " + std::to_string(parameter->arg->position) + ".";
292
- throw std::invalid_argument(message);
293
- }
408
+ // Check for unknown arguments
409
+ if (validate && values.size() > 0)
410
+ {
411
+ // There are unknown arguments
412
+ std::ostringstream message;
413
+ message << "Unknown argument(s): ";
414
+ size_t count = 0;
415
+ for (const std::pair<const std::string, VALUE>& pair : values)
416
+ {
417
+ if (count > 0)
418
+ message << ", ";
419
+ message << pair.first;
420
+ count++;
294
421
  }
422
+ throw std::invalid_argument(message.str());
295
423
  }
296
424
 
297
425
  return result;
298
426
  }
299
427
 
300
- inline Convertible Native::matchParameters(std::vector<std::optional<VALUE>>& values)
428
+ inline double Native::matchParameters(std::vector<std::optional<VALUE>>& values, size_t argc)
301
429
  {
302
- Convertible result = Convertible::Exact;
303
- for (size_t i = 0; i < this->parameters_.size(); i++)
430
+ // Only score arguments actually passed (not defaults)
431
+ double minScore = Convertible::Exact;
432
+
433
+ for (size_t i = 0; i < argc && i < this->parameters_.size(); i++)
304
434
  {
305
435
  ParameterAbstract* parameter = this->parameters_[i].get();
306
436
  std::optional<VALUE>& value = values[i];
307
- result = result & parameter->matches(value);
437
+ double score = parameter->matches(value);
438
+ minScore = (std::min)(minScore, score);
308
439
  }
309
- return result;
440
+
441
+ return minScore;
310
442
  }
311
443
 
312
- inline Resolved Native::matches(size_t argc, const VALUE* argv, VALUE self)
444
+ inline Resolved Native::matches(std::map<std::string, VALUE>& values)
313
445
  {
314
- // Return false if Ruby provided more arguments than the C++ method takes
315
- if (argc > this->parameters_.size())
316
- return Resolved{ Convertible::None, 0, this };
317
-
318
- Resolved result{ Convertible::Exact, 1, this };
446
+ // Return Convertible::None if Ruby provided more arguments than the C++ method takes
447
+ if (values.size() > this->parameters_.size())
448
+ {
449
+ return Resolved{ Convertible::None, this };
450
+ }
319
451
 
320
- std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(argc, argv, false);
321
- result.convertible = this->matchParameters(rubyValues);
452
+ // Get Ruby values for each parameter and see how they match
453
+ std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(values, false);
454
+ double minScore = this->matchParameters(rubyValues, values.size());
322
455
 
323
- if (this->parameters_.size() > 0)
456
+ // If zero score return then stop
457
+ if (minScore == 0)
324
458
  {
325
- int providedValues = std::count_if(rubyValues.begin(), rubyValues.end(), [](std::optional<VALUE>& value)
326
- {
327
- return value.has_value();
328
- });
459
+ return Resolved{ Convertible::None, this };
460
+ }
329
461
 
330
- result.parameterMatch = providedValues / (double)this->parameters_.size();
462
+ // How many actual values do we have?
463
+ size_t actualValuesCount = std::count_if(rubyValues.begin(), rubyValues.end(),
464
+ [](std::optional<VALUE>& optional)
465
+ {
466
+ return optional.has_value();
467
+ });
468
+
469
+ // If we don't have enough parameters return
470
+ if (actualValuesCount < this->parameters_.size())
471
+ return Resolved{ Convertible::None, this };
472
+
473
+ // Penalize use of default parameters
474
+ double parameterMatch = Convertible::Exact;
475
+ size_t defaultParameterCount = actualValuesCount - values.size();
476
+ for (size_t i = 0; i < defaultParameterCount; i++)
477
+ {
478
+ parameterMatch *= 0.99; // Small penalty per default used
331
479
  }
332
- return result;
480
+
481
+ // Final score: minScore * parameterMatch
482
+ double finalScore = minScore * parameterMatch;
483
+
484
+ return Resolved{ finalScore, this };
333
485
  }
334
486
 
335
487
  inline std::vector<const ParameterAbstract*> Native::parameters()
@@ -24,7 +24,8 @@ namespace Rice
24
24
 
25
25
  public:
26
26
  // Register attribute getter with Ruby
27
- static void define(VALUE klass, std::string name, Attribute_T attribute, Return returnInfo);
27
+ template<typename...Arg_Ts>
28
+ static void define(VALUE klass, std::string name, Attribute_T attribute, Arg_Ts&...args);
28
29
 
29
30
  public:
30
31
  // Disallow creating/copying/moving
@@ -34,22 +35,19 @@ namespace Rice
34
35
  void operator=(const NativeAttribute_T&) = delete;
35
36
  void operator=(NativeAttribute_T&&) = delete;
36
37
 
37
- Resolved matches(size_t argc, const VALUE* argv, VALUE self) override;
38
- VALUE operator()(size_t argc, const VALUE* argv, VALUE self) override;
38
+ Resolved matches(std::map<std::string, VALUE>& values) override;
39
+ VALUE operator()(std::map<std::string, VALUE>& values, VALUE self) override;
39
40
  std::string toString() override;
40
41
 
41
- std::string name() override;
42
42
  NativeKind kind() override;
43
43
  VALUE returnKlass() override;
44
44
 
45
45
  protected:
46
- NativeAttributeGet(VALUE klass, std::string name, Attribute_T attr, Return returnInfo);
46
+ NativeAttributeGet(VALUE klass, std::string name, Attribute_T attr, std::unique_ptr<Return>&& returnInfo);
47
47
 
48
48
  private:
49
49
  VALUE klass_;
50
- std::string name_;
51
50
  Attribute_T attribute_;
52
- Return return_;
53
51
  };
54
52
  } // detail
55
53
  } // Rice