rice 4.3.3 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +63 -26
- data/README.md +7 -2
- data/Rakefile +7 -1
- data/include/rice/rice.hpp +7291 -4430
- data/include/rice/stl.hpp +769 -222
- data/lib/mkmf-rice.rb +37 -95
- data/rice/Address_Registration_Guard.hpp +72 -3
- data/rice/Arg.hpp +19 -5
- data/rice/Arg.ipp +24 -0
- data/rice/Callback.hpp +21 -0
- data/rice/Callback.ipp +13 -0
- data/rice/Constructor.hpp +4 -27
- data/rice/Constructor.ipp +79 -0
- data/rice/Data_Object.hpp +74 -3
- data/rice/Data_Object.ipp +324 -32
- data/rice/Data_Type.hpp +215 -3
- data/rice/Data_Type.ipp +125 -64
- data/rice/Director.hpp +0 -2
- data/rice/Enum.hpp +4 -6
- data/rice/Enum.ipp +101 -57
- data/rice/Exception.hpp +62 -2
- data/rice/Exception.ipp +7 -12
- data/rice/JumpException.hpp +44 -0
- data/rice/JumpException.ipp +48 -0
- data/rice/MemoryView.hpp +11 -0
- data/rice/MemoryView.ipp +43 -0
- data/rice/Return.hpp +6 -26
- data/rice/Return.ipp +10 -16
- data/rice/detail/DefaultHandler.hpp +12 -0
- data/rice/detail/DefaultHandler.ipp +8 -0
- data/rice/detail/HandlerRegistry.hpp +5 -35
- data/rice/detail/HandlerRegistry.ipp +7 -11
- data/rice/detail/InstanceRegistry.hpp +1 -4
- data/rice/detail/MethodInfo.hpp +15 -5
- data/rice/detail/MethodInfo.ipp +78 -6
- data/rice/detail/Native.hpp +32 -0
- data/rice/detail/Native.ipp +129 -0
- data/rice/detail/NativeAttributeGet.hpp +51 -0
- data/rice/detail/NativeAttributeGet.ipp +51 -0
- data/rice/detail/NativeAttributeSet.hpp +43 -0
- data/rice/detail/NativeAttributeSet.ipp +82 -0
- data/rice/detail/NativeCallbackFFI.hpp +55 -0
- data/rice/detail/NativeCallbackFFI.ipp +151 -0
- data/rice/detail/NativeCallbackSimple.hpp +30 -0
- data/rice/detail/NativeCallbackSimple.ipp +29 -0
- data/rice/detail/NativeFunction.hpp +20 -21
- data/rice/detail/NativeFunction.ipp +199 -64
- data/rice/detail/NativeIterator.hpp +8 -11
- data/rice/detail/NativeIterator.ipp +27 -31
- data/rice/detail/NativeRegistry.hpp +24 -15
- data/rice/detail/NativeRegistry.ipp +23 -48
- data/rice/detail/Proc.hpp +4 -0
- data/rice/detail/Proc.ipp +85 -0
- data/rice/detail/Registries.hpp +0 -7
- data/rice/detail/Registries.ipp +0 -18
- data/rice/detail/RubyFunction.hpp +0 -3
- data/rice/detail/RubyFunction.ipp +4 -8
- data/rice/detail/RubyType.hpp +19 -0
- data/rice/detail/RubyType.ipp +187 -0
- data/rice/detail/TupleIterator.hpp +14 -0
- data/rice/detail/Type.hpp +5 -6
- data/rice/detail/Type.ipp +150 -33
- data/rice/detail/TypeRegistry.hpp +15 -7
- data/rice/detail/TypeRegistry.ipp +105 -12
- data/rice/detail/Wrapper.hpp +6 -5
- data/rice/detail/Wrapper.ipp +45 -23
- data/rice/detail/cpp_protect.hpp +5 -6
- data/rice/detail/default_allocation_func.ipp +0 -2
- data/rice/detail/from_ruby.hpp +37 -3
- data/rice/detail/from_ruby.ipp +911 -454
- data/rice/detail/ruby.hpp +18 -0
- data/rice/detail/to_ruby.hpp +41 -3
- data/rice/detail/to_ruby.ipp +437 -113
- data/rice/global_function.hpp +0 -4
- data/rice/global_function.ipp +1 -2
- data/rice/rice.hpp +105 -22
- data/rice/ruby_mark.hpp +4 -3
- data/rice/stl.hpp +4 -0
- data/test/embed_ruby.cpp +4 -1
- data/test/extconf.rb +2 -0
- data/test/ruby/test_multiple_extensions_same_class.rb +14 -14
- data/test/test_Address_Registration_Guard.cpp +5 -0
- data/test/test_Array.cpp +12 -1
- data/test/test_Attribute.cpp +103 -21
- data/test/test_Builtin_Object.cpp +5 -0
- data/test/test_Callback.cpp +231 -0
- data/test/test_Class.cpp +5 -31
- data/test/test_Constructor.cpp +69 -6
- data/test/test_Data_Object.cpp +9 -4
- data/test/test_Data_Type.cpp +428 -64
- data/test/test_Director.cpp +10 -5
- data/test/test_Enum.cpp +152 -40
- data/test/test_Exception.cpp +235 -0
- data/test/test_File.cpp +70 -0
- data/test/test_From_Ruby.cpp +542 -0
- data/test/test_Hash.cpp +5 -0
- data/test/test_Identifier.cpp +5 -0
- data/test/test_Inheritance.cpp +6 -1
- data/test/test_Iterator.cpp +5 -0
- data/test/test_JumpException.cpp +22 -0
- data/test/test_Keep_Alive.cpp +6 -1
- data/test/test_Keep_Alive_No_Wrapper.cpp +5 -0
- data/test/test_Memory_Management.cpp +5 -0
- data/test/test_Module.cpp +118 -64
- data/test/test_Native_Registry.cpp +2 -33
- data/test/test_Object.cpp +5 -0
- data/test/test_Overloads.cpp +631 -0
- data/test/test_Ownership.cpp +67 -4
- data/test/test_Proc.cpp +45 -0
- data/test/test_Self.cpp +5 -0
- data/test/test_Stl_Exception.cpp +109 -0
- data/test/test_Stl_Map.cpp +22 -8
- data/test/test_Stl_Optional.cpp +5 -0
- data/test/test_Stl_Pair.cpp +7 -2
- data/test/test_Stl_Reference_Wrapper.cpp +5 -0
- data/test/test_Stl_SmartPointer.cpp +210 -5
- data/test/test_Stl_String.cpp +5 -0
- data/test/test_Stl_String_View.cpp +5 -0
- data/test/test_Stl_Type.cpp +147 -0
- data/test/test_Stl_Unordered_Map.cpp +18 -7
- data/test/test_Stl_Variant.cpp +5 -0
- data/test/test_Stl_Vector.cpp +130 -8
- data/test/test_String.cpp +5 -0
- data/test/test_Struct.cpp +5 -0
- data/test/test_Symbol.cpp +5 -0
- data/test/test_Template.cpp +192 -0
- data/test/test_To_Ruby.cpp +152 -0
- data/test/test_Tracking.cpp +1 -0
- data/test/test_Type.cpp +100 -0
- data/test/test_global_functions.cpp +53 -6
- data/test/unittest.cpp +8 -0
- metadata +37 -20
- data/lib/version.rb +0 -3
- data/rice/Address_Registration_Guard_defn.hpp +0 -79
- data/rice/Data_Object_defn.hpp +0 -84
- data/rice/Data_Type_defn.hpp +0 -190
- data/rice/Exception_defn.hpp +0 -68
- data/rice/HandlerRegistration.hpp +0 -15
- data/rice/Identifier.hpp +0 -50
- data/rice/Identifier.ipp +0 -29
- data/rice/detail/ExceptionHandler.hpp +0 -8
- data/rice/detail/ExceptionHandler.ipp +0 -28
- data/rice/detail/ExceptionHandler_defn.hpp +0 -77
- data/rice/detail/Jump_Tag.hpp +0 -21
- data/rice/detail/NativeAttribute.hpp +0 -64
- data/rice/detail/NativeAttribute.ipp +0 -112
- data/rice/detail/from_ruby_defn.hpp +0 -38
- data/rice/detail/to_ruby_defn.hpp +0 -48
- data/test/test_Jump_Tag.cpp +0 -17
- 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
|
-
//
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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>::
|
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
|
-
|
29
|
-
|
32
|
+
NativeFunction_T* native = (NativeFunction_T*)callback_arg;
|
33
|
+
return (*native)(argc, argv, Qnil);
|
34
|
+
}
|
30
35
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
54
|
-
|
62
|
+
NativeFunction<Class_T, Function_T, IsMethod>::NativeFunction(Function_T function)
|
63
|
+
: NativeFunction(Qnil, "", function, new MethodInfo(arity))
|
55
64
|
{
|
56
|
-
|
57
|
-
|
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
|
73
|
+
return To_Ruby<To_Ruby_T>(&this->methodInfo_->returnInfo);
|
60
74
|
}
|
61
75
|
else
|
62
76
|
{
|
63
|
-
return
|
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
|
-
|
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
|
71
|
-
if constexpr (std::is_constructible_v<
|
85
|
+
// Does the From_Ruby instantiation work with Arg?
|
86
|
+
if constexpr (std::is_constructible_v<From_Ruby<T>, Arg*>)
|
72
87
|
{
|
73
|
-
return
|
88
|
+
return From_Ruby<T>(this->methodInfo_->arg(I));
|
74
89
|
}
|
75
90
|
else
|
76
91
|
{
|
77
|
-
return
|
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
|
-
|
104
|
+
Resolved NativeFunction<Class_T, Function_T, IsMethod>::matches(int argc, const VALUE* argv, VALUE self)
|
90
105
|
{
|
91
|
-
//
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
std::
|
101
|
-
|
102
|
-
|
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
|
-
|
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
|
-
|
108
|
-
|
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
|
-
|
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
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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(
|
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,
|
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 =
|
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
|
2
|
-
#define
|
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
|
-
|
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 //
|
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
|
14
|
-
detail::protect(rb_define_method, klass, method_name.c_str(), (RUBY_METHOD_FUNC)&
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
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
|
-
|
56
|
-
|
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
|
-
|
71
|
-
VALUE enumerator = protect(rb_enumeratorize_with_size, self,
|
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
|
-
|
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
|
-
|
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,
|
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 <
|
5
|
-
#include <
|
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
|
-
|
15
|
-
void
|
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
|
-
|
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
|