rice 4.3.3 → 4.6.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 (237) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +86 -26
  3. data/CMakeLists.txt +31 -0
  4. data/CMakePresets.json +75 -0
  5. data/COPYING +3 -2
  6. data/FindRuby.cmake +437 -0
  7. data/README.md +7 -2
  8. data/Rakefile +12 -5
  9. data/include/rice/rice.hpp +9522 -4426
  10. data/include/rice/stl.hpp +2831 -1198
  11. data/lib/make_rice_headers.rb +79 -0
  12. data/lib/mkmf-rice.rb +40 -94
  13. data/lib/rice/version.rb +3 -0
  14. data/lib/rice.rb +1 -0
  15. data/lib/rubygems/builder.rb +11 -0
  16. data/lib/rubygems/cmake_builder.rb +113 -0
  17. data/lib/rubygems_plugin.rb +9 -0
  18. data/rice/Address_Registration_Guard.hpp +72 -3
  19. data/rice/Arg.hpp +26 -6
  20. data/rice/Arg.ipp +35 -2
  21. data/rice/Buffer.hpp +123 -0
  22. data/rice/Buffer.ipp +599 -0
  23. data/rice/Callback.hpp +21 -0
  24. data/rice/Callback.ipp +13 -0
  25. data/rice/Constructor.hpp +4 -27
  26. data/rice/Constructor.ipp +79 -0
  27. data/rice/Data_Object.hpp +73 -3
  28. data/rice/Data_Object.ipp +388 -96
  29. data/rice/Data_Type.hpp +214 -3
  30. data/rice/Data_Type.ipp +144 -67
  31. data/rice/Director.hpp +0 -2
  32. data/rice/Enum.hpp +4 -7
  33. data/rice/Enum.ipp +102 -55
  34. data/rice/Exception.hpp +62 -2
  35. data/rice/Exception.ipp +7 -12
  36. data/rice/Init.hpp +8 -0
  37. data/rice/Init.ipp +8 -0
  38. data/rice/JumpException.hpp +44 -0
  39. data/rice/JumpException.ipp +48 -0
  40. data/rice/MemoryView.hpp +11 -0
  41. data/rice/MemoryView.ipp +3 -0
  42. data/rice/Return.hpp +7 -27
  43. data/rice/Return.ipp +13 -13
  44. data/rice/cpp_api/Array.hpp +209 -0
  45. data/rice/cpp_api/Array.ipp +304 -0
  46. data/rice/cpp_api/Builtin_Object.hpp +31 -0
  47. data/rice/cpp_api/Builtin_Object.ipp +37 -0
  48. data/rice/cpp_api/Class.hpp +70 -0
  49. data/rice/cpp_api/Class.ipp +97 -0
  50. data/rice/cpp_api/Encoding.hpp +32 -0
  51. data/rice/cpp_api/Encoding.ipp +59 -0
  52. data/rice/cpp_api/Hash.hpp +194 -0
  53. data/rice/cpp_api/Hash.ipp +257 -0
  54. data/rice/{Identifier.hpp → cpp_api/Identifier.hpp} +2 -6
  55. data/rice/{Identifier.ipp → cpp_api/Identifier.ipp} +4 -2
  56. data/rice/cpp_api/Module.hpp +72 -0
  57. data/rice/cpp_api/Module.ipp +101 -0
  58. data/rice/cpp_api/Object.hpp +272 -0
  59. data/rice/cpp_api/Object.ipp +235 -0
  60. data/rice/cpp_api/String.hpp +74 -0
  61. data/rice/cpp_api/String.ipp +120 -0
  62. data/rice/cpp_api/Struct.hpp +113 -0
  63. data/rice/cpp_api/Struct.ipp +92 -0
  64. data/rice/cpp_api/Symbol.hpp +46 -0
  65. data/rice/cpp_api/Symbol.ipp +93 -0
  66. data/rice/cpp_api/shared_methods.hpp +134 -0
  67. data/rice/detail/DefaultHandler.hpp +12 -0
  68. data/rice/detail/DefaultHandler.ipp +8 -0
  69. data/rice/detail/HandlerRegistry.hpp +5 -35
  70. data/rice/detail/HandlerRegistry.ipp +7 -11
  71. data/rice/detail/InstanceRegistry.hpp +1 -4
  72. data/rice/detail/MethodInfo.hpp +12 -10
  73. data/rice/detail/MethodInfo.ipp +26 -21
  74. data/rice/detail/Native.hpp +33 -0
  75. data/rice/detail/Native.ipp +157 -0
  76. data/rice/detail/NativeAttributeGet.hpp +52 -0
  77. data/rice/detail/NativeAttributeGet.ipp +57 -0
  78. data/rice/detail/NativeAttributeSet.hpp +44 -0
  79. data/rice/detail/NativeAttributeSet.ipp +88 -0
  80. data/rice/detail/NativeCallbackFFI.hpp +55 -0
  81. data/rice/detail/NativeCallbackFFI.ipp +151 -0
  82. data/rice/detail/NativeCallbackSimple.hpp +30 -0
  83. data/rice/detail/NativeCallbackSimple.ipp +29 -0
  84. data/rice/detail/NativeFunction.hpp +33 -23
  85. data/rice/detail/NativeFunction.ipp +309 -70
  86. data/rice/detail/NativeIterator.hpp +9 -11
  87. data/rice/detail/NativeIterator.ipp +33 -31
  88. data/rice/detail/NativeRegistry.hpp +24 -15
  89. data/rice/detail/NativeRegistry.ipp +23 -48
  90. data/rice/detail/Proc.hpp +4 -0
  91. data/rice/detail/Proc.ipp +85 -0
  92. data/rice/detail/Registries.hpp +0 -7
  93. data/rice/detail/Registries.ipp +0 -18
  94. data/rice/detail/RubyFunction.hpp +0 -3
  95. data/rice/detail/RubyFunction.ipp +4 -8
  96. data/rice/detail/RubyType.hpp +16 -0
  97. data/rice/detail/RubyType.ipp +232 -0
  98. data/rice/detail/Type.hpp +7 -6
  99. data/rice/detail/Type.ipp +192 -45
  100. data/rice/detail/TypeRegistry.hpp +15 -7
  101. data/rice/detail/TypeRegistry.ipp +105 -12
  102. data/rice/detail/Wrapper.hpp +68 -32
  103. data/rice/detail/Wrapper.ipp +121 -109
  104. data/rice/detail/cpp_protect.hpp +5 -6
  105. data/rice/detail/default_allocation_func.ipp +0 -2
  106. data/rice/detail/from_ruby.hpp +38 -3
  107. data/rice/detail/from_ruby.ipp +1321 -492
  108. data/rice/detail/ruby.hpp +18 -0
  109. data/rice/detail/to_ruby.hpp +41 -3
  110. data/rice/detail/to_ruby.ipp +1424 -194
  111. data/rice/global_function.hpp +0 -4
  112. data/rice/global_function.ipp +0 -1
  113. data/rice/libc/file.hpp +11 -0
  114. data/rice/libc/file.ipp +32 -0
  115. data/rice/rice.hpp +116 -26
  116. data/rice/ruby_mark.hpp +4 -3
  117. data/rice/stl/complex.hpp +6 -0
  118. data/rice/stl/complex.ipp +93 -0
  119. data/rice/stl/exception.hpp +11 -0
  120. data/rice/stl/exception.ipp +29 -0
  121. data/rice/stl/exception_ptr.hpp +6 -0
  122. data/rice/stl/exception_ptr.ipp +27 -0
  123. data/rice/stl/map.hpp +12 -0
  124. data/rice/stl/map.ipp +469 -0
  125. data/rice/stl/monostate.hpp +6 -0
  126. data/rice/stl/monostate.ipp +80 -0
  127. data/rice/stl/multimap.hpp +14 -0
  128. data/rice/stl/multimap.ipp +448 -0
  129. data/rice/stl/optional.hpp +6 -0
  130. data/rice/stl/optional.ipp +118 -0
  131. data/rice/stl/pair.hpp +13 -0
  132. data/rice/stl/pair.ipp +155 -0
  133. data/rice/stl/reference_wrapper.hpp +6 -0
  134. data/rice/stl/reference_wrapper.ipp +41 -0
  135. data/rice/stl/set.hpp +12 -0
  136. data/rice/stl/set.ipp +495 -0
  137. data/rice/stl/shared_ptr.hpp +28 -0
  138. data/rice/stl/shared_ptr.ipp +224 -0
  139. data/rice/stl/string.hpp +6 -0
  140. data/rice/stl/string.ipp +158 -0
  141. data/rice/stl/string_view.hpp +6 -0
  142. data/rice/stl/string_view.ipp +65 -0
  143. data/rice/stl/tuple.hpp +6 -0
  144. data/rice/stl/tuple.ipp +128 -0
  145. data/rice/stl/type_index.hpp +6 -0
  146. data/rice/stl/type_index.ipp +30 -0
  147. data/rice/stl/type_info.hpp +6 -0
  148. data/rice/stl/type_info.ipp +29 -0
  149. data/rice/stl/unique_ptr.hpp +22 -0
  150. data/rice/stl/unique_ptr.ipp +139 -0
  151. data/rice/stl/unordered_map.hpp +12 -0
  152. data/rice/stl/unordered_map.ipp +469 -0
  153. data/rice/stl/variant.hpp +6 -0
  154. data/rice/stl/variant.ipp +242 -0
  155. data/rice/stl/vector.hpp +12 -0
  156. data/rice/stl/vector.ipp +590 -0
  157. data/rice/stl.hpp +11 -3
  158. data/rice/traits/attribute_traits.hpp +26 -0
  159. data/rice/traits/function_traits.hpp +95 -0
  160. data/rice/traits/method_traits.hpp +47 -0
  161. data/rice/traits/rice_traits.hpp +160 -0
  162. data/rice.gemspec +85 -0
  163. data/test/embed_ruby.cpp +7 -1
  164. data/test/extconf.rb +2 -0
  165. data/test/test_Address_Registration_Guard.cpp +5 -0
  166. data/test/test_Array.cpp +18 -4
  167. data/test/test_Attribute.cpp +136 -21
  168. data/test/test_Buffer.cpp +285 -0
  169. data/test/test_Builtin_Object.cpp +5 -0
  170. data/test/test_Callback.cpp +230 -0
  171. data/test/test_Class.cpp +5 -31
  172. data/test/test_Constructor.cpp +69 -6
  173. data/test/test_Data_Object.cpp +97 -38
  174. data/test/test_Data_Type.cpp +470 -65
  175. data/test/test_Director.cpp +17 -8
  176. data/test/test_Enum.cpp +155 -40
  177. data/test/test_Exception.cpp +235 -0
  178. data/test/test_File.cpp +70 -0
  179. data/test/test_From_Ruby.cpp +609 -0
  180. data/test/test_Hash.cpp +5 -0
  181. data/test/test_Identifier.cpp +5 -0
  182. data/test/test_Inheritance.cpp +6 -1
  183. data/test/test_Iterator.cpp +6 -1
  184. data/test/test_Jump_Exception.cpp +23 -0
  185. data/test/test_Keep_Alive.cpp +13 -19
  186. data/test/test_Keep_Alive_No_Wrapper.cpp +5 -1
  187. data/test/test_Memory_Management.cpp +5 -0
  188. data/test/test_Module.cpp +128 -67
  189. data/test/test_Native_Registry.cpp +2 -34
  190. data/test/test_Object.cpp +5 -0
  191. data/test/test_Overloads.cpp +806 -0
  192. data/test/test_Ownership.cpp +160 -54
  193. data/test/test_Proc.cpp +44 -0
  194. data/test/test_Self.cpp +9 -4
  195. data/test/test_Stl_Exception.cpp +109 -0
  196. data/test/test_Stl_Map.cpp +54 -42
  197. data/test/test_Stl_Multimap.cpp +693 -0
  198. data/test/test_Stl_Optional.cpp +5 -0
  199. data/test/test_Stl_Pair.cpp +14 -9
  200. data/test/test_Stl_Reference_Wrapper.cpp +9 -2
  201. data/test/test_Stl_Set.cpp +790 -0
  202. data/test/test_Stl_SharedPtr.cpp +458 -0
  203. data/test/test_Stl_String.cpp +5 -0
  204. data/test/test_Stl_String_View.cpp +5 -0
  205. data/test/test_Stl_Tuple.cpp +116 -0
  206. data/test/test_Stl_Type.cpp +147 -0
  207. data/test/test_Stl_UniquePtr.cpp +202 -0
  208. data/test/test_Stl_Unordered_Map.cpp +43 -38
  209. data/test/test_Stl_Variant.cpp +217 -84
  210. data/test/test_Stl_Vector.cpp +306 -58
  211. data/test/test_String.cpp +5 -0
  212. data/test/test_Struct.cpp +5 -0
  213. data/test/test_Symbol.cpp +5 -0
  214. data/test/test_Template.cpp +192 -0
  215. data/test/test_To_Ruby.cpp +524 -0
  216. data/test/test_Tracking.cpp +1 -0
  217. data/test/test_Type.cpp +171 -0
  218. data/test/test_global_functions.cpp +67 -7
  219. data/test/unittest.cpp +8 -0
  220. metadata +127 -26
  221. data/lib/version.rb +0 -3
  222. data/rice/Address_Registration_Guard_defn.hpp +0 -79
  223. data/rice/Data_Object_defn.hpp +0 -84
  224. data/rice/Data_Type_defn.hpp +0 -190
  225. data/rice/Exception_defn.hpp +0 -68
  226. data/rice/HandlerRegistration.hpp +0 -15
  227. data/rice/detail/ExceptionHandler.hpp +0 -8
  228. data/rice/detail/ExceptionHandler.ipp +0 -28
  229. data/rice/detail/ExceptionHandler_defn.hpp +0 -77
  230. data/rice/detail/Jump_Tag.hpp +0 -21
  231. data/rice/detail/NativeAttribute.hpp +0 -64
  232. data/rice/detail/NativeAttribute.ipp +0 -112
  233. data/rice/detail/from_ruby_defn.hpp +0 -38
  234. data/rice/detail/to_ruby_defn.hpp +0 -48
  235. data/test/test_Jump_Tag.cpp +0 -17
  236. data/test/test_Stl_SmartPointer.cpp +0 -283
  237. data/test/test_To_From_Ruby.cpp +0 -399
@@ -1,40 +1,50 @@
1
- #include <array>
2
1
  #include <algorithm>
2
+ #include <array>
3
3
  #include <stdexcept>
4
4
  #include <sstream>
5
5
 
6
- #include "cpp_protect.hpp"
7
- #include "to_ruby_defn.hpp"
8
- #include "NativeRegistry.hpp"
9
-
10
6
  namespace Rice::detail
11
7
  {
12
8
  template<typename Class_T, typename Function_T, bool IsMethod>
13
9
  void NativeFunction<Class_T, Function_T, IsMethod>::define(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
14
10
  {
15
- // Tell Ruby to invoke the static method call on this class
16
- detail::protect(rb_define_method, klass, method_name.c_str(), (RUBY_METHOD_FUNC)&NativeFunction_T::call, -1);
17
-
18
- // Now create a NativeFunction instance and save it to the natives registry keyed on
19
- // Ruby klass and method id. There may be multiple NativeFunction instances
20
- // because the same C++ method could be mapped to multiple Ruby methods.
21
- NativeFunction_T* native = new NativeFunction_T(klass, method_name, std::forward<Function_T>(function), methodInfo);
22
- detail::Registries::instance.natives.add(klass, Identifier(method_name).id(), native);
11
+ // Have we defined this method yet in Ruby?
12
+ Identifier identifier(method_name);
13
+ const std::vector<std::unique_ptr<Native>>& natives = Registries::instance.natives.lookup(klass, identifier.id());
14
+ if (natives.empty())
15
+ {
16
+ // Tell Ruby to invoke the static resolved method defined above
17
+ detail::protect(rb_define_method, klass, method_name.c_str(), (RUBY_METHOD_FUNC)&Native::resolve, -1);
18
+ }
19
+
20
+ // Create a NativeFunction instance and save it to the NativeRegistry. There may be multiple
21
+ // NativeFunction instances for a specific method because C++ supports method overloading.
22
+ NativeFunction_T* nativeFunction = new NativeFunction_T(klass, method_name, std::forward<Function_T>(function), methodInfo);
23
+ std::unique_ptr<Native> native(nativeFunction);
24
+ detail::Registries::instance.natives.add(klass, identifier.id(), native);
23
25
  }
24
26
 
27
+ // Ruby calls this method when invoking a proc that was defined as a C++ function
25
28
  template<typename Class_T, typename Function_T, bool IsMethod>
26
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::call(int argc, VALUE* argv, VALUE self)
29
+ VALUE NativeFunction<Class_T, Function_T, IsMethod>::procEntry(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg)
27
30
  {
28
- // Look up the native function based on the Ruby klass and method id
29
- NativeFunction_T* nativeFunction = detail::Registries::instance.natives.lookup<NativeFunction_T*>();
30
-
31
- // Execute the function but make sure to catch any C++ exceptions!
32
31
  return cpp_protect([&]
33
32
  {
34
- return nativeFunction->operator()(argc, argv, self);
33
+ NativeFunction_T * native = (NativeFunction_T*)callback_arg;
34
+ return (*native)(argc, argv, Qnil);
35
35
  });
36
36
  }
37
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)
42
+ {
43
+ NativeFunction_T* native = (NativeFunction_T*)callback_arg;
44
+ delete native;
45
+ return Qnil;
46
+ }
47
+
38
48
  template<typename Class_T, typename Function_T, bool IsMethod>
39
49
  NativeFunction<Class_T, Function_T, IsMethod>::NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
40
50
  : klass_(klass), method_name_(method_name), function_(function), methodInfo_(methodInfo)
@@ -49,6 +59,63 @@ namespace Rice::detail
49
59
  this->toRuby_ = this->createToRuby();
50
60
  }
51
61
 
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>
69
+ 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)
71
+ {
72
+ std::vector<std::string> typeNames;
73
+ (typeNames.push_back(cppClassName(typeName(typeid(typename std::tuple_element<I, Arg_Ts>::type)))), ...);
74
+ return typeNames;
75
+ }
76
+
77
+ template<typename Class_T, typename Function_T, bool IsMethod>
78
+ std::string NativeFunction<Class_T, Function_T, IsMethod>::toString()
79
+ {
80
+ std::ostringstream result;
81
+
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
+
89
+ result << this->method_name_;
90
+
91
+ result << "(";
92
+
93
+ auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
94
+ std::vector<std::string> argTypeNames = this->argTypeNames(result, indices);
95
+ for (size_t i = 0; i < argTypeNames.size(); i++)
96
+ {
97
+ result << argTypeNames[i];
98
+ if (i < argTypeNames.size() - 1)
99
+ result << ", ";
100
+ }
101
+ result << ")";
102
+ return result.str();
103
+ }
104
+
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
+
52
119
  template<typename Class_T, typename Function_T, bool IsMethod>
53
120
  template<typename T, std::size_t I>
54
121
  From_Ruby<T> NativeFunction<Class_T, Function_T, IsMethod>::createFromRuby()
@@ -56,7 +123,7 @@ namespace Rice::detail
56
123
  // Does the From_Ruby instantiation work with Arg?
57
124
  if constexpr (std::is_constructible_v<From_Ruby<T>, Arg*>)
58
125
  {
59
- return From_Ruby<T>(&this->methodInfo_->arg(I));
126
+ return From_Ruby<T>(this->methodInfo_->arg(I));
60
127
  }
61
128
  else
62
129
  {
@@ -65,60 +132,220 @@ namespace Rice::detail
65
132
  }
66
133
 
67
134
  template<typename Class_T, typename Function_T, bool IsMethod>
68
- To_Ruby<typename NativeFunction<Class_T, Function_T, IsMethod>::Return_T> NativeFunction<Class_T, Function_T, IsMethod>::createToRuby()
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)
69
137
  {
70
- // Does the From_Ruby instantiation work with ReturnInfo?
71
- if constexpr (std::is_constructible_v<To_Ruby<Return_T>, Return*>)
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())
72
152
  {
73
- return To_Ruby<Return_T>(&this->methodInfo_->returnInfo);
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
+ }
74
185
  }
75
- else
186
+ // Last check if a default value has been set
187
+ else if (arg->hasDefaultValue())
76
188
  {
77
- return To_Ruby<Return_T>();
189
+ result = Convertible::Exact;
78
190
  }
191
+
192
+ return result;
79
193
  }
80
194
 
81
195
  template<typename Class_T, typename Function_T, bool IsMethod>
82
196
  template<std::size_t... I>
83
- typename NativeFunction<Class_T, Function_T, IsMethod>::From_Ruby_Args_Ts NativeFunction<Class_T, Function_T, IsMethod>::createFromRuby(std::index_sequence<I...>& indices)
197
+ Convertible NativeFunction<Class_T, Function_T, IsMethod>::matchParameters(std::vector<std::optional<VALUE>>& values,
198
+ std::index_sequence<I...>& indices)
84
199
  {
85
- return std::make_tuple(createFromRuby<remove_cv_recursive_t<typename std::tuple_element<I, Arg_Ts>::type>, I>()...);
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;
86
228
  }
87
229
 
88
230
  template<typename Class_T, typename Function_T, bool IsMethod>
89
- std::vector<VALUE> NativeFunction<Class_T, Function_T, IsMethod>::getRubyValues(int argc, VALUE* argv)
231
+ std::vector<std::optional<VALUE>> NativeFunction<Class_T, Function_T, IsMethod>::getRubyValues(size_t argc, const VALUE* argv, bool validate)
90
232
  {
91
- // Setup a tuple for the leading rb_scan_args arguments
92
- std::string scanFormat = this->methodInfo_->formatString();
93
- std::tuple<int, VALUE*, const char*> rbScanArgs = std::forward_as_tuple(argc, argv, scanFormat.c_str());
94
-
95
- // Create a vector to store the VALUEs that will be returned by rb_scan_args
96
- std::vector<VALUE> rbScanValues(std::tuple_size_v<Arg_Ts>, Qnil);
97
-
98
- // Convert the vector to an array so it can be concatenated to a tuple. As importantly
99
- // fill it with pointers to rbScanValues
100
- std::array<VALUE*, std::tuple_size_v<Arg_Ts>> rbScanValuePointers;
101
- std::transform(rbScanValues.begin(), rbScanValues.end(), rbScanValuePointers.begin(),
102
- [](VALUE& value)
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++)
103
248
  {
104
- return &value;
105
- });
249
+ result[i] = argv[i];
250
+ }
106
251
 
107
- // Combine the tuples and call rb_scan_args
108
- std::apply(rb_scan_args, std::tuple_cat(rbScanArgs, rbScanValuePointers));
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);
109
291
 
110
- return rbScanValues;
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");
111
336
  }
112
337
 
113
338
  template<typename Class_T, typename Function_T, bool IsMethod>
114
339
  template<std::size_t... I>
115
- typename NativeFunction<Class_T, Function_T, IsMethod>::Arg_Ts NativeFunction<Class_T, Function_T, IsMethod>::getNativeValues(std::vector<VALUE>& values,
340
+ typename NativeFunction<Class_T, Function_T, IsMethod>::Arg_Ts NativeFunction<Class_T, Function_T, IsMethod>::getNativeValues(std::vector<std::optional<VALUE>>& values,
116
341
  std::index_sequence<I...>& indices)
117
342
  {
118
- // Convert each Ruby value to its native value by calling the appropriate fromRuby instance.
119
- // Note that for fundamental types From_Ruby<Arg_Ts> will keep a copy of the native value
120
- // so it can be passed by reference or pointer to a native function.
121
- return std::forward_as_tuple(std::get<I>(this->fromRubys_).convert(values[I])...);
343
+ /* Loop over each value returned from Ruby and convert it to the appropriate C++ type based
344
+ on the arguments (Arg_Ts) required by the C++ function. Arg_T may have const/volatile while
345
+ the associated From_Ruby<T> template parameter will not. Thus From_Ruby produces non-const values
346
+ 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)...);
122
349
  }
123
350
 
124
351
  template<typename Class_T, typename Function_T, bool IsMethod>
@@ -140,11 +367,19 @@ namespace Rice::detail
140
367
  that was wrapped so it can correctly extract the C++ object from
141
368
  the Ruby object. */
142
369
  else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
143
- std::is_base_of_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>)
144
372
  {
145
373
  Class_T* instance = From_Ruby<Class_T*>().convert(self);
146
374
  return dynamic_cast<Receiver_T>(instance);
147
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
+ }
148
383
  // Self parameter could be derived from Object or it is an C++ instance and
149
384
  // needs to be unwrapped from Ruby
150
385
  else
@@ -154,37 +389,37 @@ namespace Rice::detail
154
389
  }
155
390
 
156
391
  template<typename Class_T, typename Function_T, bool IsMethod>
157
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::invokeNativeFunction(const Arg_Ts& nativeArgs)
392
+ VALUE NativeFunction<Class_T, Function_T, IsMethod>::invokeNativeFunction(Arg_Ts&& nativeArgs)
158
393
  {
159
394
  if constexpr (std::is_void_v<Return_T>)
160
395
  {
161
- std::apply(this->function_, nativeArgs);
396
+ std::apply(this->function_, std::forward<Arg_Ts>(nativeArgs));
162
397
  return Qnil;
163
398
  }
164
399
  else
165
400
  {
166
401
  // Call the native method and get the result
167
- Return_T nativeResult = std::apply(this->function_, nativeArgs);
402
+ Return_T nativeResult = std::apply(this->function_, std::forward<Arg_Ts>(nativeArgs));
168
403
 
169
404
  // Return the result
170
- return this->toRuby_.convert(nativeResult);
405
+ return this->toRuby_.convert(std::forward<Return_T>(nativeResult));
171
406
  }
172
407
  }
173
408
 
174
409
  template<typename Class_T, typename Function_T, bool IsMethod>
175
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::invokeNativeMethod(VALUE self, const Arg_Ts& nativeArgs)
410
+ VALUE NativeFunction<Class_T, Function_T, IsMethod>::invokeNativeMethod(VALUE self, Arg_Ts&& nativeArgs)
176
411
  {
177
412
  Receiver_T receiver = this->getReceiver(self);
178
- auto selfAndNativeArgs = std::tuple_cat(std::forward_as_tuple(receiver), nativeArgs);
413
+ auto selfAndNativeArgs = std::tuple_cat(std::forward_as_tuple(receiver), std::forward<Arg_Ts>(nativeArgs));
179
414
 
180
415
  if constexpr (std::is_void_v<Return_T>)
181
416
  {
182
- std::apply(this->function_, selfAndNativeArgs);
417
+ std::apply(this->function_, std::forward<decltype(selfAndNativeArgs)>(selfAndNativeArgs));
183
418
  return Qnil;
184
419
  }
185
420
  else
186
421
  {
187
- Return_T nativeResult = (Return_T)std::apply(this->function_, selfAndNativeArgs);
422
+ Return_T nativeResult = std::apply(this->function_, std::forward<decltype(selfAndNativeArgs)>(selfAndNativeArgs));
188
423
 
189
424
  // Special handling if the method returns self. If so we do not want
190
425
  // to create a new Ruby wrapper object and instead return self.
@@ -212,7 +447,7 @@ namespace Rice::detail
212
447
  }
213
448
  }
214
449
 
215
- return this->toRuby_.convert(nativeResult);
450
+ return this->toRuby_.convert(std::forward<Return_T>(nativeResult));
216
451
  }
217
452
  }
218
453
 
@@ -233,11 +468,15 @@ namespace Rice::detail
233
468
  }
234
469
 
235
470
  template<typename Class_T, typename Function_T, bool IsMethod>
236
- void NativeFunction<Class_T, Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues)
471
+ void NativeFunction<Class_T, Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<std::optional<VALUE>>& rubyValues)
237
472
  {
473
+ // Self will be Qnil for wrapped procs
474
+ if (self == Qnil)
475
+ return;
476
+
238
477
  // selfWrapper will be nullptr if this(self) is a builtin type and not an external(wrapped) type
239
478
  // it is highly unlikely that keepAlive is used in this case but we check anyway
240
- Wrapper* selfWrapper = getWrapper(self);
479
+ WrapperBase* selfWrapper = getWrapper(self);
241
480
 
242
481
  // Check function arguments
243
482
  for (const Arg& arg : (*this->methodInfo_))
@@ -248,7 +487,7 @@ namespace Rice::detail
248
487
  {
249
488
  noWrapper(self, "self");
250
489
  }
251
- selfWrapper->addKeepAlive(rubyValues[arg.position]);
490
+ selfWrapper->addKeepAlive(rubyValues[arg.position].value());
252
491
  }
253
492
  }
254
493
 
@@ -261,7 +500,7 @@ namespace Rice::detail
261
500
  }
262
501
 
263
502
  // returnWrapper will be nullptr if returnValue is a built-in type and not an external(wrapped) type
264
- Wrapper* returnWrapper = getWrapper(returnValue);
503
+ WrapperBase* returnWrapper = getWrapper(returnValue);
265
504
  if (returnWrapper == nullptr)
266
505
  {
267
506
  noWrapper(returnValue, "return");
@@ -271,10 +510,10 @@ namespace Rice::detail
271
510
  }
272
511
 
273
512
  template<typename Class_T, typename Function_T, bool IsMethod>
274
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::operator()(int argc, VALUE* argv, VALUE self)
513
+ VALUE NativeFunction<Class_T, Function_T, IsMethod>::operator()(size_t argc, const VALUE* argv, VALUE self)
275
514
  {
276
- // Get the ruby values
277
- std::vector<VALUE> rubyValues = this->getRubyValues(argc, argv);
515
+ // Get the ruby values and make sure we have the correct number
516
+ std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(argc, argv, true);
278
517
 
279
518
  auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
280
519
 
@@ -285,11 +524,11 @@ namespace Rice::detail
285
524
  VALUE result = Qnil;
286
525
  if constexpr (std::is_same_v<Receiver_T, std::nullptr_t>)
287
526
  {
288
- result = this->invokeNativeFunction(nativeValues);
527
+ result = this->invokeNativeFunction(std::forward<Arg_Ts>(nativeValues));
289
528
  }
290
529
  else
291
530
  {
292
- result = this->invokeNativeMethod(self, nativeValues);
531
+ result = this->invokeNativeMethod(self, std::forward<Arg_Ts>(nativeValues));
293
532
  }
294
533
 
295
534
  // Check if any function arguments or return values need to have their lifetimes tied to the receiver
@@ -1,26 +1,23 @@
1
- #ifndef Rice_NativeIterator__hpp_
2
- #define Rice_NativeIterator__hpp_
3
-
4
- #include "../traits/function_traits.hpp"
1
+ #ifndef Rice__NativeIterator__hpp_
2
+ #define Rice__NativeIterator__hpp_
5
3
 
6
4
  namespace Rice::detail
7
5
  {
8
6
  template<typename T, typename Iterator_Func_T>
9
- class NativeIterator
7
+ class NativeIterator: Native
10
8
  {
11
9
  public:
12
10
  using NativeIterator_T = NativeIterator<T, Iterator_Func_T>;
13
11
  using Iterator_T = typename function_traits<Iterator_Func_T>::return_type;
14
12
  using Value_T = typename std::iterator_traits<Iterator_T>::value_type;
13
+ using Reference_T = typename std::iterator_traits<Iterator_T>::reference;
15
14
  using Difference_T = typename std::iterator_traits<Iterator_T>::difference_type;
15
+ using To_Ruby_T = remove_cv_recursive_t<Reference_T>;
16
16
 
17
17
  public:
18
18
  // Register function with Ruby
19
19
  void static define(VALUE klass, std::string method_name, Iterator_Func_T begin, Iterator_Func_T end);
20
20
 
21
- // Static member function that Ruby calls
22
- static VALUE call(VALUE self);
23
-
24
21
  public:
25
22
  // Disallow creating/copying/moving
26
23
  NativeIterator() = delete;
@@ -29,7 +26,9 @@ namespace Rice::detail
29
26
  void operator=(const NativeIterator_T&) = delete;
30
27
  void operator=(NativeIterator_T&&) = delete;
31
28
 
32
- VALUE operator()(VALUE self);
29
+ Resolved matches(size_t argc, const VALUE* argv, VALUE self) override;
30
+ VALUE operator()(size_t argc, const VALUE* argv, VALUE self) override;
31
+ std::string toString() override;
33
32
 
34
33
  protected:
35
34
  NativeIterator(VALUE klass, std::string method_name, Iterator_Func_T begin, Iterator_Func_T end);
@@ -44,6 +43,5 @@ namespace Rice::detail
44
43
  Iterator_Func_T end_;
45
44
  };
46
45
  }
47
- #include "NativeIterator.ipp"
48
46
 
49
- #endif // Rice_NativeIterator__hpp_
47
+ #endif // Rice__NativeIterator__hpp_