rice 4.7.0 → 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 (155) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -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 -142
  7. data/bin/rice-rbs.rb +1 -2
  8. data/include/rice/api.hpp +248 -0
  9. data/include/rice/rice.hpp +2281 -1668
  10. data/include/rice/stl.hpp +364 -443
  11. data/lib/rice/doc/config.rb +70 -0
  12. data/lib/rice/doc/cpp_reference.rb +1 -4
  13. data/lib/rice/doc/mkdocs.rb +58 -20
  14. data/lib/rice/doc/rice.rb +20 -0
  15. data/lib/rice/doc.rb +1 -0
  16. data/lib/rice/make_rice_headers.rb +7 -0
  17. data/lib/rice/native_registry.rb +2 -2
  18. data/lib/rice/rbs.rb +4 -4
  19. data/lib/rice/version.rb +1 -1
  20. data/lib/rubygems_plugin.rb +12 -9
  21. data/rice/Arg.hpp +12 -6
  22. data/rice/Arg.ipp +14 -7
  23. data/rice/Buffer.ipp +44 -40
  24. data/rice/Callback.hpp +1 -1
  25. data/rice/Callback.ipp +2 -7
  26. data/rice/Constructor.hpp +1 -1
  27. data/rice/Constructor.ipp +11 -11
  28. data/rice/Data_Object.ipp +59 -30
  29. data/rice/Data_Type.hpp +9 -10
  30. data/rice/Data_Type.ipp +22 -25
  31. data/rice/Director.hpp +1 -0
  32. data/rice/Enum.ipp +58 -39
  33. data/rice/Exception.hpp +4 -4
  34. data/rice/Exception.ipp +7 -7
  35. data/rice/NoGVL.hpp +13 -0
  36. data/rice/Reference.hpp +56 -0
  37. data/rice/Reference.ipp +96 -0
  38. data/rice/Return.hpp +4 -1
  39. data/rice/Return.ipp +0 -6
  40. data/rice/cpp_api/Array.hpp +44 -7
  41. data/rice/cpp_api/Array.ipp +105 -9
  42. data/rice/cpp_api/Class.hpp +2 -2
  43. data/rice/cpp_api/Class.ipp +4 -4
  44. data/rice/cpp_api/Hash.ipp +7 -4
  45. data/rice/cpp_api/Module.hpp +4 -4
  46. data/rice/cpp_api/Module.ipp +12 -10
  47. data/rice/cpp_api/Object.hpp +4 -4
  48. data/rice/cpp_api/Object.ipp +15 -12
  49. data/rice/cpp_api/String.hpp +2 -2
  50. data/rice/cpp_api/String.ipp +11 -8
  51. data/rice/cpp_api/Symbol.ipp +16 -7
  52. data/rice/cpp_api/shared_methods.hpp +5 -9
  53. data/rice/detail/InstanceRegistry.hpp +0 -2
  54. data/rice/detail/Native.hpp +31 -21
  55. data/rice/detail/Native.ipp +281 -133
  56. data/rice/detail/NativeAttributeGet.hpp +5 -7
  57. data/rice/detail/NativeAttributeGet.ipp +26 -26
  58. data/rice/detail/NativeAttributeSet.hpp +2 -4
  59. data/rice/detail/NativeAttributeSet.ipp +20 -16
  60. data/rice/detail/NativeCallback.hpp +77 -0
  61. data/rice/detail/NativeCallback.ipp +280 -0
  62. data/rice/detail/NativeFunction.hpp +11 -21
  63. data/rice/detail/NativeFunction.ipp +58 -119
  64. data/rice/detail/NativeInvoker.hpp +4 -4
  65. data/rice/detail/NativeInvoker.ipp +7 -7
  66. data/rice/detail/NativeIterator.hpp +2 -4
  67. data/rice/detail/NativeIterator.ipp +18 -14
  68. data/rice/detail/NativeMethod.hpp +10 -20
  69. data/rice/detail/NativeMethod.ipp +54 -114
  70. data/rice/detail/NativeProc.hpp +5 -7
  71. data/rice/detail/NativeProc.ipp +39 -28
  72. data/rice/detail/NativeRegistry.hpp +0 -1
  73. data/rice/detail/NativeRegistry.ipp +0 -1
  74. data/rice/detail/Parameter.hpp +15 -8
  75. data/rice/detail/Parameter.ipp +102 -43
  76. data/rice/detail/Proc.ipp +14 -28
  77. data/rice/detail/RubyType.ipp +2 -53
  78. data/rice/detail/Type.hpp +23 -7
  79. data/rice/detail/Type.ipp +73 -93
  80. data/rice/detail/TypeRegistry.ipp +5 -4
  81. data/rice/detail/Wrapper.hpp +1 -1
  82. data/rice/detail/Wrapper.ipp +18 -10
  83. data/rice/detail/from_ruby.hpp +8 -6
  84. data/rice/detail/from_ruby.ipp +306 -173
  85. data/rice/detail/ruby.hpp +23 -0
  86. data/rice/libc/file.hpp +4 -4
  87. data/rice/rice.hpp +6 -8
  88. data/rice/rice_api/Native.ipp +5 -1
  89. data/rice/rice_api/Parameter.ipp +1 -1
  90. data/rice/ruby_mark.hpp +2 -1
  91. data/rice/stl/complex.ipp +12 -8
  92. data/rice/stl/map.ipp +27 -22
  93. data/rice/stl/monostate.ipp +16 -12
  94. data/rice/stl/multimap.hpp +0 -2
  95. data/rice/stl/multimap.ipp +27 -22
  96. data/rice/stl/optional.ipp +27 -11
  97. data/rice/stl/pair.ipp +5 -5
  98. data/rice/stl/reference_wrapper.ipp +5 -4
  99. data/rice/stl/set.ipp +16 -16
  100. data/rice/stl/shared_ptr.hpp +0 -16
  101. data/rice/stl/shared_ptr.ipp +34 -190
  102. data/rice/stl/string.ipp +18 -18
  103. data/rice/stl/string_view.ipp +19 -1
  104. data/rice/stl/tuple.ipp +15 -36
  105. data/rice/stl/unique_ptr.ipp +18 -8
  106. data/rice/stl/unordered_map.ipp +20 -15
  107. data/rice/stl/variant.ipp +37 -21
  108. data/rice/stl/vector.ipp +41 -36
  109. data/rice/traits/function_traits.hpp +19 -19
  110. data/rice/traits/method_traits.hpp +4 -4
  111. data/rice/traits/rice_traits.hpp +162 -39
  112. data/rice.gemspec +1 -4
  113. data/test/test_Array.cpp +261 -3
  114. data/test/test_Attribute.cpp +6 -3
  115. data/test/test_Buffer.cpp +6 -42
  116. data/test/test_Callback.cpp +77 -23
  117. data/test/test_Data_Object.cpp +2 -2
  118. data/test/test_Data_Type.cpp +23 -23
  119. data/test/test_Director.cpp +2 -4
  120. data/test/test_Enum.cpp +34 -5
  121. data/test/test_File.cpp +9 -5
  122. data/test/test_From_Ruby.cpp +7 -6
  123. data/test/test_GVL.cpp +3 -3
  124. data/test/test_Hash.cpp +1 -1
  125. data/test/test_Iterator.cpp +54 -22
  126. data/test/test_Keep_Alive.cpp +1 -1
  127. data/test/test_Keep_Alive_No_Wrapper.cpp +1 -1
  128. data/test/test_Module.cpp +5 -5
  129. data/test/test_Overloads.cpp +395 -50
  130. data/test/test_Proc.cpp +54 -0
  131. data/test/test_Reference.cpp +181 -0
  132. data/test/test_Self.cpp +2 -2
  133. data/test/test_Stl_Set.cpp +6 -6
  134. data/test/test_Stl_SharedPtr.cpp +54 -30
  135. data/test/test_Stl_String_View.cpp +12 -0
  136. data/test/test_Stl_Tuple.cpp +1 -1
  137. data/test/test_Stl_Variant.cpp +6 -14
  138. data/test/test_Stl_Vector.cpp +61 -30
  139. data/test/test_String.cpp +4 -2
  140. data/test/test_Struct.cpp +1 -1
  141. data/test/test_Symbol.cpp +12 -0
  142. data/test/test_To_Ruby.cpp +1 -0
  143. data/test/test_Type.cpp +36 -35
  144. data/test/test_global_functions.cpp +1 -1
  145. data/test/unittest.cpp +1 -1
  146. data/test/unittest.hpp +5 -5
  147. metadata +10 -24
  148. data/rice/Function.hpp +0 -17
  149. data/rice/Function.ipp +0 -13
  150. data/rice/detail/MethodInfo.hpp +0 -48
  151. data/rice/detail/MethodInfo.ipp +0 -99
  152. data/rice/detail/NativeCallbackFFI.hpp +0 -55
  153. data/rice/detail/NativeCallbackFFI.ipp +0 -152
  154. data/rice/detail/NativeCallbackSimple.hpp +0 -30
  155. data/rice/detail/NativeCallbackSimple.ipp +0 -29
@@ -11,8 +11,7 @@ namespace Rice::detail
11
11
  inline bool operator<(Resolved other);
12
12
  inline bool operator>(Resolved other);
13
13
 
14
- Convertible convertible;
15
- double parameterMatch;
14
+ double score; // Combined score: minParameterScore * parameterMatch
16
15
  Native* native;
17
16
  };
18
17
 
@@ -23,7 +22,8 @@ namespace Rice::detail
23
22
  Iterator,
24
23
  AttributeReader,
25
24
  AttributeWriter,
26
- Proc
25
+ Proc,
26
+ Callback
27
27
  };
28
28
 
29
29
  class Native
@@ -31,8 +31,9 @@ namespace Rice::detail
31
31
  public:
32
32
  static VALUE resolve(int argc, VALUE* argv, VALUE self);
33
33
  public:
34
- Native() = default;
35
- Native(std::vector<std::unique_ptr<ParameterAbstract>>&& parameters);
34
+ Native(std::string name);
35
+ Native(std::string name, std::unique_ptr<Return>&& returnInfo);
36
+ Native(std::string name, std::unique_ptr<Return>&& returnInfo, std::vector<std::unique_ptr<ParameterAbstract>>&& parameters);
36
37
  virtual ~Native() = default;
37
38
 
38
39
  Native(const Native&) = delete;
@@ -40,36 +41,45 @@ namespace Rice::detail
40
41
  void operator=(const Native&) = delete;
41
42
  void operator=(Native&&) = delete;
42
43
 
43
- virtual Resolved matches(size_t argc, const VALUE* argv, VALUE self);
44
- virtual VALUE operator()(size_t argc, const VALUE* argv, VALUE self) = 0;
44
+ virtual Resolved matches(std::map<std::string, VALUE>& values);
45
+ virtual VALUE operator()(std::map<std::string, VALUE>& values, VALUE self) = 0;
45
46
  virtual std::string toString() = 0;
46
47
 
47
48
  // Ruby API access
48
- virtual std::string name() = 0;
49
+ std::string name();
50
+ std::vector<const ParameterAbstract*> parameters();
49
51
  virtual NativeKind kind() = 0;
50
52
  virtual VALUE returnKlass() = 0;
51
- std::vector<const ParameterAbstract*> parameters();
52
53
 
53
54
  protected:
54
- template<typename T>
55
- static void verify_type(bool isBuffer);
55
+ template<typename T, bool isBuffer>
56
+ static void verify_type();
56
57
 
57
- template<typename Tuple_T, std::size_t ...Indices>
58
- static void verify_args(MethodInfo* methodInfo, std::index_sequence<Indices...> indices);
59
-
60
- std::vector<std::optional<VALUE>> getRubyValues(size_t argc, const VALUE* argv, bool validate);
58
+ static std::map<std::string, VALUE> readRubyArgs(size_t argc, const VALUE* argv);
59
+ std::vector<std::optional<VALUE>> getRubyValues(std::map<std::string, VALUE> values, bool validate);
61
60
  ParameterAbstract* getParameterByName(std::string name);
62
- Convertible matchParameters(std::vector<std::optional<VALUE>>& values);
61
+ double matchParameters(std::vector<std::optional<VALUE>>& values, size_t argc);
62
+
63
+ template<typename Parameter_Tuple, typename... Arg_Ts>
64
+ static std::vector<std::unique_ptr<ParameterAbstract>> create_parameters(Arg_Ts&& ...args);
63
65
 
64
- template<typename Tuple_T>
65
- static std::vector<std::unique_ptr<ParameterAbstract>> create_parameters(MethodInfo* methodInfo);
66
+ template<typename... Arg_Ts>
67
+ static std::unique_ptr<Return> create_return(Arg_Ts& ...args);
66
68
 
67
- template<typename Tuple_T, std::size_t ...Indices>
68
- static inline void create_parameters_impl(std::vector<std::unique_ptr<ParameterAbstract>>& parameters, MethodInfo* methodInfo, std::index_sequence<Indices...> indices);
69
+ // Do we need to keep alive any arguments?
70
+ void checkKeepAlive(VALUE self, VALUE returnValue, std::vector<std::optional<VALUE>>& rubyValues);
71
+
72
+ private:
73
+ template<typename Parameter_Tuple, typename Arg_Tuple, std::size_t ...Indices>
74
+ static inline void create_parameters_impl(std::vector<std::unique_ptr<ParameterAbstract>>& parameters, std::index_sequence<Indices...> indices, std::vector<std::unique_ptr<Arg>>&& args);
75
+
76
+ template<typename Parameter_Tuple, typename Arg_Tuple, size_t I>
77
+ static void verify_parameter();
69
78
 
70
79
  protected:
80
+ std::string name_;
81
+ std::unique_ptr<Return> returnInfo_;
71
82
  std::vector<std::unique_ptr<ParameterAbstract>> parameters_;
72
-
73
83
  };
74
84
  }
75
85
 
@@ -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:
24
+
25
+ score = minParameterScore * parameterMatch
42
26
 
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.
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).
46
31
 
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:
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,11 +47,9 @@ 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
  {
69
- Identifier id(methodId);
70
- std::string methodName = id.str();
71
- std::string className = rb_class2name(klass);
52
+ std::map<std::string, VALUE> values = readRubyArgs(argc, argv);
72
53
 
73
54
  const std::vector<std::unique_ptr<Native>>& natives = Registries::instance.natives.lookup(klass, methodId);
74
55
 
@@ -91,7 +72,7 @@ namespace Rice::detail
91
72
  std::back_inserter(resolves),
92
73
  [&](const std::unique_ptr<Native>& native)
93
74
  {
94
- return native->matches(argc, argv, self);
75
+ return native->matches(values);
95
76
  });
96
77
 
97
78
  // Now sort from best to worst
@@ -122,7 +103,7 @@ namespace Rice::detail
122
103
  }*/
123
104
 
124
105
  // Did it match?
125
- if (resolved.convertible != Convertible::None)
106
+ if (resolved.score > Convertible::None)
126
107
  {
127
108
  native = resolved.native;
128
109
  }
@@ -151,19 +132,34 @@ namespace Rice::detail
151
132
  }
152
133
 
153
134
  // Call the C++ function
154
- return (*native)(argc, argv, self);
135
+ return (*native)(values, self);
155
136
  });
156
137
  }
157
138
 
158
- inline Native::Native(std::vector<std::unique_ptr<ParameterAbstract>>&& parameters) : parameters_(std::move(parameters))
139
+ inline Native::Native(std::string name) :
140
+ name_(name)
141
+ {
142
+ }
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))
159
151
  {
160
152
  }
161
153
 
154
+ inline std::string Native::name()
155
+ {
156
+ return this->name_;
157
+ }
162
158
  inline ParameterAbstract* Native::getParameterByName(std::string name)
163
159
  {
164
160
  for (std::unique_ptr<ParameterAbstract>& parameter : this->parameters_)
165
161
  {
166
- if (parameter->arg->name == name)
162
+ if (parameter->arg()->name == name)
167
163
  {
168
164
  return parameter.get();
169
165
  }
@@ -172,168 +168,320 @@ namespace Rice::detail
172
168
  return nullptr;
173
169
  }
174
170
 
175
- // ----------- Helpers ----------------
176
- template<typename T>
177
- inline void Native::verify_type(bool isBuffer)
171
+ inline void Native::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<std::optional<VALUE>>& rubyValues)
178
172
  {
179
- using Base_T = std::remove_pointer_t<remove_cv_recursive_t<T>>;
180
-
181
- detail::verifyType<T>();
182
-
183
- 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++)
184
175
  {
185
- Type<Pointer<std::remove_pointer_t<T>>>::verify();
186
- Type<Buffer<std::remove_pointer_t<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
+ }
187
182
  }
188
- if constexpr (std::is_array_v<T>)
183
+
184
+ // Check return value
185
+ if (this->returnInfo_->isKeepAlive())
189
186
  {
190
- Type<Pointer<std::remove_extent_t<remove_cv_recursive_t<T>>>>::verify();
191
- Type<Buffer<std::remove_extent_t<remove_cv_recursive_t<T>>>>::verify();
187
+ WrapperBase* returnWrapper = getWrapper(returnValue);
188
+ returnWrapper->addKeepAlive(self);
192
189
  }
193
- 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>)
194
199
  {
195
- 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)
196
203
  {
197
204
  Type<Pointer<Base_T>>::verify();
198
- Type<Buffer<Base_T>>::verify();
199
205
  }
200
- 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>)
201
212
  {
202
- static_assert(true, "Only pointer types can be marked as buffers");
213
+ Type<Reference<Base_T>>::verify();
203
214
  }
204
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
+ }
205
222
  }
206
223
 
207
- template<typename Tuple_T, std::size_t ...Indices>
208
- 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()
209
226
  {
210
- (Native::verify_type<std::tuple_element_t<Indices, Tuple_T>>(methodInfo->arg(Indices)->isBuffer()), ...);
211
- }
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
+ };
212
238
 
213
- template<typename Tuple_T, std::size_t ...Indices>
214
- 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)
215
241
  {
216
- (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])))), ...);
217
248
  }
218
249
 
219
- template<typename Tuple_T>
220
- 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)
221
252
  {
222
253
  std::vector<std::unique_ptr<ParameterAbstract>> result;
223
- auto indices = std::make_index_sequence<std::tuple_size_v<Tuple_T>>{};
224
- 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));
287
+ return result;
288
+ }
289
+
290
+ template<typename... Arg_Ts>
291
+ inline std::unique_ptr<Return> Native::create_return(Arg_Ts& ...args)
292
+ {
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
+
225
310
  return result;
226
311
  }
227
312
 
228
- inline std::vector<std::optional<VALUE>> Native::getRubyValues(size_t argc, const VALUE* argv, bool validate)
313
+ inline std::map<std::string, VALUE> Native::readRubyArgs(size_t argc, const VALUE* argv)
229
314
  {
230
- #undef max
231
- int size = std::max(this->parameters_.size(), argc);
232
- std::vector<std::optional<VALUE>> result(size);
315
+ std::map<std::string, VALUE> result;
233
316
 
234
317
  // Keyword handling
235
318
  if (rb_keyword_given_p())
236
319
  {
237
320
  // Keywords are stored in the last element in a hash
238
- int actualArgc = argc - 1;
239
-
240
- VALUE value = argv[actualArgc];
241
- Hash keywords(value);
321
+ size_t actualArgc = argc - 1;
242
322
 
243
323
  // Copy over leading non-keyword arguments
244
- for (int i = 0; i < actualArgc; i++)
324
+ for (size_t i = 0; i < actualArgc; i++)
245
325
  {
246
- result[i] = argv[i];
326
+ std::string key = "arg_" + std::to_string(i);
327
+ result[key] = argv[i];
247
328
  }
248
329
 
330
+ VALUE value = argv[actualArgc];
331
+ Hash keywords(value);
332
+
249
333
  // Copy over keyword arguments
250
334
  for (auto pair : keywords)
251
335
  {
252
- Symbol key(pair.first);
253
- ParameterAbstract* parameter = this->getParameterByName(key.str());
254
- if (!parameter)
255
- {
256
- throw std::invalid_argument("Unknown keyword: " + key.str());
257
- }
258
-
259
- const Arg* arg = parameter->arg;
260
-
261
- result[arg->position] = pair.second.value();
336
+ result[pair.first.to_s().str()] = pair.second.value();
262
337
  }
263
338
  }
264
339
  else
265
340
  {
266
- 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
+ }
267
347
  }
268
348
 
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())
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())
272
358
  {
273
- VALUE proc = rb_block_proc();
274
- 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);
275
362
  }
276
363
 
277
- if (validate)
364
+ std::vector<std::optional<VALUE>> result(this->parameters_.size());
365
+
366
+ for (size_t i=0; i< this->parameters_.size(); i++)
278
367
  {
279
- // Protect against user sending too many arguments
280
- 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)
281
378
  {
282
- std::string message = "wrong number of arguments (given " +
283
- std::to_string(argc) + ", expected " + std::to_string(this->parameters_.size()) + ")";
284
- throw std::invalid_argument(message);
379
+ iter = values.find(positionKey);
285
380
  }
286
381
 
287
- for (size_t i = 0; i < result.size(); i++)
382
+ if (iter != values.end())
383
+ {
384
+ result[i] = iter->second;
385
+ // Remove the value
386
+ values.erase(iter);
387
+ }
388
+ else if (arg->hasDefaultValue())
288
389
  {
289
- std::optional<VALUE> value = result[i];
290
- ParameterAbstract* parameter = this->parameters_[i].get();
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
+ }
291
407
 
292
- if (!parameter->arg->hasDefaultValue() && !value.has_value())
293
- {
294
- std::string message;
295
- message = "Missing argument. Name: " + parameter->arg->name + ". Index: " + std::to_string(parameter->arg->position) + ".";
296
- throw std::invalid_argument(message);
297
- }
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++;
298
421
  }
422
+ throw std::invalid_argument(message.str());
299
423
  }
300
424
 
301
425
  return result;
302
426
  }
303
427
 
304
- inline Convertible Native::matchParameters(std::vector<std::optional<VALUE>>& values)
428
+ inline double Native::matchParameters(std::vector<std::optional<VALUE>>& values, size_t argc)
305
429
  {
306
- Convertible result = Convertible::Exact;
307
- 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++)
308
434
  {
309
435
  ParameterAbstract* parameter = this->parameters_[i].get();
310
436
  std::optional<VALUE>& value = values[i];
311
- result = result & parameter->matches(value);
437
+ double score = parameter->matches(value);
438
+ minScore = (std::min)(minScore, score);
312
439
  }
313
- return result;
440
+
441
+ return minScore;
314
442
  }
315
443
 
316
- inline Resolved Native::matches(size_t argc, const VALUE* argv, VALUE self)
444
+ inline Resolved Native::matches(std::map<std::string, VALUE>& values)
317
445
  {
318
- // Return false if Ruby provided more arguments than the C++ method takes
319
- if (argc > this->parameters_.size())
320
- return Resolved{ Convertible::None, 0, this };
321
-
322
- 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
+ }
323
451
 
324
- std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(argc, argv, false);
325
- 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());
326
455
 
327
- if (this->parameters_.size() > 0)
456
+ // If zero score return then stop
457
+ if (minScore == 0)
328
458
  {
329
- int providedValues = std::count_if(rubyValues.begin(), rubyValues.end(), [](std::optional<VALUE>& value)
330
- {
331
- return value.has_value();
332
- });
459
+ return Resolved{ Convertible::None, this };
460
+ }
333
461
 
334
- 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
335
479
  }
336
- return result;
480
+
481
+ // Final score: minScore * parameterMatch
482
+ double finalScore = minScore * parameterMatch;
483
+
484
+ return Resolved{ finalScore, this };
337
485
  }
338
486
 
339
487
  inline std::vector<const ParameterAbstract*> Native::parameters()