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.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/CMakeLists.txt +0 -4
  4. data/Rakefile +2 -8
  5. data/bin/rice-doc.rb +212 -0
  6. data/bin/rice-rbs.rb +93 -0
  7. data/include/rice/rice.hpp +4972 -4015
  8. data/include/rice/stl.hpp +822 -294
  9. data/lib/rice/doc/cpp_reference.rb +166 -0
  10. data/lib/rice/doc/doxygen.rb +294 -0
  11. data/lib/rice/doc/mkdocs.rb +298 -0
  12. data/lib/rice/doc/rice.rb +29 -0
  13. data/lib/rice/doc/ruby.rb +37 -0
  14. data/lib/rice/doc.rb +5 -0
  15. data/lib/{make_rice_headers.rb → rice/make_rice_headers.rb} +3 -0
  16. data/lib/rice/native.rb +18 -0
  17. data/lib/rice/native_registry.rb +21 -0
  18. data/lib/rice/parameter.rb +7 -0
  19. data/lib/rice/rbs.rb +104 -0
  20. data/lib/rice/version.rb +1 -1
  21. data/lib/rice.rb +4 -0
  22. data/lib/rubygems/cmake_builder.rb +24 -27
  23. data/rice/Arg.hpp +4 -4
  24. data/rice/Arg.ipp +4 -4
  25. data/rice/Buffer.hpp +32 -28
  26. data/rice/Buffer.ipp +306 -178
  27. data/rice/Data_Object.ipp +101 -82
  28. data/rice/Data_Type.hpp +5 -7
  29. data/rice/Data_Type.ipp +48 -29
  30. data/rice/Enum.ipp +15 -21
  31. data/rice/Function.hpp +17 -0
  32. data/rice/Function.ipp +13 -0
  33. data/rice/Pointer.hpp +15 -0
  34. data/rice/Pointer.ipp +49 -0
  35. data/rice/Return.hpp +1 -1
  36. data/rice/Return.ipp +2 -2
  37. data/rice/api.hpp +30 -0
  38. data/rice/cpp_api/Array.hpp +2 -2
  39. data/rice/cpp_api/Array.ipp +50 -5
  40. data/rice/cpp_api/Class.hpp +0 -5
  41. data/rice/cpp_api/Class.ipp +19 -0
  42. data/rice/cpp_api/Hash.ipp +20 -0
  43. data/rice/cpp_api/Module.hpp +6 -3
  44. data/rice/cpp_api/Module.ipp +49 -11
  45. data/rice/cpp_api/Object.ipp +31 -2
  46. data/rice/cpp_api/String.hpp +1 -2
  47. data/rice/cpp_api/String.ipp +21 -1
  48. data/rice/cpp_api/Struct.ipp +5 -0
  49. data/rice/cpp_api/Symbol.ipp +34 -0
  50. data/rice/cpp_api/shared_methods.hpp +12 -12
  51. data/rice/detail/MethodInfo.hpp +4 -2
  52. data/rice/detail/MethodInfo.ipp +19 -3
  53. data/rice/detail/ModuleRegistry.hpp +18 -0
  54. data/rice/detail/ModuleRegistry.ipp +25 -0
  55. data/rice/detail/Native.hpp +45 -2
  56. data/rice/detail/Native.ipp +196 -2
  57. data/rice/detail/NativeAttributeGet.hpp +9 -4
  58. data/rice/detail/NativeAttributeGet.ipp +65 -11
  59. data/rice/detail/NativeAttributeSet.hpp +4 -0
  60. data/rice/detail/NativeAttributeSet.ipp +30 -2
  61. data/rice/detail/NativeCallbackFFI.ipp +2 -2
  62. data/rice/detail/NativeCallbackSimple.ipp +1 -1
  63. data/rice/detail/NativeFunction.hpp +11 -49
  64. data/rice/detail/NativeFunction.ipp +82 -379
  65. data/rice/detail/NativeInvoker.hpp +74 -0
  66. data/rice/detail/NativeInvoker.ipp +197 -0
  67. data/rice/detail/NativeIterator.hpp +4 -0
  68. data/rice/detail/NativeIterator.ipp +19 -0
  69. data/rice/detail/NativeMethod.hpp +97 -0
  70. data/rice/detail/NativeMethod.ipp +332 -0
  71. data/rice/detail/NativeProc.hpp +51 -0
  72. data/rice/detail/NativeProc.ipp +133 -0
  73. data/rice/detail/NativeRegistry.hpp +8 -0
  74. data/rice/detail/NativeRegistry.ipp +27 -0
  75. data/rice/detail/Parameter.hpp +47 -0
  76. data/rice/detail/Parameter.ipp +105 -0
  77. data/rice/detail/Proc.ipp +14 -13
  78. data/rice/detail/Registries.hpp +1 -0
  79. data/rice/detail/RubyType.hpp +0 -2
  80. data/rice/detail/RubyType.ipp +15 -33
  81. data/rice/detail/Type.hpp +44 -8
  82. data/rice/detail/Type.ipp +151 -49
  83. data/rice/detail/TypeRegistry.hpp +3 -0
  84. data/rice/detail/TypeRegistry.ipp +17 -27
  85. data/rice/detail/Types.ipp +430 -0
  86. data/rice/detail/Wrapper.hpp +12 -0
  87. data/rice/detail/Wrapper.ipp +45 -2
  88. data/rice/detail/from_ruby.ipp +567 -1073
  89. data/rice/detail/ruby.hpp +1 -0
  90. data/rice/detail/to_ruby.ipp +4 -635
  91. data/rice/libc/file.ipp +3 -6
  92. data/rice/rice.hpp +22 -12
  93. data/rice/rice_api/Arg.hpp +7 -0
  94. data/rice/rice_api/Arg.ipp +9 -0
  95. data/rice/rice_api/ModuleRegistry.hpp +7 -0
  96. data/rice/rice_api/ModuleRegistry.ipp +10 -0
  97. data/rice/rice_api/Native.hpp +7 -0
  98. data/rice/rice_api/Native.ipp +52 -0
  99. data/rice/rice_api/NativeRegistry.hpp +7 -0
  100. data/rice/rice_api/NativeRegistry.ipp +21 -0
  101. data/rice/rice_api/Parameter.hpp +7 -0
  102. data/rice/rice_api/Parameter.ipp +11 -0
  103. data/rice/rice_api/Registries.hpp +6 -0
  104. data/rice/rice_api/Registries.ipp +12 -0
  105. data/rice/rice_api/TypeRegistry.hpp +7 -0
  106. data/rice/rice_api/TypeRegistry.ipp +10 -0
  107. data/rice/stl/complex.ipp +35 -0
  108. data/rice/stl/exception.ipp +20 -7
  109. data/rice/stl/filesystem.hpp +6 -0
  110. data/rice/stl/filesystem.ipp +34 -0
  111. data/rice/stl/map.ipp +13 -21
  112. data/rice/stl/monostate.ipp +37 -1
  113. data/rice/stl/multimap.ipp +17 -24
  114. data/rice/stl/optional.ipp +47 -2
  115. data/rice/stl/pair.ipp +23 -58
  116. data/rice/stl/reference_wrapper.ipp +22 -1
  117. data/rice/stl/set.ipp +17 -9
  118. data/rice/stl/shared_ptr.ipp +44 -17
  119. data/rice/stl/string.ipp +175 -7
  120. data/rice/stl/string_view.ipp +5 -0
  121. data/rice/stl/tuple.ipp +38 -9
  122. data/rice/stl/unique_ptr.ipp +46 -2
  123. data/rice/stl/unordered_map.ipp +13 -21
  124. data/rice/stl/variant.ipp +47 -11
  125. data/rice/stl/vector.ipp +183 -104
  126. data/rice/stl.hpp +1 -0
  127. data/rice/traits/function_traits.hpp +2 -2
  128. data/rice/traits/method_traits.hpp +5 -16
  129. data/rice/traits/rice_traits.hpp +24 -4
  130. data/rice.gemspec +11 -22
  131. data/test/embed_ruby.cpp +0 -3
  132. data/test/test_Array.cpp +38 -38
  133. data/test/test_Attribute.cpp +187 -2
  134. data/test/test_Buffer.cpp +302 -26
  135. data/test/test_Callback.cpp +2 -3
  136. data/test/test_Class.cpp +5 -5
  137. data/test/test_Data_Object.cpp +0 -55
  138. data/test/test_Data_Type.cpp +19 -30
  139. data/test/test_Enum.cpp +4 -46
  140. data/test/test_From_Ruby.cpp +88 -81
  141. data/test/test_GVL.cpp +109 -0
  142. data/test/test_Iterator.cpp +1 -1
  143. data/test/test_Keep_Alive_No_Wrapper.cpp +5 -3
  144. data/test/test_Module.cpp +8 -9
  145. data/test/test_Object.cpp +1 -1
  146. data/test/test_Overloads.cpp +3 -3
  147. data/test/test_Stl_Map.cpp +8 -8
  148. data/test/test_Stl_Multimap.cpp +4 -4
  149. data/test/test_Stl_Pair.cpp +5 -3
  150. data/test/test_Stl_SharedPtr.cpp +24 -12
  151. data/test/test_Stl_Tuple.cpp +1 -1
  152. data/test/test_Stl_UniquePtr.cpp +8 -0
  153. data/test/test_Stl_Unordered_Map.cpp +9 -9
  154. data/test/test_Stl_Variant.cpp +9 -3
  155. data/test/test_Stl_Vector.cpp +118 -13
  156. data/test/test_To_Ruby.cpp +35 -28
  157. data/test/test_Type.cpp +256 -53
  158. data/test/unittest.hpp +35 -0
  159. metadata +66 -34
  160. data/rice/Init.hpp +0 -8
  161. data/rice/Init.ipp +0 -8
  162. data/rice/detail/RubyFunction.hpp +0 -31
  163. data/rice/detail/RubyFunction.ipp +0 -77
  164. data/sample/callbacks/extconf.rb +0 -5
  165. data/sample/callbacks/sample_callbacks.cpp +0 -35
  166. data/sample/callbacks/test.rb +0 -28
  167. data/sample/enum/extconf.rb +0 -5
  168. data/sample/enum/sample_enum.cpp +0 -40
  169. data/sample/enum/test.rb +0 -8
  170. data/sample/inheritance/animals.cpp +0 -82
  171. data/sample/inheritance/extconf.rb +0 -5
  172. data/sample/inheritance/test.rb +0 -7
  173. data/sample/map/extconf.rb +0 -5
  174. data/sample/map/map.cpp +0 -73
  175. data/sample/map/test.rb +0 -7
  176. data/test/ext/t1/Foo.hpp +0 -10
  177. data/test/ext/t1/extconf.rb +0 -4
  178. data/test/ext/t1/t1.cpp +0 -13
  179. data/test/ext/t2/extconf.rb +0 -4
  180. data/test/ext/t2/t2.cpp +0 -11
  181. data/test/ruby/test_callbacks_sample.rb +0 -28
  182. data/test/ruby/test_multiple_extensions.rb +0 -18
  183. data/test/ruby/test_multiple_extensions_same_class.rb +0 -14
  184. data/test/ruby/test_multiple_extensions_with_inheritance.rb +0 -20
  185. /data/test/{test_Stl_Type.cpp → test_Stl_Type_Info.cpp} +0 -0
@@ -35,77 +35,40 @@ namespace Rice::detail
35
35
  * calling them methods (self) or functions (no self).
36
36
  */
37
37
 
38
- template<typename Class_T, typename Function_T, bool IsMethod>
38
+ template<typename Function_T>
39
39
  class NativeFunction: Native
40
40
  {
41
41
  public:
42
- using NativeFunction_T = NativeFunction<Class_T, Function_T, IsMethod>;
42
+ using NativeFunction_T = NativeFunction<Function_T>;
43
43
 
44
44
  // We remove const to avoid an explosion of To_Ruby specializations and Ruby doesn't
45
45
  // have the concept of constants anyways
46
46
  using Return_T = typename function_traits<Function_T>::return_type;
47
- using Receiver_T = typename method_traits<Function_T, IsMethod>::Class_T;
48
- using Arg_Ts = typename method_traits<Function_T, IsMethod>::Arg_Ts;
49
- static constexpr std::size_t arity = method_traits<Function_T, IsMethod>::arity;
50
- using From_Ruby_Args_Ts = typename tuple_map<From_Ruby, Arg_Ts>::type;
47
+ using Class_T = typename function_traits<Function_T>::class_type;
48
+ using Arg_Ts = typename function_traits<Function_T>::arg_types;
51
49
  using To_Ruby_T = remove_cv_recursive_t<Return_T>;
52
50
 
53
51
  // Register function with Ruby
54
52
  static void define(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo);
55
53
 
56
- // Proc entry
57
- static VALUE procEntry(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg);
58
- static VALUE finalizerCallback(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg);
59
54
  public:
60
- // Disallow creating/copying/moving
61
- NativeFunction() = delete;
62
- NativeFunction(const NativeFunction_T&) = delete;
63
- NativeFunction(NativeFunction_T&&) = delete;
64
- void operator=(const NativeFunction_T&) = delete;
65
- void operator=(NativeFunction_T&&) = delete;
55
+ NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo);
66
56
 
67
- Resolved matches(size_t argc, const VALUE* argv, VALUE self) override;
68
57
  VALUE operator()(size_t argc, const VALUE* argv, VALUE self) override;
69
58
  std::string toString() override;
70
59
 
71
- NativeFunction(Function_T function);
72
- NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo);
73
-
74
- protected:
60
+ std::string name() override;
61
+ NativeKind kind() override;
62
+ VALUE returnKlass() override;
75
63
 
76
64
  private:
77
- template<int I>
78
- Convertible matchParameter(std::vector<std::optional<VALUE>>& value);
79
-
80
- template<std::size_t...I>
81
- Convertible matchParameters(std::vector<std::optional<VALUE>>& values, std::index_sequence<I...>& indices);
82
-
83
65
  template<std::size_t...I>
84
66
  std::vector<std::string> argTypeNames(std::ostringstream& stream, std::index_sequence<I...>& indices);
85
67
 
86
- template<typename T, std::size_t I>
87
- From_Ruby<T> createFromRuby();
88
-
89
- // Create NativeArgs which are used to convert values from Ruby to C++
90
- template<std::size_t...I>
91
- From_Ruby_Args_Ts createFromRuby(std::index_sequence<I...>& indices);
92
-
93
- // Convert C++ value to Ruby
94
- To_Ruby<To_Ruby_T> createToRuby();
95
-
96
- // Convert Ruby argv pointer to Ruby values
97
- std::vector<std::optional<VALUE>> getRubyValues(size_t argc, const VALUE* argv, bool validate);
98
-
99
- template<typename Arg_T, int I>
100
- Arg_T getNativeValue(std::vector<std::optional<VALUE>>& values);
101
-
102
68
  // Convert Ruby values to C++ values
103
69
  template<typename std::size_t...I>
104
70
  Arg_Ts getNativeValues(std::vector<std::optional<VALUE>>& values, std::index_sequence<I...>& indices);
105
71
 
106
- // Figure out what self is
107
- Receiver_T getReceiver(VALUE self);
108
-
109
72
  // Throw an exception when wrapper cannot be extracted
110
73
  [[noreturn]] void noWrapper(const VALUE klass, const std::string& wrapper);
111
74
 
@@ -113,16 +76,15 @@ namespace Rice::detail
113
76
  void checkKeepAlive(VALUE self, VALUE returnValue, std::vector<std::optional<VALUE>>& rubyValues);
114
77
 
115
78
  // Call the underlying C++ function
116
- VALUE invokeNativeFunction(Arg_Ts&& nativeArgs);
117
- VALUE invokeNativeMethod(VALUE self, Arg_Ts&& nativeArgs);
79
+ VALUE invoke(Arg_Ts&& nativeArgs);
80
+ VALUE invokeNoGVL(Arg_Ts&& nativeArgs);
118
81
 
119
82
  private:
120
83
  VALUE klass_;
121
84
  std::string method_name_;
122
85
  Function_T function_;
123
- From_Ruby_Args_Ts fromRubys_;
124
- To_Ruby<To_Ruby_T> toRuby_;
125
86
  std::unique_ptr<MethodInfo> methodInfo_;
87
+ To_Ruby<To_Ruby_T> toRuby_;
126
88
  };
127
89
  }
128
90
 
@@ -6,9 +6,14 @@
6
6
 
7
7
  namespace Rice::detail
8
8
  {
9
- template<typename Class_T, typename Function_T, bool IsMethod>
10
- void NativeFunction<Class_T, Function_T, IsMethod>::define(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
9
+ template<typename Function_T>
10
+ void NativeFunction<Function_T>::define(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
11
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
+
12
17
  // Have we defined this method yet in Ruby?
13
18
  Identifier identifier(method_name);
14
19
  const std::vector<std::unique_ptr<Native>>& natives = Registries::instance.natives.lookup(klass, identifier.id());
@@ -25,68 +30,33 @@ namespace Rice::detail
25
30
  detail::Registries::instance.natives.add(klass, identifier.id(), native);
26
31
  }
27
32
 
28
- // Ruby calls this method when invoking a proc that was defined as a C++ function
29
- template<typename Class_T, typename Function_T, bool IsMethod>
30
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::procEntry(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg)
31
- {
32
- return cpp_protect([&]
33
- {
34
- NativeFunction_T * native = (NativeFunction_T*)callback_arg;
35
- return (*native)(argc, argv, Qnil);
36
- });
37
- }
38
-
39
- // Ruby calls this method if an instance f a NativeFunction is owned by a Ruby proc. That happens when C++
40
- // returns a function back to Ruby
41
- template<typename Class_T, typename Function_T, bool IsMethod>
42
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::finalizerCallback(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg)
33
+ template<typename Function_T>
34
+ NativeFunction<Function_T>::NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
35
+ : Native(Native::create_parameters<Arg_Ts>(methodInfo)),
36
+ klass_(klass), method_name_(method_name), function_(function), methodInfo_(methodInfo),
37
+ toRuby_(methodInfo->returnInfo())
43
38
  {
44
- NativeFunction_T* native = (NativeFunction_T*)callback_arg;
45
- delete native;
46
- return Qnil;
47
- }
48
-
49
- template<typename Class_T, typename Function_T, bool IsMethod>
50
- NativeFunction<Class_T, Function_T, IsMethod>::NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
51
- : klass_(klass), method_name_(method_name), function_(function), methodInfo_(methodInfo)
52
- {
53
- // Create a tuple of NativeArgs that will convert the Ruby values to native values. For
54
- // builtin types NativeArgs will keep a copy of the native value so that it
55
- // can be passed by reference or pointer to the native function. For non-builtin types
56
- // it will just pass the value through.
57
- auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
58
- this->fromRubys_ = this->createFromRuby(indices);
59
-
60
- this->toRuby_ = this->createToRuby();
61
39
  }
62
40
 
63
- template<typename Class_T, typename Function_T, bool IsMethod>
64
- NativeFunction<Class_T, Function_T, IsMethod>::NativeFunction(Function_T function)
65
- : NativeFunction(Qnil, "", function, new MethodInfo(arity))
66
- {
67
- }
68
-
69
- template<typename Class_T, typename Function_T, bool IsMethod>
41
+ template<typename Function_T>
70
42
  template<std::size_t... I>
71
- std::vector<std::string> NativeFunction<Class_T, Function_T, IsMethod>::argTypeNames(std::ostringstream& stream, std::index_sequence<I...>& indices)
43
+ std::vector<std::string> NativeFunction<Function_T>::argTypeNames(std::ostringstream& stream, std::index_sequence<I...>& indices)
72
44
  {
73
- std::vector<std::string> typeNames;
74
- (typeNames.push_back(cppClassName(typeName(typeid(typename std::tuple_element<I, Arg_Ts>::type)))), ...);
75
- return typeNames;
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;
76
51
  }
77
52
 
78
- template<typename Class_T, typename Function_T, bool IsMethod>
79
- std::string NativeFunction<Class_T, Function_T, IsMethod>::toString()
53
+ template<typename Function_T>
54
+ std::string NativeFunction<Function_T>::toString()
80
55
  {
81
56
  std::ostringstream result;
82
57
 
83
- result << cppClassName(typeName(typeid(Return_T))) << " ";
84
-
85
- if (!std::is_null_pointer_v<Receiver_T>)
86
- {
87
- result << cppClassName(typeName(typeid(Receiver_T))) << "::";
88
- }
89
-
58
+ detail::TypeMapper<Return_T> typeMapper;
59
+ result << typeMapper.simplifiedName() << " ";
90
60
  result << this->method_name_;
91
61
 
92
62
  result << "(";
@@ -103,294 +73,24 @@ namespace Rice::detail
103
73
  return result.str();
104
74
  }
105
75
 
106
- template<typename Class_T, typename Function_T, bool IsMethod>
107
- To_Ruby<typename NativeFunction<Class_T, Function_T, IsMethod>::To_Ruby_T> NativeFunction<Class_T, Function_T, IsMethod>::createToRuby()
108
- {
109
- // Does the From_Ruby instantiation work with ReturnInfo?
110
- if constexpr (std::is_constructible_v<To_Ruby<To_Ruby_T>, Return*>)
111
- {
112
- return To_Ruby<To_Ruby_T>(&this->methodInfo_->returnInfo);
113
- }
114
- else
115
- {
116
- return To_Ruby<To_Ruby_T>();
117
- }
118
- }
119
-
120
- template<typename Class_T, typename Function_T, bool IsMethod>
121
- template<typename T, std::size_t I>
122
- From_Ruby<T> NativeFunction<Class_T, Function_T, IsMethod>::createFromRuby()
123
- {
124
- // Does the From_Ruby instantiation work with Arg?
125
- if constexpr (std::is_constructible_v<From_Ruby<T>, Arg*>)
126
- {
127
- return From_Ruby<T>(this->methodInfo_->arg(I));
128
- }
129
- else
130
- {
131
- return From_Ruby<T>();
132
- }
133
- }
134
-
135
- template<typename Class_T, typename Function_T, bool IsMethod>
136
- template<std::size_t... I>
137
- typename NativeFunction<Class_T, Function_T, IsMethod>::From_Ruby_Args_Ts NativeFunction<Class_T, Function_T, IsMethod>::createFromRuby(std::index_sequence<I...>& indices)
138
- {
139
- return std::make_tuple(createFromRuby<remove_cv_recursive_t<typename std::tuple_element<I, Arg_Ts>::type>, I>()...);
140
- }
141
-
142
- template<typename Class_T, typename Function_T, bool IsMethod>
143
- template<int I>
144
- Convertible NativeFunction<Class_T, Function_T, IsMethod>::matchParameter(std::vector<std::optional<VALUE>>& values)
145
- {
146
- Convertible result = Convertible::None;
147
- MethodInfo* methodInfo = this->methodInfo_.get();
148
- const Arg* arg = methodInfo->arg(I);
149
- std::optional<VALUE> value = values[I];
150
-
151
- // Is a VALUE being passed directly to C++ ?
152
- if (value.has_value())
153
- {
154
- if (arg->isValue())
155
- {
156
- result = Convertible::Exact;
157
- }
158
- // If index is less than argc then check with FromRuby if the VALUE is convertible
159
- // to C++.
160
- else
161
- {
162
- VALUE value = values[I].value();
163
- auto fromRuby = std::get<I>(this->fromRubys_);
164
- result = fromRuby.is_convertible(value);
165
-
166
- // If this is an exact match check if the const-ness of the value and the parameter match
167
- if (result == Convertible::Exact && rb_type(value) == RUBY_T_DATA)
168
- {
169
- // Check the constness of the Ruby wrapped value and the parameter
170
- WrapperBase* wrapper = getWrapper(value);
171
- using Parameter_T = std::tuple_element_t<I, Arg_Ts>;
172
-
173
- // Do not send a const value to a non-const parameter
174
- if (wrapper->isConst() && !is_const_any_v<Parameter_T>)
175
- {
176
- result = Convertible::None;
177
- }
178
- // It is ok to send a non-const value to a const parameter but
179
- // prefer non-const to non-const by slighly decreasing the convertible value
180
- else if (!wrapper->isConst() && is_const_any_v<Parameter_T>)
181
- {
182
- result = Convertible::Const;
183
- }
184
- }
185
- }
186
- }
187
- // Last check if a default value has been set
188
- else if (arg->hasDefaultValue())
189
- {
190
- result = Convertible::Exact;
191
- }
192
-
193
- return result;
194
- }
195
-
196
- template<typename Class_T, typename Function_T, bool IsMethod>
197
- template<std::size_t... I>
198
- Convertible NativeFunction<Class_T, Function_T, IsMethod>::matchParameters(std::vector<std::optional<VALUE>>& values,
199
- std::index_sequence<I...>& indices)
200
- {
201
- Convertible result = Convertible::Exact;
202
- ((result = result & this->matchParameter<I>(values)), ...);
203
- return result;
204
- }
205
-
206
- template<typename Class_T, typename Function_T, bool IsMethod>
207
- Resolved NativeFunction<Class_T, Function_T, IsMethod>::matches(size_t argc, const VALUE* argv, VALUE self)
208
- {
209
- // Return false if Ruby provided more arguments than the C++ method takes
210
- if (argc > arity)
211
- return Resolved{ Convertible::None, 0, this };
212
-
213
- Resolved result { Convertible::Exact, 1, this };
214
-
215
- std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(argc, argv, false);
216
- auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
217
- result.convertible = this->matchParameters(rubyValues, indices);
218
-
219
- if constexpr (arity > 0)
220
- {
221
- int providedValues = std::count_if(rubyValues.begin(), rubyValues.end(), [](std::optional<VALUE>& value)
222
- {
223
- return value.has_value();
224
- });
225
-
226
- result.parameterMatch = providedValues / (double)arity;
227
- }
228
- return result;
229
- }
230
-
231
- template<typename Class_T, typename Function_T, bool IsMethod>
232
- std::vector<std::optional<VALUE>> NativeFunction<Class_T, Function_T, IsMethod>::getRubyValues(size_t argc, const VALUE* argv, bool validate)
233
- {
234
- #undef max
235
- int size = std::max((size_t)arity, (size_t)argc);
236
- std::vector<std::optional<VALUE>> result(size);
237
-
238
- // Keyword handling
239
- if (rb_keyword_given_p())
240
- {
241
- // Keywords are stored in the last element in a hash
242
- int actualArgc = argc - 1;
243
-
244
- VALUE value = argv[actualArgc];
245
- Hash keywords(value);
246
-
247
- // Copy over leading non-keyword arguments
248
- for (int i = 0; i < actualArgc; i++)
249
- {
250
- result[i] = argv[i];
251
- }
252
-
253
- // Copy over keyword arguments
254
- for (auto pair : keywords)
255
- {
256
- Symbol key(pair.first);
257
- const Arg* arg = this->methodInfo_->arg(key.str());
258
- if (!arg)
259
- {
260
- throw std::invalid_argument("Unknown keyword: " + key.str());
261
- }
262
- result[arg->position] = pair.second.value();
263
- }
264
- }
265
- else
266
- {
267
- std::copy(argv, argv + argc, result.begin());
268
- }
269
-
270
- // Block handling. If we find a block and the last parameter is missing then
271
- // set it to the block
272
- if (rb_block_given_p() && result.size() > 0 && !result.back().has_value())
273
- {
274
- VALUE proc = rb_block_proc();
275
- result.back() = proc;
276
- }
277
-
278
- if (validate)
279
- {
280
- // Protect against user sending too many arguments
281
- if (argc > arity)
282
- {
283
- std::string message = "wrong number of arguments (given " +
284
- std::to_string(argc) + ", expected " + std::to_string(arity) + ")";
285
- throw std::invalid_argument(message);
286
- }
287
-
288
- for (size_t i=0; i<result.size(); i++)
289
- {
290
- std::optional<VALUE> value = result[i];
291
- Arg* arg = this->methodInfo_->arg(i);
292
-
293
- if (!arg->hasDefaultValue() && !value.has_value())
294
- {
295
- std::string message;
296
- message = "Missing argument. Name: " + arg->name + ". Index: " + std::to_string(arg->position) + ".";
297
- throw std::invalid_argument(message);
298
- }
299
- }
300
- }
301
-
302
- return result;
303
- }
304
-
305
- template<typename Class_T, typename Function_T, bool IsMethod>
306
- template<typename Arg_T, int I>
307
- Arg_T NativeFunction<Class_T, Function_T, IsMethod>::getNativeValue(std::vector<std::optional<VALUE>>& values)
308
- {
309
- /* In general the compiler will convert T to const T, but that does not work for converting
310
- T** to const T** (see see https://isocpp.org/wiki/faq/const-correctness#constptrptr-conversion)
311
- which comes up in the OpenCV bindings.
312
-
313
- An alternative solution is updating From_Ruby#convert to become a templated function that specifies
314
- the return type. That works but requires a lot more code changes for this one case and is not
315
- backwards compatible. */
316
-
317
- std::optional<VALUE> value = values[I];
318
- Arg* arg = this->methodInfo_->arg(I);
319
-
320
- if constexpr (is_pointer_pointer_v<Arg_T> && !std::is_convertible_v<remove_cv_recursive_t<Arg_T>, Arg_T>)
321
- {
322
- return (Arg_T)std::get<I>(this->fromRubys_).convert(value.value());
323
- }
324
- else if (value.has_value())
325
- {
326
- return std::get<I>(this->fromRubys_).convert(value.value());
327
- }
328
- else if constexpr (std::is_constructible_v<std::remove_cv_t<Arg_T>, std::remove_cv_t<std::remove_reference_t<Arg_T>>&>)
329
- {
330
- if (arg->hasDefaultValue())
331
- {
332
- return arg->defaultValue<Arg_T>();
333
- }
334
- }
335
-
336
- throw std::invalid_argument("Could not convert Rubyy value");
337
- }
338
-
339
- template<typename Class_T, typename Function_T, bool IsMethod>
76
+ template<typename Function_T>
340
77
  template<std::size_t... I>
341
- typename NativeFunction<Class_T, Function_T, IsMethod>::Arg_Ts NativeFunction<Class_T, Function_T, IsMethod>::getNativeValues(std::vector<std::optional<VALUE>>& values,
78
+ typename NativeFunction<Function_T>::Arg_Ts NativeFunction<Function_T>::getNativeValues(std::vector<std::optional<VALUE>>& values,
342
79
  std::index_sequence<I...>& indices)
343
80
  {
344
81
  /* Loop over each value returned from Ruby and convert it to the appropriate C++ type based
345
82
  on the arguments (Arg_Ts) required by the C++ function. Arg_T may have const/volatile while
346
83
  the associated From_Ruby<T> template parameter will not. Thus From_Ruby produces non-const values
347
84
  which we let the compiler convert to const values as needed. This works except for
348
- T** -> const T**, see comment in getNativeValue method. */
349
- return std::forward_as_tuple(this->getNativeValue<std::tuple_element_t<I, Arg_Ts>, I>(values)...);
350
- }
351
-
352
- template<typename Class_T, typename Function_T, bool IsMethod>
353
- typename NativeFunction<Class_T, Function_T, IsMethod>::Receiver_T NativeFunction<Class_T, Function_T, IsMethod>::getReceiver(VALUE self)
354
- {
355
- // There is no self parameter
356
- if constexpr (std::is_same_v<Receiver_T, std::nullptr_t>)
357
- {
358
- return nullptr;
359
- }
360
- // Self parameter is a Ruby VALUE so no conversion is needed
361
- else if constexpr (std::is_same_v<Receiver_T, VALUE>)
362
- {
363
- return self;
364
- }
365
- /* This case happens when a class wrapped by Rice is calling a method
366
- defined on an ancestor class. For example, the std::map size method
367
- is defined on _Tree not map. Rice needs to know the actual type
368
- that was wrapped so it can correctly extract the C++ object from
369
- the Ruby object. */
370
- else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
371
- std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
372
- std::is_pointer_v<Receiver_T>)
373
- {
374
- Class_T* instance = From_Ruby<Class_T*>().convert(self);
375
- return dynamic_cast<Receiver_T>(instance);
376
- }
377
- else if constexpr (!std::is_same_v<intrinsic_type<Receiver_T>, Class_T> &&
378
- std::is_base_of_v<intrinsic_type<Receiver_T>, Class_T> &&
379
- std::is_reference_v<Receiver_T>)
380
- {
381
- Class_T& instance = From_Ruby<Class_T&>().convert(self);
382
- return dynamic_cast<Receiver_T>(instance);
383
- }
384
- // Self parameter could be derived from Object or it is an C++ instance and
385
- // needs to be unwrapped from Ruby
386
- else
387
- {
388
- return From_Ruby<Receiver_T>().convert(self);
389
- }
85
+ T** -> const T**, see comment in convertToNative method. */
86
+ //return std::forward_as_tuple(this->getNativeValue<std::tuple_element_t<I, Arg_Ts>, I>(values)...);
87
+ return std::forward_as_tuple(
88
+ (dynamic_cast<Parameter<std::tuple_element_t<I, Arg_Ts>>*>(this->parameters_[I].get()))->
89
+ convertToNative(values[I])...);
390
90
  }
391
91
 
392
- template<typename Class_T, typename Function_T, bool IsMethod>
393
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::invokeNativeFunction(Arg_Ts&& nativeArgs)
92
+ template<typename Function_T>
93
+ VALUE NativeFunction<Function_T>::invoke(Arg_Ts&& nativeArgs)
394
94
  {
395
95
  if constexpr (std::is_void_v<Return_T>)
396
96
  {
@@ -403,57 +103,30 @@ namespace Rice::detail
403
103
  Return_T nativeResult = std::apply(this->function_, std::forward<Arg_Ts>(nativeArgs));
404
104
 
405
105
  // Return the result
406
- return this->toRuby_.convert(std::forward<Return_T>(nativeResult));
106
+ return this->toRuby_.convert(nativeResult);
407
107
  }
408
108
  }
409
109
 
410
- template<typename Class_T, typename Function_T, bool IsMethod>
411
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::invokeNativeMethod(VALUE self, Arg_Ts&& nativeArgs)
110
+ template<typename Function_T>
111
+ VALUE NativeFunction<Function_T>::invokeNoGVL(Arg_Ts&& nativeArgs)
412
112
  {
413
- Receiver_T receiver = this->getReceiver(self);
414
- auto selfAndNativeArgs = std::tuple_cat(std::forward_as_tuple(receiver), std::forward<Arg_Ts>(nativeArgs));
415
-
416
113
  if constexpr (std::is_void_v<Return_T>)
417
114
  {
418
- std::apply(this->function_, std::forward<decltype(selfAndNativeArgs)>(selfAndNativeArgs));
115
+ no_gvl(this->function_, std::forward<Arg_Ts>(nativeArgs));
419
116
  return Qnil;
420
117
  }
421
118
  else
422
119
  {
423
- Return_T nativeResult = std::apply(this->function_, std::forward<decltype(selfAndNativeArgs)>(selfAndNativeArgs));
424
-
425
- // Special handling if the method returns self. If so we do not want
426
- // to create a new Ruby wrapper object and instead return self.
427
- if constexpr (std::is_same_v<intrinsic_type<Return_T>, intrinsic_type<Receiver_T>>)
428
- {
429
- if constexpr (std::is_pointer_v<Return_T> && std::is_pointer_v<Receiver_T>)
430
- {
431
- if (nativeResult == receiver)
432
- return self;
433
- }
434
- else if constexpr (std::is_pointer_v<Return_T> && std::is_reference_v<Receiver_T>)
435
- {
436
- if (nativeResult == &receiver)
437
- return self;
438
- }
439
- else if constexpr (std::is_reference_v<Return_T> && std::is_pointer_v<Receiver_T>)
440
- {
441
- if (&nativeResult == receiver)
442
- return self;
443
- }
444
- else if constexpr (std::is_reference_v<Return_T> && std::is_reference_v<Receiver_T>)
445
- {
446
- if (&nativeResult == &receiver)
447
- return self;
448
- }
449
- }
120
+ // Call the native method and get the result
121
+ Return_T nativeResult = no_gvl(this->function_, std::forward<Arg_Ts>(nativeArgs));
450
122
 
451
- return this->toRuby_.convert(std::forward<Return_T>(nativeResult));
123
+ // Return the result
124
+ return this->toRuby_.convert(nativeResult);
452
125
  }
453
126
  }
454
127
 
455
- template<typename Class_T, typename Function_T, bool IsMethod>
456
- void NativeFunction<Class_T, Function_T, IsMethod>::noWrapper(const VALUE klass, const std::string& wrapper)
128
+ template<typename Function_T>
129
+ void NativeFunction<Function_T>::noWrapper(const VALUE klass, const std::string& wrapper)
457
130
  {
458
131
  std::stringstream message;
459
132
 
@@ -468,8 +141,8 @@ namespace Rice::detail
468
141
  throw std::runtime_error(message.str());
469
142
  }
470
143
 
471
- template<typename Class_T, typename Function_T, bool IsMethod>
472
- void NativeFunction<Class_T, Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<std::optional<VALUE>>& rubyValues)
144
+ template<typename Function_T>
145
+ void NativeFunction<Function_T>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<std::optional<VALUE>>& rubyValues)
473
146
  {
474
147
  // Self will be Qnil for wrapped procs
475
148
  if (self == Qnil)
@@ -493,7 +166,7 @@ namespace Rice::detail
493
166
  }
494
167
 
495
168
  // Check return value
496
- if (this->methodInfo_->returnInfo.isKeepAlive())
169
+ if (this->methodInfo_->returnInfo()->isKeepAlive())
497
170
  {
498
171
  if (selfWrapper == nullptr)
499
172
  {
@@ -510,8 +183,8 @@ namespace Rice::detail
510
183
  }
511
184
  }
512
185
 
513
- template<typename Class_T, typename Function_T, bool IsMethod>
514
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::operator()(size_t argc, const VALUE* argv, VALUE self)
186
+ template<typename Function_T>
187
+ VALUE NativeFunction<Function_T>::operator()(size_t argc, const VALUE* argv, VALUE self)
515
188
  {
516
189
  // Get the ruby values and make sure we have the correct number
517
190
  std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(argc, argv, true);
@@ -521,15 +194,17 @@ namespace Rice::detail
521
194
  // Convert the Ruby values to native values
522
195
  Arg_Ts nativeValues = this->getNativeValues(rubyValues, indices);
523
196
 
524
- // Now call the native method
197
+ bool noGvl = this->methodInfo_->function()->isNoGvl();
198
+
525
199
  VALUE result = Qnil;
526
- if constexpr (std::is_same_v<Receiver_T, std::nullptr_t>)
200
+
201
+ if (noGvl)
527
202
  {
528
- result = this->invokeNativeFunction(std::forward<Arg_Ts>(nativeValues));
203
+ result = this->invokeNoGVL(std::forward<Arg_Ts>(nativeValues));
529
204
  }
530
205
  else
531
206
  {
532
- result = this->invokeNativeMethod(self, std::forward<Arg_Ts>(nativeValues));
207
+ result = this->invoke(std::forward<Arg_Ts>(nativeValues));
533
208
  }
534
209
 
535
210
  // Check if any function arguments or return values need to have their lifetimes tied to the receiver
@@ -537,4 +212,32 @@ namespace Rice::detail
537
212
 
538
213
  return result;
539
214
  }
215
+
216
+ template<typename Function_T>
217
+ inline std::string NativeFunction<Function_T>::name()
218
+ {
219
+ return this->method_name_;
220
+ }
221
+
222
+ template<typename Function_T>
223
+ inline NativeKind NativeFunction<Function_T>::kind()
224
+ {
225
+ return NativeKind::Function;
226
+ }
227
+
228
+ template<typename Function_T>
229
+ inline VALUE NativeFunction<Function_T>::returnKlass()
230
+ {
231
+ // Check if an array is being returned
232
+ if (this->methodInfo_->returnInfo()->isBuffer())
233
+ {
234
+ TypeMapper<Pointer<Return_T>> typeMapper;
235
+ return typeMapper.rubyKlass();
236
+ }
237
+ else
238
+ {
239
+ TypeMapper<Return_T> typeMapper;
240
+ return typeMapper.rubyKlass();
241
+ }
242
+ }
540
243
  }