rice 4.3.3 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +63 -26
  3. data/README.md +7 -2
  4. data/Rakefile +7 -1
  5. data/include/rice/rice.hpp +7291 -4430
  6. data/include/rice/stl.hpp +769 -222
  7. data/lib/mkmf-rice.rb +37 -95
  8. data/rice/Address_Registration_Guard.hpp +72 -3
  9. data/rice/Arg.hpp +19 -5
  10. data/rice/Arg.ipp +24 -0
  11. data/rice/Callback.hpp +21 -0
  12. data/rice/Callback.ipp +13 -0
  13. data/rice/Constructor.hpp +4 -27
  14. data/rice/Constructor.ipp +79 -0
  15. data/rice/Data_Object.hpp +74 -3
  16. data/rice/Data_Object.ipp +324 -32
  17. data/rice/Data_Type.hpp +215 -3
  18. data/rice/Data_Type.ipp +125 -64
  19. data/rice/Director.hpp +0 -2
  20. data/rice/Enum.hpp +4 -6
  21. data/rice/Enum.ipp +101 -57
  22. data/rice/Exception.hpp +62 -2
  23. data/rice/Exception.ipp +7 -12
  24. data/rice/JumpException.hpp +44 -0
  25. data/rice/JumpException.ipp +48 -0
  26. data/rice/MemoryView.hpp +11 -0
  27. data/rice/MemoryView.ipp +43 -0
  28. data/rice/Return.hpp +6 -26
  29. data/rice/Return.ipp +10 -16
  30. data/rice/detail/DefaultHandler.hpp +12 -0
  31. data/rice/detail/DefaultHandler.ipp +8 -0
  32. data/rice/detail/HandlerRegistry.hpp +5 -35
  33. data/rice/detail/HandlerRegistry.ipp +7 -11
  34. data/rice/detail/InstanceRegistry.hpp +1 -4
  35. data/rice/detail/MethodInfo.hpp +15 -5
  36. data/rice/detail/MethodInfo.ipp +78 -6
  37. data/rice/detail/Native.hpp +32 -0
  38. data/rice/detail/Native.ipp +129 -0
  39. data/rice/detail/NativeAttributeGet.hpp +51 -0
  40. data/rice/detail/NativeAttributeGet.ipp +51 -0
  41. data/rice/detail/NativeAttributeSet.hpp +43 -0
  42. data/rice/detail/NativeAttributeSet.ipp +82 -0
  43. data/rice/detail/NativeCallbackFFI.hpp +55 -0
  44. data/rice/detail/NativeCallbackFFI.ipp +151 -0
  45. data/rice/detail/NativeCallbackSimple.hpp +30 -0
  46. data/rice/detail/NativeCallbackSimple.ipp +29 -0
  47. data/rice/detail/NativeFunction.hpp +20 -21
  48. data/rice/detail/NativeFunction.ipp +199 -64
  49. data/rice/detail/NativeIterator.hpp +8 -11
  50. data/rice/detail/NativeIterator.ipp +27 -31
  51. data/rice/detail/NativeRegistry.hpp +24 -15
  52. data/rice/detail/NativeRegistry.ipp +23 -48
  53. data/rice/detail/Proc.hpp +4 -0
  54. data/rice/detail/Proc.ipp +85 -0
  55. data/rice/detail/Registries.hpp +0 -7
  56. data/rice/detail/Registries.ipp +0 -18
  57. data/rice/detail/RubyFunction.hpp +0 -3
  58. data/rice/detail/RubyFunction.ipp +4 -8
  59. data/rice/detail/RubyType.hpp +19 -0
  60. data/rice/detail/RubyType.ipp +187 -0
  61. data/rice/detail/TupleIterator.hpp +14 -0
  62. data/rice/detail/Type.hpp +5 -6
  63. data/rice/detail/Type.ipp +150 -33
  64. data/rice/detail/TypeRegistry.hpp +15 -7
  65. data/rice/detail/TypeRegistry.ipp +105 -12
  66. data/rice/detail/Wrapper.hpp +6 -5
  67. data/rice/detail/Wrapper.ipp +45 -23
  68. data/rice/detail/cpp_protect.hpp +5 -6
  69. data/rice/detail/default_allocation_func.ipp +0 -2
  70. data/rice/detail/from_ruby.hpp +37 -3
  71. data/rice/detail/from_ruby.ipp +911 -454
  72. data/rice/detail/ruby.hpp +18 -0
  73. data/rice/detail/to_ruby.hpp +41 -3
  74. data/rice/detail/to_ruby.ipp +437 -113
  75. data/rice/global_function.hpp +0 -4
  76. data/rice/global_function.ipp +1 -2
  77. data/rice/rice.hpp +105 -22
  78. data/rice/ruby_mark.hpp +4 -3
  79. data/rice/stl.hpp +4 -0
  80. data/test/embed_ruby.cpp +4 -1
  81. data/test/extconf.rb +2 -0
  82. data/test/ruby/test_multiple_extensions_same_class.rb +14 -14
  83. data/test/test_Address_Registration_Guard.cpp +5 -0
  84. data/test/test_Array.cpp +12 -1
  85. data/test/test_Attribute.cpp +103 -21
  86. data/test/test_Builtin_Object.cpp +5 -0
  87. data/test/test_Callback.cpp +231 -0
  88. data/test/test_Class.cpp +5 -31
  89. data/test/test_Constructor.cpp +69 -6
  90. data/test/test_Data_Object.cpp +9 -4
  91. data/test/test_Data_Type.cpp +428 -64
  92. data/test/test_Director.cpp +10 -5
  93. data/test/test_Enum.cpp +152 -40
  94. data/test/test_Exception.cpp +235 -0
  95. data/test/test_File.cpp +70 -0
  96. data/test/test_From_Ruby.cpp +542 -0
  97. data/test/test_Hash.cpp +5 -0
  98. data/test/test_Identifier.cpp +5 -0
  99. data/test/test_Inheritance.cpp +6 -1
  100. data/test/test_Iterator.cpp +5 -0
  101. data/test/test_JumpException.cpp +22 -0
  102. data/test/test_Keep_Alive.cpp +6 -1
  103. data/test/test_Keep_Alive_No_Wrapper.cpp +5 -0
  104. data/test/test_Memory_Management.cpp +5 -0
  105. data/test/test_Module.cpp +118 -64
  106. data/test/test_Native_Registry.cpp +2 -33
  107. data/test/test_Object.cpp +5 -0
  108. data/test/test_Overloads.cpp +631 -0
  109. data/test/test_Ownership.cpp +67 -4
  110. data/test/test_Proc.cpp +45 -0
  111. data/test/test_Self.cpp +5 -0
  112. data/test/test_Stl_Exception.cpp +109 -0
  113. data/test/test_Stl_Map.cpp +22 -8
  114. data/test/test_Stl_Optional.cpp +5 -0
  115. data/test/test_Stl_Pair.cpp +7 -2
  116. data/test/test_Stl_Reference_Wrapper.cpp +5 -0
  117. data/test/test_Stl_SmartPointer.cpp +210 -5
  118. data/test/test_Stl_String.cpp +5 -0
  119. data/test/test_Stl_String_View.cpp +5 -0
  120. data/test/test_Stl_Type.cpp +147 -0
  121. data/test/test_Stl_Unordered_Map.cpp +18 -7
  122. data/test/test_Stl_Variant.cpp +5 -0
  123. data/test/test_Stl_Vector.cpp +130 -8
  124. data/test/test_String.cpp +5 -0
  125. data/test/test_Struct.cpp +5 -0
  126. data/test/test_Symbol.cpp +5 -0
  127. data/test/test_Template.cpp +192 -0
  128. data/test/test_To_Ruby.cpp +152 -0
  129. data/test/test_Tracking.cpp +1 -0
  130. data/test/test_Type.cpp +100 -0
  131. data/test/test_global_functions.cpp +53 -6
  132. data/test/unittest.cpp +8 -0
  133. metadata +37 -20
  134. data/lib/version.rb +0 -3
  135. data/rice/Address_Registration_Guard_defn.hpp +0 -79
  136. data/rice/Data_Object_defn.hpp +0 -84
  137. data/rice/Data_Type_defn.hpp +0 -190
  138. data/rice/Exception_defn.hpp +0 -68
  139. data/rice/HandlerRegistration.hpp +0 -15
  140. data/rice/Identifier.hpp +0 -50
  141. data/rice/Identifier.ipp +0 -29
  142. data/rice/detail/ExceptionHandler.hpp +0 -8
  143. data/rice/detail/ExceptionHandler.ipp +0 -28
  144. data/rice/detail/ExceptionHandler_defn.hpp +0 -77
  145. data/rice/detail/Jump_Tag.hpp +0 -21
  146. data/rice/detail/NativeAttribute.hpp +0 -64
  147. data/rice/detail/NativeAttribute.ipp +0 -112
  148. data/rice/detail/from_ruby_defn.hpp +0 -38
  149. data/rice/detail/to_ruby_defn.hpp +0 -48
  150. data/test/test_Jump_Tag.cpp +0 -17
  151. data/test/test_To_From_Ruby.cpp +0 -399
@@ -3,38 +3,47 @@
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
6
 
10
7
  namespace Rice::detail
11
8
  {
12
9
  template<typename Class_T, typename Function_T, bool IsMethod>
13
10
  void NativeFunction<Class_T, Function_T, IsMethod>::define(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
14
11
  {
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);
12
+ // Have we defined this method yet in Ruby?
13
+ Identifier identifier(method_name);
14
+ const std::vector<std::unique_ptr<Native>>& natives = Registries::instance.natives.lookup(klass, identifier.id());
15
+ if (natives.empty())
16
+ {
17
+ // Tell Ruby to invoke the static resolved method defined above
18
+ detail::protect(rb_define_method, klass, method_name.c_str(), (RUBY_METHOD_FUNC)&Native::resolve, -1);
19
+ }
20
+
21
+ // Create a NativeFunction instance and save it to the NativeRegistry. There may be multiple
22
+ // NativeFunction instances for a specific method because C++ supports method overloading.
23
+ NativeFunction_T* nativeFunction = new NativeFunction_T(klass, method_name, std::forward<Function_T>(function), methodInfo);
24
+ std::unique_ptr<Native> native(nativeFunction);
25
+ detail::Registries::instance.natives.add(klass, identifier.id(), native);
23
26
  }
24
27
 
28
+ // Ruby calls this method when invoking a proc that was defined as a C++ function
25
29
  template<typename Class_T, typename Function_T, bool IsMethod>
26
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::call(int argc, VALUE* argv, VALUE self)
30
+ VALUE NativeFunction<Class_T, Function_T, IsMethod>::procEntry(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg)
27
31
  {
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*>();
32
+ NativeFunction_T* native = (NativeFunction_T*)callback_arg;
33
+ return (*native)(argc, argv, Qnil);
34
+ }
30
35
 
31
- // Execute the function but make sure to catch any C++ exceptions!
32
- return cpp_protect([&]
33
- {
34
- return nativeFunction->operator()(argc, argv, self);
35
- });
36
+ // Ruby calls this method if an instance f a NativeFunction is owned by a Ruby proc. That happens when C++
37
+ // returns a function back to Ruby
38
+ template<typename Class_T, typename Function_T, bool IsMethod>
39
+ VALUE NativeFunction<Class_T, Function_T, IsMethod>::finalizerCallback(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg)
40
+ {
41
+ NativeFunction_T* native = (NativeFunction_T*)callback_arg;
42
+ delete native;
43
+ return Qnil;
36
44
  }
37
45
 
46
+
38
47
  template<typename Class_T, typename Function_T, bool IsMethod>
39
48
  NativeFunction<Class_T, Function_T, IsMethod>::NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
40
49
  : klass_(klass), method_name_(method_name), function_(function), methodInfo_(methodInfo)
@@ -50,31 +59,37 @@ namespace Rice::detail
50
59
  }
51
60
 
52
61
  template<typename Class_T, typename Function_T, bool IsMethod>
53
- template<typename T, std::size_t I>
54
- From_Ruby<T> NativeFunction<Class_T, Function_T, IsMethod>::createFromRuby()
62
+ NativeFunction<Class_T, Function_T, IsMethod>::NativeFunction(Function_T function)
63
+ : NativeFunction(Qnil, "", function, new MethodInfo(arity))
55
64
  {
56
- // Does the From_Ruby instantiation work with Arg?
57
- if constexpr (std::is_constructible_v<From_Ruby<T>, Arg*>)
65
+ }
66
+
67
+ template<typename Class_T, typename Function_T, bool IsMethod>
68
+ To_Ruby<typename NativeFunction<Class_T, Function_T, IsMethod>::To_Ruby_T> NativeFunction<Class_T, Function_T, IsMethod>::createToRuby()
69
+ {
70
+ // Does the From_Ruby instantiation work with ReturnInfo?
71
+ if constexpr (std::is_constructible_v<To_Ruby<To_Ruby_T>, Return*>)
58
72
  {
59
- return From_Ruby<T>(&this->methodInfo_->arg(I));
73
+ return To_Ruby<To_Ruby_T>(&this->methodInfo_->returnInfo);
60
74
  }
61
75
  else
62
76
  {
63
- return From_Ruby<T>();
77
+ return To_Ruby<To_Ruby_T>();
64
78
  }
65
79
  }
66
80
 
67
81
  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()
82
+ template<typename T, std::size_t I>
83
+ From_Ruby<T> NativeFunction<Class_T, Function_T, IsMethod>::createFromRuby()
69
84
  {
70
- // Does the From_Ruby instantiation work with ReturnInfo?
71
- if constexpr (std::is_constructible_v<To_Ruby<Return_T>, Return*>)
85
+ // Does the From_Ruby instantiation work with Arg?
86
+ if constexpr (std::is_constructible_v<From_Ruby<T>, Arg*>)
72
87
  {
73
- return To_Ruby<Return_T>(&this->methodInfo_->returnInfo);
88
+ return From_Ruby<T>(this->methodInfo_->arg(I));
74
89
  }
75
90
  else
76
91
  {
77
- return To_Ruby<Return_T>();
92
+ return From_Ruby<T>();
78
93
  }
79
94
  }
80
95
 
@@ -86,28 +101,134 @@ namespace Rice::detail
86
101
  }
87
102
 
88
103
  template<typename Class_T, typename Function_T, bool IsMethod>
89
- std::vector<VALUE> NativeFunction<Class_T, Function_T, IsMethod>::getRubyValues(int argc, VALUE* argv)
104
+ Resolved NativeFunction<Class_T, Function_T, IsMethod>::matches(int argc, const VALUE* argv, VALUE self)
90
105
  {
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)
106
+ // Return false if Ruby provided more arguments than the C++ method takes
107
+ if (argc > arity)
108
+ return Resolved{ Convertible::None, 0, this };
109
+
110
+ Resolved result { Convertible::Exact, 1, this };
111
+
112
+ MethodInfo* methodInfo = this->methodInfo_.get();
113
+ int index = 0;
114
+
115
+ std::vector<VALUE> rubyValues = this->getRubyValues(argc, argv, false);
116
+
117
+ // Loop over each FromRuby instance
118
+ for_each_tuple(this->fromRubys_,
119
+ [&](auto& fromRuby)
103
120
  {
104
- return &value;
121
+ Convertible convertible = Convertible::None;
122
+
123
+ const Arg* arg = methodInfo->arg(index);
124
+
125
+ // Is a VALUE being passed directly to C++ ?
126
+ if (arg->isValue() && index < rubyValues.size())
127
+ {
128
+ convertible = Convertible::Exact;
129
+ }
130
+ // If index is less than argc then check with FromRuby if the VALUE is convertible
131
+ // to C++.
132
+ else if (index < rubyValues.size())
133
+ {
134
+ VALUE value = rubyValues[index];
135
+ convertible = fromRuby.is_convertible(value);
136
+ }
137
+ // Last check if a default value has been set
138
+ else if (arg->hasDefaultValue())
139
+ {
140
+ convertible = Convertible::Exact;
141
+ }
142
+
143
+ result.convertible = result.convertible & convertible;
144
+
145
+ index++;
105
146
  });
106
147
 
107
- // Combine the tuples and call rb_scan_args
108
- std::apply(rb_scan_args, std::tuple_cat(rbScanArgs, rbScanValuePointers));
148
+ if constexpr (arity > 0)
149
+ result.parameterMatch = rubyValues.size() / (double)arity;
150
+
151
+ return result;
152
+ }
153
+
154
+ template<typename Class_T, typename Function_T, bool IsMethod>
155
+ std::vector<VALUE> NativeFunction<Class_T, Function_T, IsMethod>::getRubyValues(int argc, const VALUE* argv, bool validate)
156
+ {
157
+ std::vector<VALUE> result;
158
+
159
+ // Keyword handling
160
+ if (rb_keyword_given_p())
161
+ {
162
+ // Keywords are stored in the last element in a hash
163
+ int actualArgc = argc - 1;
164
+
165
+ VALUE value = argv[actualArgc];
166
+ Hash keywords(value);
167
+
168
+ result.resize(actualArgc + keywords.size());
169
+
170
+ // Copy over leading arguments
171
+ for (int i = 0; i < actualArgc; i++)
172
+ {
173
+ result[i] = argv[i];
174
+ }
109
175
 
110
- return rbScanValues;
176
+ // Copy over keyword arguments
177
+ for (auto pair : keywords)
178
+ {
179
+ Symbol key(pair.first);
180
+ const Arg* arg = this->methodInfo_->arg(key.str());
181
+ if (!arg)
182
+ {
183
+ throw std::invalid_argument("Unknown keyword: " + key.str());
184
+ }
185
+ result[arg->position] = pair.second.value();
186
+ }
187
+ }
188
+ else
189
+ {
190
+ std::copy(argv, argv + argc, std::back_inserter(result));
191
+ }
192
+
193
+ // Block handling. If we find a block and the last parameter is missing then
194
+ // set it to the block
195
+ if (rb_block_given_p() && result.size() < std::tuple_size_v<Arg_Ts>)
196
+ {
197
+ VALUE proc = rb_block_proc();
198
+ result.push_back(proc);
199
+ }
200
+
201
+ if (validate)
202
+ {
203
+ this->methodInfo_->verifyArgCount(result.size());
204
+ }
205
+
206
+ return result;
207
+ }
208
+
209
+ template<typename Class_T, typename Function_T, bool IsMethod>
210
+ template<typename Arg_T, int I>
211
+ Arg_T NativeFunction<Class_T, Function_T, IsMethod>::getNativeValue(std::vector<VALUE>& values)
212
+ {
213
+ /* In general the compiler will convert T to const T, but that does not work for converting
214
+ T** to const T** (see see https://isocpp.org/wiki/faq/const-correctness#constptrptr-conversion)
215
+ which comes up in the OpenCV bindings.
216
+
217
+ An alternative solution is updating From_Ruby#convert to become a templated function that specifies
218
+ the return type. That works but requires a lot more code changes for this one case and is not
219
+ backwards compatible. */
220
+
221
+ // If the user did provide a value assume Qnil
222
+ VALUE value = I < values.size() ? values[I] : Qnil;
223
+
224
+ if constexpr (is_pointer_pointer_v<Arg_T> && !std::is_convertible_v<remove_cv_recursive_t<Arg_T>, Arg_T>)
225
+ {
226
+ return (Arg_T)std::get<I>(this->fromRubys_).convert(value);
227
+ }
228
+ else
229
+ {
230
+ return std::get<I>(this->fromRubys_).convert(value);
231
+ }
111
232
  }
112
233
 
113
234
  template<typename Class_T, typename Function_T, bool IsMethod>
@@ -115,10 +236,12 @@ namespace Rice::detail
115
236
  typename NativeFunction<Class_T, Function_T, IsMethod>::Arg_Ts NativeFunction<Class_T, Function_T, IsMethod>::getNativeValues(std::vector<VALUE>& values,
116
237
  std::index_sequence<I...>& indices)
117
238
  {
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])...);
239
+ /* Loop over each value returned from Ruby and convert it to the appropriate C++ type based
240
+ on the arguments (Arg_Ts) required by the C++ function. Arg_T may have const/volatile while
241
+ the associated From_Ruby<T> template parameter will not. Thus From_Ruby produces non-const values
242
+ which we let the compiler convert to const values as needed. This works except for
243
+ T** -> const T**, see comment in getNativeValue method. */
244
+ return std::forward_as_tuple(this->getNativeValue<std::tuple_element_t<I, Arg_Ts>, I>(values)...);
122
245
  }
123
246
 
124
247
  template<typename Class_T, typename Function_T, bool IsMethod>
@@ -140,11 +263,19 @@ namespace Rice::detail
140
263
  that was wrapped so it can correctly extract the C++ object from
141
264
  the Ruby object. */
142
265
  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>)
266
+ std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
267
+ std::is_pointer_v<Receiver_T>)
144
268
  {
145
269
  Class_T* instance = From_Ruby<Class_T*>().convert(self);
146
270
  return dynamic_cast<Receiver_T>(instance);
147
271
  }
272
+ else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
273
+ std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
274
+ std::is_reference_v<Receiver_T>)
275
+ {
276
+ Class_T& instance = From_Ruby<Class_T&>().convert(self);
277
+ return dynamic_cast<Receiver_T>(instance);
278
+ }
148
279
  // Self parameter could be derived from Object or it is an C++ instance and
149
280
  // needs to be unwrapped from Ruby
150
281
  else
@@ -154,17 +285,17 @@ namespace Rice::detail
154
285
  }
155
286
 
156
287
  template<typename Class_T, typename Function_T, bool IsMethod>
157
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::invokeNativeFunction(const Arg_Ts& nativeArgs)
288
+ VALUE NativeFunction<Class_T, Function_T, IsMethod>::invokeNativeFunction(Arg_Ts&& nativeArgs)
158
289
  {
159
290
  if constexpr (std::is_void_v<Return_T>)
160
291
  {
161
- std::apply(this->function_, nativeArgs);
292
+ std::apply(this->function_, std::forward<Arg_Ts>(nativeArgs));
162
293
  return Qnil;
163
294
  }
164
295
  else
165
296
  {
166
297
  // Call the native method and get the result
167
- Return_T nativeResult = std::apply(this->function_, nativeArgs);
298
+ Return_T nativeResult = std::apply(this->function_, std::forward<Arg_Ts>(nativeArgs));
168
299
 
169
300
  // Return the result
170
301
  return this->toRuby_.convert(nativeResult);
@@ -172,19 +303,19 @@ namespace Rice::detail
172
303
  }
173
304
 
174
305
  template<typename Class_T, typename Function_T, bool IsMethod>
175
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::invokeNativeMethod(VALUE self, const Arg_Ts& nativeArgs)
306
+ VALUE NativeFunction<Class_T, Function_T, IsMethod>::invokeNativeMethod(VALUE self, Arg_Ts&& nativeArgs)
176
307
  {
177
308
  Receiver_T receiver = this->getReceiver(self);
178
- auto selfAndNativeArgs = std::tuple_cat(std::forward_as_tuple(receiver), nativeArgs);
309
+ auto selfAndNativeArgs = std::tuple_cat(std::forward_as_tuple(receiver), std::forward<Arg_Ts>(nativeArgs));
179
310
 
180
311
  if constexpr (std::is_void_v<Return_T>)
181
312
  {
182
- std::apply(this->function_, selfAndNativeArgs);
313
+ std::apply(this->function_, std::forward<decltype(selfAndNativeArgs)>(selfAndNativeArgs));
183
314
  return Qnil;
184
315
  }
185
316
  else
186
317
  {
187
- Return_T nativeResult = (Return_T)std::apply(this->function_, selfAndNativeArgs);
318
+ Return_T nativeResult = std::apply(this->function_, std::forward<decltype(selfAndNativeArgs)>(selfAndNativeArgs));
188
319
 
189
320
  // Special handling if the method returns self. If so we do not want
190
321
  // to create a new Ruby wrapper object and instead return self.
@@ -235,6 +366,10 @@ namespace Rice::detail
235
366
  template<typename Class_T, typename Function_T, bool IsMethod>
236
367
  void NativeFunction<Class_T, Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues)
237
368
  {
369
+ // Self will be Qnil for wrapped procs
370
+ if (self == Qnil)
371
+ return;
372
+
238
373
  // selfWrapper will be nullptr if this(self) is a builtin type and not an external(wrapped) type
239
374
  // it is highly unlikely that keepAlive is used in this case but we check anyway
240
375
  Wrapper* selfWrapper = getWrapper(self);
@@ -271,10 +406,10 @@ namespace Rice::detail
271
406
  }
272
407
 
273
408
  template<typename Class_T, typename Function_T, bool IsMethod>
274
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::operator()(int argc, VALUE* argv, VALUE self)
409
+ VALUE NativeFunction<Class_T, Function_T, IsMethod>::operator()(int argc, const VALUE* argv, VALUE self)
275
410
  {
276
- // Get the ruby values
277
- std::vector<VALUE> rubyValues = this->getRubyValues(argc, argv);
411
+ // Get the ruby values and make sure we have the correct number
412
+ std::vector<VALUE> rubyValues = this->getRubyValues(argc, argv, true);
278
413
 
279
414
  auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
280
415
 
@@ -285,11 +420,11 @@ namespace Rice::detail
285
420
  VALUE result = Qnil;
286
421
  if constexpr (std::is_same_v<Receiver_T, std::nullptr_t>)
287
422
  {
288
- result = this->invokeNativeFunction(nativeValues);
423
+ result = this->invokeNativeFunction(std::forward<Arg_Ts>(nativeValues));
289
424
  }
290
425
  else
291
426
  {
292
- result = this->invokeNativeMethod(self, nativeValues);
427
+ result = this->invokeNativeMethod(self, std::forward<Arg_Ts>(nativeValues));
293
428
  }
294
429
 
295
430
  // 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,8 @@ 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(int argc, const VALUE* argv, VALUE self) override;
30
+ VALUE operator()(int argc, const VALUE* argv, VALUE self) override;
33
31
 
34
32
  protected:
35
33
  NativeIterator(VALUE klass, std::string method_name, Iterator_Func_T begin, Iterator_Func_T end);
@@ -44,6 +42,5 @@ namespace Rice::detail
44
42
  Iterator_Func_T end_;
45
43
  };
46
44
  }
47
- #include "NativeIterator.ipp"
48
45
 
49
- #endif // Rice_NativeIterator__hpp_
46
+ #endif // Rice__NativeIterator__hpp_
@@ -2,34 +2,21 @@
2
2
  #include <functional>
3
3
  #include <type_traits>
4
4
 
5
- #include "cpp_protect.hpp"
6
- #include "NativeRegistry.hpp"
7
-
8
5
  namespace Rice::detail
9
6
  {
10
7
  template <typename T, typename Iterator_Func_T>
11
8
  inline void NativeIterator<T, Iterator_Func_T>::define(VALUE klass, std::string method_name, Iterator_Func_T begin, Iterator_Func_T end)
12
9
  {
13
- // Tell Ruby to invoke the static method call on this class
14
- detail::protect(rb_define_method, klass, method_name.c_str(), (RUBY_METHOD_FUNC)&NativeIterator_T::call, 0);
15
-
16
- // Now create a NativeIterator instance and save it to the natives registry keyed on
17
- // Ruby klass and method id. There may be multiple NativeIterator instances
18
- // because the same C++ method could be mapped to multiple Ruby methods.
19
- NativeIterator_T* native = new NativeIterator_T(klass, method_name, begin, end);
20
- detail::Registries::instance.natives.add(klass, Identifier(method_name).id(), native);
21
- }
10
+ // Tell Ruby to invoke the resolveIterator static method defined in Native super class.
11
+ detail::protect(rb_define_method, klass, method_name.c_str(), (RUBY_METHOD_FUNC)&Native::resolve, -1);
22
12
 
23
- template<typename T, typename Iterator_Func_T>
24
- inline VALUE NativeIterator<T, Iterator_Func_T>::call(VALUE self)
25
- {
26
- // Look up the native function based on the Ruby klass and method id
27
- NativeIterator_T* nativeIterator = detail::Registries::instance.natives.lookup<NativeIterator_T*>();
13
+ // Create a NativeIterator instance and save it to the NativeRegistry. There may be multiple
14
+ // NativeFunction instances for a specific method because C++ supports method overloading.
15
+ NativeIterator_T* nativeIterator = new NativeIterator_T(klass, method_name, begin, end);
16
+ std::unique_ptr<Native> native(nativeIterator);
28
17
 
29
- return cpp_protect([&]
30
- {
31
- return nativeIterator->operator()(self);
32
- });
18
+ Identifier identifier(method_name);
19
+ detail::Registries::instance.natives.add(klass, identifier.id(), native);
33
20
  }
34
21
 
35
22
  template <typename T, typename Iterator_Func_T>
@@ -38,6 +25,12 @@ namespace Rice::detail
38
25
  {
39
26
  }
40
27
 
28
+ template<typename T, typename Iterator_Func_T>
29
+ inline Resolved NativeIterator<T, Iterator_Func_T>::matches(int argc, const VALUE* argv, VALUE self)
30
+ {
31
+ return Resolved{ Convertible::Exact, 1.0, this };
32
+ }
33
+
41
34
  template<typename T, typename Iterator_Func_T>
42
35
  inline VALUE NativeIterator<T, Iterator_Func_T>::createRubyEnumerator(VALUE self)
43
36
  {
@@ -48,12 +41,13 @@ namespace Rice::detail
48
41
  return cpp_protect([&]
49
42
  {
50
43
  // Get the iterator instance
51
- using Iter_T = NativeIterator<T, Iterator_Func_T>;
52
44
  // Class is easy
53
45
  VALUE klass = protect(rb_class_of, recv);
54
46
  // Read the method_id from an attribute we added to the enumerator instance
55
- ID method_id = protect(rb_ivar_get, eobj, rb_intern("rice_method"));
56
- Iter_T* iterator = detail::Registries::instance.natives.lookup<Iter_T*>(klass, method_id);
47
+ Identifier identifier = protect(rb_ivar_get, eobj, rb_intern("rice_method"));
48
+
49
+ const std::vector<std::unique_ptr<Native>>& natives = detail::Registries::instance.natives.lookup(klass, identifier.id());
50
+ NativeIterator_T* iterator = static_cast<NativeIterator_T*>(natives.back().get());
57
51
 
58
52
  // Get the wrapped C++ instance
59
53
  T* receiver = detail::From_Ruby<T*>().convert(recv);
@@ -67,19 +61,18 @@ namespace Rice::detail
67
61
  });
68
62
  };
69
63
 
70
- VALUE method_sym = Identifier(this->method_name_).to_sym();
71
- VALUE enumerator = protect(rb_enumeratorize_with_size, self, method_sym, 0, nullptr, rb_size_function);
64
+ Identifier identifier(this->method_name_);
65
+ VALUE enumerator = protect(rb_enumeratorize_with_size, self, identifier.to_sym(), 0, nullptr, rb_size_function);
72
66
 
73
67
  // Hack the enumerator object by storing name_ on the enumerator object so
74
68
  // the rb_size_function above has access to it
75
- ID method_id = Identifier(this->method_name_).id();
76
- protect(rb_ivar_set, enumerator, rb_intern("rice_method"), method_id);
69
+ protect(rb_ivar_set, enumerator, rb_intern("rice_method"), identifier.id());
77
70
 
78
71
  return enumerator;
79
72
  }
80
73
 
81
74
  template<typename T, typename Iterator_Func_T>
82
- inline VALUE NativeIterator<T, Iterator_Func_T>::operator()(VALUE self)
75
+ inline VALUE NativeIterator<T, Iterator_Func_T>::operator()(int argc, const VALUE* argv, VALUE self)
83
76
  {
84
77
  if (!protect(rb_block_given_p))
85
78
  {
@@ -87,13 +80,16 @@ namespace Rice::detail
87
80
  }
88
81
  else
89
82
  {
90
- T* receiver = detail::From_Ruby<T*>().convert(self);
83
+ detail::From_Ruby<T*> fromRuby;
84
+ T* receiver = fromRuby.convert(self);
85
+
91
86
  Iterator_T it = std::invoke(this->begin_, *receiver);
92
87
  Iterator_T end = std::invoke(this->end_, *receiver);
93
88
 
89
+ detail::To_Ruby<To_Ruby_T> toRuby;
94
90
  for (; it != end; ++it)
95
91
  {
96
- protect(rb_yield, detail::To_Ruby<Value_T&>().convert(*it));
92
+ protect(rb_yield, toRuby.convert(*it));
97
93
  }
98
94
 
99
95
  return self;
@@ -1,30 +1,39 @@
1
1
  #ifndef Rice__detail__NativeRegistry__hpp
2
2
  #define Rice__detail__NativeRegistry__hpp
3
3
 
4
- #include <unordered_map>
5
- #include <any>
6
-
7
- #include "ruby.hpp"
4
+ #include <map>
5
+ #include <memory>
6
+ #include <utility>
8
7
 
8
+ /* The Native Registry tracks C++ instance that are used to invoke C++ methods for Ruby.
9
+ These objects include instances of the NativeFunction, NativeIterator, NativeAttributeGet
10
+ and NativeAttributeSet Each instance is specialized to call a specific C++ function, method
11
+ or attribute that is exposed to Ruby.
12
+
13
+ The registry stores these C++ instances using a map of vectors. The map is keyed on the
14
+ the Ruby class (VALUE) and method id (ID). The value is a vector of Native pointers stored
15
+ in a std::unique_ptr. Thus the registry takes ownership of the pointers when calling
16
+ code adds them to the registry. The value is a vector to support C++ method overloading.
17
+
18
+ Note - when an existing Ruby class is redefined using rb_define_class, its VALUE stays the same
19
+ but all its methods and fields are reset. Thus any call to rb_define_class must be followed
20
+ by calling the reset method on the registry. Although redefinition shouldn't happen in
21
+ production code it happens in many places in the unit tests. */
22
+
9
23
  namespace Rice::detail
10
24
  {
11
25
  class NativeRegistry
12
26
  {
13
27
  public:
14
- // Add a new native callable object keyed by Ruby class and method_id
15
- void add(VALUE klass, ID method_id, std::any callable);
16
-
17
- // Returns the Rice data for the currently active Ruby method
18
- template <typename Return_T>
19
- Return_T lookup();
20
-
21
- template <typename Return_T>
22
- Return_T lookup(VALUE klass, ID method_id);
28
+ void add(VALUE klass, ID methodId, std::unique_ptr<Native>& native);
29
+ void reset(VALUE klass);
30
+ const std::vector<std::unique_ptr<Native>>& lookup(VALUE klass, ID methodId);
23
31
 
24
32
  private:
25
- std::unordered_multimap<ID, std::pair<VALUE, std::any>> natives_ = {};
33
+ // Key - Ruby klass/method
34
+ // Value - Vector of Native pointers owned by the registry (thus wrapped in std::unique_ptr)
35
+ std::map<std::pair<VALUE, ID>, std::vector<std::unique_ptr<Native>>> natives_ = {};
26
36
  };
27
37
  }
28
- #include "NativeRegistry.ipp"
29
38
 
30
39
  #endif // Rice__detail__NativeRegistry__hpp