rice 4.3.2 → 4.5.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 (151) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -25
  3. data/README.md +7 -2
  4. data/Rakefile +7 -1
  5. data/include/rice/rice.hpp +7321 -4470
  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 -17
  52. data/rice/detail/NativeRegistry.ipp +23 -56
  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,32 +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
- #include <tuple>
7
-
8
- #include "ruby.hpp"
4
+ #include <map>
5
+ #include <memory>
6
+ #include <utility>
9
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
+
10
23
  namespace Rice::detail
11
24
  {
12
25
  class NativeRegistry
13
26
  {
14
27
  public:
15
- // Add a new native callable object keyed by Ruby class and method_id
16
- void add(VALUE klass, ID method_id, std::any callable);
17
-
18
- // Returns the Rice data for the currently active Ruby method
19
- template <typename Return_T>
20
- Return_T lookup();
21
-
22
- template <typename Return_T>
23
- 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);
24
31
 
25
32
  private:
26
- size_t key(VALUE klass, ID method_id);
27
- std::unordered_multimap<size_t, std::tuple<VALUE, ID, 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_ = {};
28
36
  };
29
37
  }
30
- #include "NativeRegistry.ipp"
31
38
 
32
39
  #endif // Rice__detail__NativeRegistry__hpp