rice 4.6.1 → 4.7.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 +31 -0
- data/CMakeLists.txt +0 -4
- data/Rakefile +2 -8
- data/bin/rice-doc.rb +212 -0
- data/bin/rice-rbs.rb +93 -0
- data/include/rice/rice.hpp +4972 -4015
- data/include/rice/stl.hpp +822 -294
- data/lib/rice/doc/cpp_reference.rb +166 -0
- data/lib/rice/doc/doxygen.rb +294 -0
- data/lib/rice/doc/mkdocs.rb +298 -0
- data/lib/rice/doc/rice.rb +29 -0
- data/lib/rice/doc/ruby.rb +37 -0
- data/lib/rice/doc.rb +5 -0
- data/lib/{make_rice_headers.rb → rice/make_rice_headers.rb} +3 -0
- data/lib/rice/native.rb +18 -0
- data/lib/rice/native_registry.rb +21 -0
- data/lib/rice/parameter.rb +7 -0
- data/lib/rice/rbs.rb +104 -0
- data/lib/rice/version.rb +1 -1
- data/lib/rice.rb +4 -0
- data/lib/rubygems/cmake_builder.rb +24 -27
- data/rice/Arg.hpp +4 -4
- data/rice/Arg.ipp +4 -4
- data/rice/Buffer.hpp +32 -28
- data/rice/Buffer.ipp +306 -178
- data/rice/Data_Object.ipp +101 -82
- data/rice/Data_Type.hpp +5 -7
- data/rice/Data_Type.ipp +48 -29
- data/rice/Enum.ipp +15 -21
- data/rice/Function.hpp +17 -0
- data/rice/Function.ipp +13 -0
- data/rice/Pointer.hpp +15 -0
- data/rice/Pointer.ipp +49 -0
- data/rice/Return.hpp +1 -1
- data/rice/Return.ipp +2 -2
- data/rice/api.hpp +30 -0
- data/rice/cpp_api/Array.hpp +2 -2
- data/rice/cpp_api/Array.ipp +50 -5
- data/rice/cpp_api/Class.hpp +0 -5
- data/rice/cpp_api/Class.ipp +19 -0
- data/rice/cpp_api/Hash.ipp +20 -0
- data/rice/cpp_api/Module.hpp +6 -3
- data/rice/cpp_api/Module.ipp +49 -11
- data/rice/cpp_api/Object.ipp +31 -2
- data/rice/cpp_api/String.hpp +1 -2
- data/rice/cpp_api/String.ipp +21 -1
- data/rice/cpp_api/Struct.ipp +5 -0
- data/rice/cpp_api/Symbol.ipp +34 -0
- data/rice/cpp_api/shared_methods.hpp +12 -12
- data/rice/detail/MethodInfo.hpp +4 -2
- data/rice/detail/MethodInfo.ipp +19 -3
- data/rice/detail/ModuleRegistry.hpp +18 -0
- data/rice/detail/ModuleRegistry.ipp +25 -0
- data/rice/detail/Native.hpp +45 -2
- data/rice/detail/Native.ipp +196 -2
- data/rice/detail/NativeAttributeGet.hpp +9 -4
- data/rice/detail/NativeAttributeGet.ipp +65 -11
- data/rice/detail/NativeAttributeSet.hpp +4 -0
- data/rice/detail/NativeAttributeSet.ipp +30 -2
- data/rice/detail/NativeCallbackFFI.ipp +2 -2
- data/rice/detail/NativeCallbackSimple.ipp +1 -1
- data/rice/detail/NativeFunction.hpp +11 -49
- data/rice/detail/NativeFunction.ipp +82 -379
- data/rice/detail/NativeInvoker.hpp +74 -0
- data/rice/detail/NativeInvoker.ipp +197 -0
- data/rice/detail/NativeIterator.hpp +4 -0
- data/rice/detail/NativeIterator.ipp +19 -0
- data/rice/detail/NativeMethod.hpp +97 -0
- data/rice/detail/NativeMethod.ipp +332 -0
- data/rice/detail/NativeProc.hpp +51 -0
- data/rice/detail/NativeProc.ipp +133 -0
- data/rice/detail/NativeRegistry.hpp +8 -0
- data/rice/detail/NativeRegistry.ipp +27 -0
- data/rice/detail/Parameter.hpp +47 -0
- data/rice/detail/Parameter.ipp +105 -0
- data/rice/detail/Proc.ipp +14 -13
- data/rice/detail/Registries.hpp +1 -0
- data/rice/detail/RubyType.hpp +0 -2
- data/rice/detail/RubyType.ipp +15 -33
- data/rice/detail/Type.hpp +44 -8
- data/rice/detail/Type.ipp +151 -49
- data/rice/detail/TypeRegistry.hpp +3 -0
- data/rice/detail/TypeRegistry.ipp +17 -27
- data/rice/detail/Types.ipp +430 -0
- data/rice/detail/Wrapper.hpp +12 -0
- data/rice/detail/Wrapper.ipp +45 -2
- data/rice/detail/from_ruby.ipp +567 -1073
- data/rice/detail/ruby.hpp +1 -0
- data/rice/detail/to_ruby.ipp +4 -635
- data/rice/libc/file.ipp +3 -6
- data/rice/rice.hpp +22 -12
- data/rice/rice_api/Arg.hpp +7 -0
- data/rice/rice_api/Arg.ipp +9 -0
- data/rice/rice_api/ModuleRegistry.hpp +7 -0
- data/rice/rice_api/ModuleRegistry.ipp +10 -0
- data/rice/rice_api/Native.hpp +7 -0
- data/rice/rice_api/Native.ipp +52 -0
- data/rice/rice_api/NativeRegistry.hpp +7 -0
- data/rice/rice_api/NativeRegistry.ipp +21 -0
- data/rice/rice_api/Parameter.hpp +7 -0
- data/rice/rice_api/Parameter.ipp +11 -0
- data/rice/rice_api/Registries.hpp +6 -0
- data/rice/rice_api/Registries.ipp +12 -0
- data/rice/rice_api/TypeRegistry.hpp +7 -0
- data/rice/rice_api/TypeRegistry.ipp +10 -0
- data/rice/stl/complex.ipp +35 -0
- data/rice/stl/exception.ipp +20 -7
- data/rice/stl/filesystem.hpp +6 -0
- data/rice/stl/filesystem.ipp +34 -0
- data/rice/stl/map.ipp +13 -21
- data/rice/stl/monostate.ipp +37 -1
- data/rice/stl/multimap.ipp +17 -24
- data/rice/stl/optional.ipp +47 -2
- data/rice/stl/pair.ipp +23 -58
- data/rice/stl/reference_wrapper.ipp +22 -1
- data/rice/stl/set.ipp +17 -9
- data/rice/stl/shared_ptr.ipp +44 -17
- data/rice/stl/string.ipp +175 -7
- data/rice/stl/string_view.ipp +5 -0
- data/rice/stl/tuple.ipp +38 -9
- data/rice/stl/unique_ptr.ipp +46 -2
- data/rice/stl/unordered_map.ipp +13 -21
- data/rice/stl/variant.ipp +47 -11
- data/rice/stl/vector.ipp +183 -104
- data/rice/stl.hpp +1 -0
- data/rice/traits/function_traits.hpp +2 -2
- data/rice/traits/method_traits.hpp +5 -16
- data/rice/traits/rice_traits.hpp +24 -4
- data/rice.gemspec +11 -22
- data/test/embed_ruby.cpp +0 -3
- data/test/test_Array.cpp +38 -38
- data/test/test_Attribute.cpp +187 -2
- data/test/test_Buffer.cpp +302 -26
- data/test/test_Callback.cpp +2 -3
- data/test/test_Class.cpp +5 -5
- data/test/test_Data_Object.cpp +0 -55
- data/test/test_Data_Type.cpp +19 -30
- data/test/test_Enum.cpp +4 -46
- data/test/test_From_Ruby.cpp +88 -81
- data/test/test_GVL.cpp +109 -0
- data/test/test_Iterator.cpp +1 -1
- data/test/test_Keep_Alive_No_Wrapper.cpp +5 -3
- data/test/test_Module.cpp +8 -9
- data/test/test_Object.cpp +1 -1
- data/test/test_Overloads.cpp +3 -3
- data/test/test_Stl_Map.cpp +8 -8
- data/test/test_Stl_Multimap.cpp +4 -4
- data/test/test_Stl_Pair.cpp +5 -3
- data/test/test_Stl_SharedPtr.cpp +24 -12
- data/test/test_Stl_Tuple.cpp +1 -1
- data/test/test_Stl_UniquePtr.cpp +8 -0
- data/test/test_Stl_Unordered_Map.cpp +9 -9
- data/test/test_Stl_Variant.cpp +9 -3
- data/test/test_Stl_Vector.cpp +118 -13
- data/test/test_To_Ruby.cpp +35 -28
- data/test/test_Type.cpp +256 -53
- data/test/unittest.hpp +35 -0
- metadata +66 -34
- data/rice/Init.hpp +0 -8
- data/rice/Init.ipp +0 -8
- data/rice/detail/RubyFunction.hpp +0 -31
- data/rice/detail/RubyFunction.ipp +0 -77
- data/sample/callbacks/extconf.rb +0 -5
- data/sample/callbacks/sample_callbacks.cpp +0 -35
- data/sample/callbacks/test.rb +0 -28
- data/sample/enum/extconf.rb +0 -5
- data/sample/enum/sample_enum.cpp +0 -40
- data/sample/enum/test.rb +0 -8
- data/sample/inheritance/animals.cpp +0 -82
- data/sample/inheritance/extconf.rb +0 -5
- data/sample/inheritance/test.rb +0 -7
- data/sample/map/extconf.rb +0 -5
- data/sample/map/map.cpp +0 -73
- data/sample/map/test.rb +0 -7
- data/test/ext/t1/Foo.hpp +0 -10
- data/test/ext/t1/extconf.rb +0 -4
- data/test/ext/t1/t1.cpp +0 -13
- data/test/ext/t2/extconf.rb +0 -4
- data/test/ext/t2/t2.cpp +0 -11
- data/test/ruby/test_callbacks_sample.rb +0 -28
- data/test/ruby/test_multiple_extensions.rb +0 -18
- data/test/ruby/test_multiple_extensions_same_class.rb +0 -14
- data/test/ruby/test_multiple_extensions_with_inheritance.rb +0 -20
- /data/test/{test_Stl_Type.cpp → test_Stl_Type_Info.cpp} +0 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
#include <algorithm>
|
|
2
|
+
#include <array>
|
|
3
|
+
#include <stdexcept>
|
|
4
|
+
#include <sstream>
|
|
5
|
+
#include <tuple>
|
|
6
|
+
|
|
7
|
+
namespace Rice::detail
|
|
8
|
+
{
|
|
9
|
+
template<typename Class_T, typename Method_T>
|
|
10
|
+
void NativeMethod<Class_T, Method_T>::define(VALUE klass, std::string method_name, Method_T method, MethodInfo* methodInfo)
|
|
11
|
+
{
|
|
12
|
+
// Verify return and argument types
|
|
13
|
+
Native::verify_type<Return_T>(methodInfo->returnInfo()->isBuffer());
|
|
14
|
+
auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
|
|
15
|
+
Native::verify_args<Arg_Ts>(methodInfo, indices);
|
|
16
|
+
|
|
17
|
+
// Have we defined this method yet in Ruby?
|
|
18
|
+
Identifier identifier(method_name);
|
|
19
|
+
const std::vector<std::unique_ptr<Native>>& natives = Registries::instance.natives.lookup(klass, identifier.id());
|
|
20
|
+
if (natives.empty())
|
|
21
|
+
{
|
|
22
|
+
// Tell Ruby to invoke the static resolved method defined above
|
|
23
|
+
detail::protect(rb_define_method, klass, method_name.c_str(), (RUBY_METHOD_FUNC)&Native::resolve, -1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Create a NativeMethod instance and save it to the NativeRegistry. There may be multiple
|
|
27
|
+
// NativeMethod instances for a specific method because C++ supports method overloading.
|
|
28
|
+
NativeMethod_T* NativeMethod = new NativeMethod_T(klass, method_name, std::forward<Method_T>(method), methodInfo);
|
|
29
|
+
std::unique_ptr<Native> native(NativeMethod);
|
|
30
|
+
detail::Registries::instance.natives.add(klass, identifier.id(), native);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
template<typename Class_T, typename Method_T>
|
|
34
|
+
NativeMethod<Class_T, Method_T>::NativeMethod(VALUE klass, std::string method_name, Method_T method, MethodInfo* methodInfo)
|
|
35
|
+
: Native(Native::create_parameters<Arg_Ts>(methodInfo)),
|
|
36
|
+
klass_(klass), method_name_(method_name), method_(method), methodInfo_(methodInfo),
|
|
37
|
+
toRuby_(methodInfo->returnInfo())
|
|
38
|
+
{
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
template<typename Class_T, typename Method_T>
|
|
42
|
+
template<std::size_t... I>
|
|
43
|
+
std::vector<std::string> NativeMethod<Class_T, Method_T>::argTypeNames(std::ostringstream& stream, std::index_sequence<I...>& indices)
|
|
44
|
+
{
|
|
45
|
+
std::vector<std::string> result;
|
|
46
|
+
for (std::unique_ptr<ParameterAbstract>& parameter : this->parameters_)
|
|
47
|
+
{
|
|
48
|
+
result.push_back(parameter->cppTypeName());
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
template<typename Class_T, typename Method_T>
|
|
54
|
+
std::string NativeMethod<Class_T, Method_T>::toString()
|
|
55
|
+
{
|
|
56
|
+
std::ostringstream result;
|
|
57
|
+
|
|
58
|
+
detail::TypeMapper<Return_T> typeReturnMapper;
|
|
59
|
+
result << typeReturnMapper.simplifiedName() << " ";
|
|
60
|
+
|
|
61
|
+
if (!std::is_null_pointer_v<Receiver_T>)
|
|
62
|
+
{
|
|
63
|
+
detail::TypeMapper<Receiver_T> typeReceiverMapper;
|
|
64
|
+
result << typeReceiverMapper.simplifiedName() << "::";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
result << this->method_name_;
|
|
68
|
+
|
|
69
|
+
result << "(";
|
|
70
|
+
|
|
71
|
+
auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
|
|
72
|
+
std::vector<std::string> argTypeNames = this->argTypeNames(result, indices);
|
|
73
|
+
for (size_t i = 0; i < argTypeNames.size(); i++)
|
|
74
|
+
{
|
|
75
|
+
result << argTypeNames[i];
|
|
76
|
+
if (i < argTypeNames.size() - 1)
|
|
77
|
+
result << ", ";
|
|
78
|
+
}
|
|
79
|
+
result << ")";
|
|
80
|
+
return result.str();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
template<typename Class_T, typename Method_T>
|
|
84
|
+
template<std::size_t... I>
|
|
85
|
+
typename NativeMethod<Class_T, Method_T>::Apply_Args_T NativeMethod<Class_T, Method_T>::getNativeValues(VALUE self, std::vector<std::optional<VALUE>>& values, std::index_sequence<I...>& indices)
|
|
86
|
+
{
|
|
87
|
+
/* Loop over each value returned from Ruby and convert it to the appropriate C++ type based
|
|
88
|
+
on the arguments (Arg_Ts) required by the C++ method. Arg_T may have const/volatile while
|
|
89
|
+
the associated From_Ruby<T> template parameter will not. Thus From_Ruby produces non-const values
|
|
90
|
+
which we let the compiler convert to const values as needed. This works except for
|
|
91
|
+
T** -> const T**, see comment in convertToNative method. */
|
|
92
|
+
return std::forward_as_tuple(this->getReceiver(self),
|
|
93
|
+
(dynamic_cast<Parameter<std::tuple_element_t<I, Arg_Ts>>*>(this->parameters_[I].get()))->
|
|
94
|
+
convertToNative(values[I])...);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
template<typename Class_T, typename Method_T>
|
|
98
|
+
typename NativeMethod<Class_T, Method_T>::Receiver_T NativeMethod<Class_T, Method_T>::getReceiver(VALUE self)
|
|
99
|
+
{
|
|
100
|
+
// Self parameter is a Ruby VALUE so no conversion is needed
|
|
101
|
+
if constexpr (std::is_same_v<Receiver_T, VALUE>)
|
|
102
|
+
{
|
|
103
|
+
return self;
|
|
104
|
+
}
|
|
105
|
+
/* This case happens when a class wrapped by Rice is calling a method
|
|
106
|
+
defined on an ancestor class. For example, the std::map size method
|
|
107
|
+
is defined on _Tree not map. Rice needs to know the actual type
|
|
108
|
+
that was wrapped so it can correctly extract the C++ object from
|
|
109
|
+
the Ruby object. */
|
|
110
|
+
else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
|
|
111
|
+
std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
|
|
112
|
+
std::is_pointer_v<Receiver_T>)
|
|
113
|
+
{
|
|
114
|
+
Class_T* instance = From_Ruby<Class_T*>().convert(self);
|
|
115
|
+
return dynamic_cast<Receiver_T>(instance);
|
|
116
|
+
}
|
|
117
|
+
else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
|
|
118
|
+
std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
|
|
119
|
+
std::is_reference_v<Receiver_T>)
|
|
120
|
+
{
|
|
121
|
+
Class_T& instance = From_Ruby<Class_T&>().convert(self);
|
|
122
|
+
return dynamic_cast<Receiver_T>(instance);
|
|
123
|
+
}
|
|
124
|
+
// Self parameter could be derived from Object or it is an C++ instance and
|
|
125
|
+
// needs to be unwrapped from Ruby
|
|
126
|
+
else
|
|
127
|
+
{
|
|
128
|
+
return From_Ruby<Receiver_T>().convert(self);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
template<typename Class_T, typename Method_T>
|
|
133
|
+
inline VALUE NativeMethod<Class_T, Method_T>::invoke(VALUE self, Apply_Args_T&& nativeArgs)
|
|
134
|
+
{
|
|
135
|
+
if constexpr (std::is_void_v<Return_T>)
|
|
136
|
+
{
|
|
137
|
+
std::apply(this->method_, std::forward<Apply_Args_T>(nativeArgs));
|
|
138
|
+
return Qnil;
|
|
139
|
+
}
|
|
140
|
+
else
|
|
141
|
+
{
|
|
142
|
+
Return_T nativeResult = std::apply(this->method_, std::forward<Apply_Args_T>(nativeArgs));
|
|
143
|
+
|
|
144
|
+
// Special handling if the method returns self. If so we do not want
|
|
145
|
+
// to create a new Ruby wrapper object and instead return self.
|
|
146
|
+
if constexpr (std::is_same_v<intrinsic_type<Return_T>, intrinsic_type<Receiver_T>>)
|
|
147
|
+
{
|
|
148
|
+
Receiver_T receiver = std::get<0>(nativeArgs);
|
|
149
|
+
|
|
150
|
+
if constexpr (std::is_pointer_v<Return_T> && std::is_pointer_v<Receiver_T>)
|
|
151
|
+
{
|
|
152
|
+
if (nativeResult == receiver)
|
|
153
|
+
return self;
|
|
154
|
+
}
|
|
155
|
+
else if constexpr (std::is_pointer_v<Return_T> && std::is_reference_v<Receiver_T>)
|
|
156
|
+
{
|
|
157
|
+
if (nativeResult == &receiver)
|
|
158
|
+
return self;
|
|
159
|
+
}
|
|
160
|
+
else if constexpr (std::is_reference_v<Return_T> && std::is_pointer_v<Receiver_T>)
|
|
161
|
+
{
|
|
162
|
+
if (&nativeResult == receiver)
|
|
163
|
+
return self;
|
|
164
|
+
}
|
|
165
|
+
else if constexpr (std::is_reference_v<Return_T> && std::is_reference_v<Receiver_T>)
|
|
166
|
+
{
|
|
167
|
+
if (&nativeResult == &receiver)
|
|
168
|
+
return self;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return this->toRuby_.convert(nativeResult);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
template<typename Class_T, typename Method_T>
|
|
177
|
+
inline VALUE NativeMethod<Class_T, Method_T>::invokeNoGVL(VALUE self, Apply_Args_T&& nativeArgs)
|
|
178
|
+
{
|
|
179
|
+
if constexpr (std::is_void_v<Return_T>)
|
|
180
|
+
{
|
|
181
|
+
no_gvl(this->method_, std::forward<Apply_Args_T>(nativeArgs));
|
|
182
|
+
return Qnil;
|
|
183
|
+
}
|
|
184
|
+
else
|
|
185
|
+
{
|
|
186
|
+
Return_T nativeResult = no_gvl(this->method_, std::forward<Apply_Args_T>(nativeArgs));
|
|
187
|
+
|
|
188
|
+
// Special handling if the method returns self. If so we do not want
|
|
189
|
+
// to create a new Ruby wrapper object and instead return self.
|
|
190
|
+
if constexpr (std::is_same_v<intrinsic_type<Return_T>, intrinsic_type<Receiver_T>>)
|
|
191
|
+
{
|
|
192
|
+
Receiver_T receiver = std::get<0>(nativeArgs);
|
|
193
|
+
|
|
194
|
+
if constexpr (std::is_pointer_v<Return_T> && std::is_pointer_v<Receiver_T>)
|
|
195
|
+
{
|
|
196
|
+
if (nativeResult == receiver)
|
|
197
|
+
return self;
|
|
198
|
+
}
|
|
199
|
+
else if constexpr (std::is_pointer_v<Return_T> && std::is_reference_v<Receiver_T>)
|
|
200
|
+
{
|
|
201
|
+
if (nativeResult == &receiver)
|
|
202
|
+
return self;
|
|
203
|
+
}
|
|
204
|
+
else if constexpr (std::is_reference_v<Return_T> && std::is_pointer_v<Receiver_T>)
|
|
205
|
+
{
|
|
206
|
+
if (&nativeResult == receiver)
|
|
207
|
+
return self;
|
|
208
|
+
}
|
|
209
|
+
else if constexpr (std::is_reference_v<Return_T> && std::is_reference_v<Receiver_T>)
|
|
210
|
+
{
|
|
211
|
+
if (&nativeResult == &receiver)
|
|
212
|
+
return self;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return this->toRuby_.convert(nativeResult);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
template<typename Class_T, typename Method_T>
|
|
221
|
+
inline void NativeMethod<Class_T, Method_T>::noWrapper(const VALUE klass, const std::string& wrapper)
|
|
222
|
+
{
|
|
223
|
+
std::stringstream message;
|
|
224
|
+
|
|
225
|
+
message << "When calling the method `";
|
|
226
|
+
message << this->method_name_;
|
|
227
|
+
message << "' we could not find the wrapper for the '";
|
|
228
|
+
message << rb_obj_classname(klass);
|
|
229
|
+
message << "' ";
|
|
230
|
+
message << wrapper;
|
|
231
|
+
message << " type. You should not use keepAlive() on a Return or Arg that is a builtin Rice type.";
|
|
232
|
+
|
|
233
|
+
throw std::runtime_error(message.str());
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
template<typename Class_T, typename Method_T>
|
|
237
|
+
void NativeMethod<Class_T, Method_T>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<std::optional<VALUE>>& rubyValues)
|
|
238
|
+
{
|
|
239
|
+
// Self will be Qnil for wrapped procs
|
|
240
|
+
if (self == Qnil)
|
|
241
|
+
return;
|
|
242
|
+
|
|
243
|
+
// selfWrapper will be nullptr if this(self) is a builtin type and not an external(wrapped) type
|
|
244
|
+
// it is highly unlikely that keepAlive is used in this case but we check anyway
|
|
245
|
+
WrapperBase* selfWrapper = getWrapper(self);
|
|
246
|
+
|
|
247
|
+
// Check method arguments
|
|
248
|
+
for (const Arg& arg : (*this->methodInfo_))
|
|
249
|
+
{
|
|
250
|
+
if (arg.isKeepAlive())
|
|
251
|
+
{
|
|
252
|
+
if (selfWrapper == nullptr)
|
|
253
|
+
{
|
|
254
|
+
noWrapper(self, "self");
|
|
255
|
+
}
|
|
256
|
+
selfWrapper->addKeepAlive(rubyValues[arg.position].value());
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Check return value
|
|
261
|
+
if (this->methodInfo_->returnInfo()->isKeepAlive())
|
|
262
|
+
{
|
|
263
|
+
if (selfWrapper == nullptr)
|
|
264
|
+
{
|
|
265
|
+
noWrapper(self, "self");
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// returnWrapper will be nullptr if returnValue is a built-in type and not an external(wrapped) type
|
|
269
|
+
WrapperBase* returnWrapper = getWrapper(returnValue);
|
|
270
|
+
if (returnWrapper == nullptr)
|
|
271
|
+
{
|
|
272
|
+
noWrapper(returnValue, "return");
|
|
273
|
+
}
|
|
274
|
+
returnWrapper->addKeepAlive(self);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
template<typename Class_T, typename Method_T>
|
|
279
|
+
VALUE NativeMethod<Class_T, Method_T>::operator()(size_t argc, const VALUE* argv, VALUE self)
|
|
280
|
+
{
|
|
281
|
+
// Get the ruby values and make sure we have the correct number
|
|
282
|
+
std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(argc, argv, true);
|
|
283
|
+
auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
|
|
284
|
+
Apply_Args_T nativeArgs = this->getNativeValues(self, rubyValues, indices);
|
|
285
|
+
|
|
286
|
+
bool noGvl = this->methodInfo_->function()->isNoGvl();
|
|
287
|
+
|
|
288
|
+
VALUE result = Qnil;
|
|
289
|
+
|
|
290
|
+
if (noGvl)
|
|
291
|
+
{
|
|
292
|
+
result = this->invokeNoGVL(self, std::forward<Apply_Args_T>(nativeArgs));
|
|
293
|
+
}
|
|
294
|
+
else
|
|
295
|
+
{
|
|
296
|
+
result = this->invoke(self, std::forward<Apply_Args_T>(nativeArgs));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Check if any method arguments or return values need to have their lifetimes tied to the receiver
|
|
300
|
+
this->checkKeepAlive(self, result, rubyValues);
|
|
301
|
+
|
|
302
|
+
return result;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
template<typename Class_T, typename Method_T>
|
|
306
|
+
inline std::string NativeMethod<Class_T, Method_T>::name()
|
|
307
|
+
{
|
|
308
|
+
return this->method_name_;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
template<typename Class_T, typename Method_T>
|
|
312
|
+
inline NativeKind NativeMethod<Class_T, Method_T>::kind()
|
|
313
|
+
{
|
|
314
|
+
return NativeKind::Method;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
template<typename Class_T, typename Method_T>
|
|
318
|
+
inline VALUE NativeMethod<Class_T, Method_T>::returnKlass()
|
|
319
|
+
{
|
|
320
|
+
// Check if an array is being returned
|
|
321
|
+
if (this->methodInfo_->returnInfo()->isBuffer())
|
|
322
|
+
{
|
|
323
|
+
TypeMapper<Pointer<Return_T>> typeMapper;
|
|
324
|
+
return typeMapper.rubyKlass();
|
|
325
|
+
}
|
|
326
|
+
else
|
|
327
|
+
{
|
|
328
|
+
TypeMapper<Return_T> typeMapper;
|
|
329
|
+
return typeMapper.rubyKlass();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#ifndef Rice__detail__Native_Proc__hpp_
|
|
2
|
+
#define Rice__detail__Native_Proc__hpp_
|
|
3
|
+
|
|
4
|
+
namespace Rice::detail
|
|
5
|
+
{
|
|
6
|
+
template<typename Proc_T>
|
|
7
|
+
class NativeProc: Native
|
|
8
|
+
{
|
|
9
|
+
public:
|
|
10
|
+
using NativeProc_T = NativeProc<Proc_T>;
|
|
11
|
+
|
|
12
|
+
// We remove const to avoid an explosion of To_Ruby specializations and Ruby doesn't
|
|
13
|
+
// have the concept of constants anyways
|
|
14
|
+
using Return_T = typename function_traits<Proc_T>::return_type;
|
|
15
|
+
using Arg_Ts = typename function_traits<Proc_T>::arg_types;
|
|
16
|
+
using To_Ruby_T = remove_cv_recursive_t<Return_T>;
|
|
17
|
+
|
|
18
|
+
// Define a new Ruby Proc to wrap a C++ function
|
|
19
|
+
static VALUE createRubyProc(Proc_T proc);
|
|
20
|
+
static NativeProc<Proc_T>* define(Proc_T proc);
|
|
21
|
+
|
|
22
|
+
// This is the method Ruby calls when invoking the proc
|
|
23
|
+
static VALUE resolve(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg);
|
|
24
|
+
|
|
25
|
+
public:
|
|
26
|
+
NativeProc(Proc_T proc, MethodInfo* methodInfo);
|
|
27
|
+
VALUE operator()(size_t argc, const VALUE* argv, VALUE self) override;
|
|
28
|
+
std::string toString() override;
|
|
29
|
+
|
|
30
|
+
std::string name() override;
|
|
31
|
+
NativeKind kind() override;
|
|
32
|
+
VALUE returnKlass() override;
|
|
33
|
+
|
|
34
|
+
private:
|
|
35
|
+
static VALUE finalizerCallback(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg);
|
|
36
|
+
|
|
37
|
+
// Convert Ruby values to C++ values
|
|
38
|
+
template<typename std::size_t...I>
|
|
39
|
+
Arg_Ts getNativeValues(std::vector<std::optional<VALUE>>& values, std::index_sequence<I...>& indices);
|
|
40
|
+
|
|
41
|
+
// Call the underlying C++ function
|
|
42
|
+
VALUE invoke(Arg_Ts&& nativeArgs);
|
|
43
|
+
|
|
44
|
+
private:
|
|
45
|
+
Proc_T proc_;
|
|
46
|
+
std::unique_ptr<MethodInfo> methodInfo_;
|
|
47
|
+
To_Ruby<To_Ruby_T> toRuby_;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#endif // Rice__detail__Native_Proc__hpp_
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#include <algorithm>
|
|
2
|
+
#include <array>
|
|
3
|
+
#include <stdexcept>
|
|
4
|
+
#include <sstream>
|
|
5
|
+
#include <tuple>
|
|
6
|
+
|
|
7
|
+
namespace Rice::detail
|
|
8
|
+
{
|
|
9
|
+
template<typename Proc_T>
|
|
10
|
+
NativeProc<Proc_T>* NativeProc<Proc_T>::define(Proc_T proc)
|
|
11
|
+
{
|
|
12
|
+
MethodInfo* methodInfo = new MethodInfo(detail::function_traits<Proc_T>::arity);
|
|
13
|
+
return new NativeProc_T(std::forward<Proc_T>(proc), methodInfo);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
template<typename Proc_T>
|
|
17
|
+
VALUE NativeProc<Proc_T>::createRubyProc(Proc_T proc)
|
|
18
|
+
{
|
|
19
|
+
NativeProc_T* nativeProc = NativeProc_T::define(std::forward<Proc_T>(proc));
|
|
20
|
+
|
|
21
|
+
// Create a Ruby proc to wrap it and pass the NativeProc as a callback parameter
|
|
22
|
+
VALUE result = rb_proc_new(NativeProc_T::resolve, (VALUE)nativeProc);
|
|
23
|
+
|
|
24
|
+
// Tie the lifetime of the NativeProc to the Ruby Proc
|
|
25
|
+
VALUE finalizer = rb_proc_new(NativeProc_T::finalizerCallback, (VALUE)nativeProc);
|
|
26
|
+
rb_define_finalizer(result, finalizer);
|
|
27
|
+
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Ruby calls this method when invoking a proc that was defined as a C++ function
|
|
32
|
+
template<typename Proc_T>
|
|
33
|
+
VALUE NativeProc<Proc_T>::resolve(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg)
|
|
34
|
+
{
|
|
35
|
+
return cpp_protect([&]
|
|
36
|
+
{
|
|
37
|
+
NativeProc_T * native = (NativeProc_T*)callback_arg;
|
|
38
|
+
return (*native)(argc, argv, Qnil);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Ruby calls this method if an instance of a NativeProc is owned by a Ruby proc. That happens when C++
|
|
43
|
+
// returns a function back to Ruby
|
|
44
|
+
template<typename Proc_T>
|
|
45
|
+
VALUE NativeProc<Proc_T>::finalizerCallback(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg)
|
|
46
|
+
{
|
|
47
|
+
NativeProc_T* native = (NativeProc_T*)callback_arg;
|
|
48
|
+
delete native;
|
|
49
|
+
return Qnil;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
template<typename Proc_T>
|
|
53
|
+
NativeProc<Proc_T>::NativeProc(Proc_T proc, MethodInfo* methodInfo) : Native(Native::create_parameters<Arg_Ts>(methodInfo)),
|
|
54
|
+
proc_(proc), methodInfo_(methodInfo)
|
|
55
|
+
{
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
template<typename Proc_T>
|
|
59
|
+
std::string NativeProc<Proc_T>::toString()
|
|
60
|
+
{
|
|
61
|
+
return "Proc";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
template<typename Proc_T>
|
|
65
|
+
template<std::size_t... I>
|
|
66
|
+
typename NativeProc<Proc_T>::Arg_Ts NativeProc<Proc_T>::getNativeValues(std::vector<std::optional<VALUE>>& values,
|
|
67
|
+
std::index_sequence<I...>& indices)
|
|
68
|
+
{
|
|
69
|
+
/* Loop over each value returned from Ruby and convert it to the appropriate C++ type based
|
|
70
|
+
on the arguments (Arg_Ts) required by the C++ function. Arg_T may have const/volatile while
|
|
71
|
+
the associated From_Ruby<T> template parameter will not. Thus From_Ruby produces non-const values
|
|
72
|
+
which we let the compiler convert to const values as needed. This works except for
|
|
73
|
+
T** -> const T**, see comment in convertToNative method. */
|
|
74
|
+
//return std::forward_as_tuple(this->getNativeValue<std::tuple_element_t<I, Arg_Ts>, I>(values)...);
|
|
75
|
+
return std::forward_as_tuple(
|
|
76
|
+
(dynamic_cast<Parameter<std::tuple_element_t<I, Arg_Ts>>*>(this->parameters_[I].get()))->
|
|
77
|
+
convertToNative(values[I])...);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
template<typename Proc_T>
|
|
81
|
+
VALUE NativeProc<Proc_T>::invoke(Arg_Ts&& nativeArgs)
|
|
82
|
+
{
|
|
83
|
+
if constexpr (std::is_void_v<Return_T>)
|
|
84
|
+
{
|
|
85
|
+
std::apply(this->proc_, std::forward<Arg_Ts>(nativeArgs));
|
|
86
|
+
return Qnil;
|
|
87
|
+
}
|
|
88
|
+
else
|
|
89
|
+
{
|
|
90
|
+
// Call the native method and get the result
|
|
91
|
+
Return_T nativeResult = std::apply(this->proc_, std::forward<Arg_Ts>(nativeArgs));
|
|
92
|
+
|
|
93
|
+
// Return the result
|
|
94
|
+
return this->toRuby_.convert(std::forward<Return_T>(nativeResult));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
template<typename Proc_T>
|
|
99
|
+
VALUE NativeProc<Proc_T>::operator()(size_t argc, const VALUE* argv, VALUE self)
|
|
100
|
+
{
|
|
101
|
+
// Get the ruby values and make sure we have the correct number
|
|
102
|
+
std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(argc, argv, true);
|
|
103
|
+
|
|
104
|
+
auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
|
|
105
|
+
|
|
106
|
+
// Convert the Ruby values to native values
|
|
107
|
+
Arg_Ts nativeValues = this->getNativeValues(rubyValues, indices);
|
|
108
|
+
|
|
109
|
+
// Now call the native method
|
|
110
|
+
VALUE result = this->invoke(std::forward<Arg_Ts>(nativeValues));
|
|
111
|
+
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
template<typename Proc_T>
|
|
116
|
+
inline std::string NativeProc< Proc_T>::name()
|
|
117
|
+
{
|
|
118
|
+
return "proc";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
template<typename Proc_T>
|
|
122
|
+
inline NativeKind NativeProc< Proc_T>::kind()
|
|
123
|
+
{
|
|
124
|
+
return NativeKind::Proc;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
template<typename Proc_T>
|
|
128
|
+
inline VALUE NativeProc<Proc_T>::returnKlass()
|
|
129
|
+
{
|
|
130
|
+
TypeMapper<Return_T> typeMapper;
|
|
131
|
+
return typeMapper.rubyKlass();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -25,8 +25,16 @@ namespace Rice::detail
|
|
|
25
25
|
class NativeRegistry
|
|
26
26
|
{
|
|
27
27
|
public:
|
|
28
|
+
// std::is_copy_constructible returns true for std::vector<std::unique_ptr> - so we need
|
|
29
|
+
// to force the issue
|
|
30
|
+
NativeRegistry() = default;
|
|
31
|
+
NativeRegistry(const NativeRegistry& other) = delete;
|
|
32
|
+
NativeRegistry& operator=(const NativeRegistry& other) = delete;
|
|
33
|
+
|
|
28
34
|
void add(VALUE klass, ID methodId, std::unique_ptr<Native>& native);
|
|
29
35
|
void reset(VALUE klass);
|
|
36
|
+
|
|
37
|
+
const std::vector<Native*> lookup(VALUE klass);
|
|
30
38
|
const std::vector<std::unique_ptr<Native>>& lookup(VALUE klass, ID methodId);
|
|
31
39
|
|
|
32
40
|
private:
|
|
@@ -33,6 +33,33 @@ namespace Rice::detail
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
inline const std::vector<Native*> NativeRegistry::lookup(VALUE klass)
|
|
37
|
+
{
|
|
38
|
+
std::vector<Native*> result;
|
|
39
|
+
|
|
40
|
+
if (rb_type(klass) == T_ICLASS)
|
|
41
|
+
{
|
|
42
|
+
klass = detail::protect(rb_class_of, klass);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (auto& pair : this->natives_)
|
|
46
|
+
{
|
|
47
|
+
const std::pair<VALUE, ID>& key = pair.first;
|
|
48
|
+
|
|
49
|
+
if (klass == key.first)
|
|
50
|
+
{
|
|
51
|
+
const std::vector<std::unique_ptr<Native>>& value = pair.second;
|
|
52
|
+
for (auto& native : value)
|
|
53
|
+
{
|
|
54
|
+
result.push_back(native.get());
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
36
63
|
inline const std::vector<std::unique_ptr<Native>>& NativeRegistry::lookup(VALUE klass, ID methodId)
|
|
37
64
|
{
|
|
38
65
|
if (rb_type(klass) == T_ICLASS)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#ifndef Rice__detail__Parameter__hpp_
|
|
2
|
+
#define Rice__detail__Parameter__hpp_
|
|
3
|
+
|
|
4
|
+
#include <optional>
|
|
5
|
+
|
|
6
|
+
namespace Rice::detail
|
|
7
|
+
{
|
|
8
|
+
class ParameterAbstract
|
|
9
|
+
{
|
|
10
|
+
public:
|
|
11
|
+
ParameterAbstract() = default;
|
|
12
|
+
ParameterAbstract(Arg* arg);
|
|
13
|
+
virtual ~ParameterAbstract() = default;
|
|
14
|
+
|
|
15
|
+
ParameterAbstract(ParameterAbstract&& other) = default;
|
|
16
|
+
ParameterAbstract& operator=(ParameterAbstract&& other) = default;
|
|
17
|
+
|
|
18
|
+
virtual Convertible matches(std::optional<VALUE>& valueOpt) = 0;
|
|
19
|
+
virtual std::string cppTypeName() = 0;
|
|
20
|
+
virtual VALUE klass() = 0;
|
|
21
|
+
|
|
22
|
+
public:
|
|
23
|
+
Arg* arg = nullptr;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
template<typename T>
|
|
27
|
+
class Parameter: public ParameterAbstract
|
|
28
|
+
{
|
|
29
|
+
public:
|
|
30
|
+
using Type = T;
|
|
31
|
+
|
|
32
|
+
Parameter() = default;
|
|
33
|
+
Parameter(Arg* arg);
|
|
34
|
+
Parameter(Parameter&& other) = default;
|
|
35
|
+
Parameter& operator=(Parameter&& other) = default;
|
|
36
|
+
|
|
37
|
+
T convertToNative(std::optional<VALUE>& valueOpt);
|
|
38
|
+
Convertible matches(std::optional<VALUE>& valueOpt) override;
|
|
39
|
+
std::string cppTypeName() override;
|
|
40
|
+
VALUE klass() override;
|
|
41
|
+
|
|
42
|
+
// std::string typeName() override;
|
|
43
|
+
private:
|
|
44
|
+
From_Ruby<remove_cv_recursive_t<T>> fromRuby_;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
#endif // Rice__detail__Parameter__hpp_
|