rice 4.3.3 → 4.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|