rice 4.5.0 → 4.6.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 (166) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/CMakeLists.txt +31 -0
  4. data/CMakePresets.json +75 -0
  5. data/COPYING +3 -2
  6. data/FindRuby.cmake +437 -0
  7. data/Rakefile +5 -4
  8. data/include/rice/rice.hpp +5436 -3201
  9. data/include/rice/stl.hpp +2355 -1269
  10. data/lib/make_rice_headers.rb +79 -0
  11. data/lib/mkmf-rice.rb +4 -0
  12. data/lib/rice/version.rb +3 -0
  13. data/lib/rice.rb +1 -0
  14. data/lib/rubygems/builder.rb +11 -0
  15. data/lib/rubygems/cmake_builder.rb +113 -0
  16. data/lib/rubygems_plugin.rb +9 -0
  17. data/rice/Arg.hpp +7 -1
  18. data/rice/Arg.ipp +11 -2
  19. data/rice/Buffer.hpp +123 -0
  20. data/rice/Buffer.ipp +599 -0
  21. data/rice/Constructor.ipp +3 -3
  22. data/rice/Data_Object.hpp +2 -3
  23. data/rice/Data_Object.ipp +188 -188
  24. data/rice/Data_Type.hpp +4 -5
  25. data/rice/Data_Type.ipp +42 -26
  26. data/rice/Enum.hpp +0 -1
  27. data/rice/Enum.ipp +26 -23
  28. data/rice/Init.hpp +8 -0
  29. data/rice/Init.ipp +8 -0
  30. data/rice/MemoryView.ipp +1 -41
  31. data/rice/Return.hpp +1 -1
  32. data/rice/Return.ipp +6 -0
  33. data/rice/cpp_api/Array.hpp +209 -0
  34. data/rice/cpp_api/Array.ipp +304 -0
  35. data/rice/cpp_api/Builtin_Object.hpp +31 -0
  36. data/rice/cpp_api/Builtin_Object.ipp +37 -0
  37. data/rice/cpp_api/Class.hpp +70 -0
  38. data/rice/cpp_api/Class.ipp +97 -0
  39. data/rice/cpp_api/Encoding.hpp +32 -0
  40. data/rice/cpp_api/Encoding.ipp +59 -0
  41. data/rice/cpp_api/Hash.hpp +194 -0
  42. data/rice/cpp_api/Hash.ipp +257 -0
  43. data/rice/cpp_api/Identifier.hpp +46 -0
  44. data/rice/cpp_api/Identifier.ipp +31 -0
  45. data/rice/cpp_api/Module.hpp +72 -0
  46. data/rice/cpp_api/Module.ipp +101 -0
  47. data/rice/cpp_api/Object.hpp +272 -0
  48. data/rice/cpp_api/Object.ipp +235 -0
  49. data/rice/cpp_api/String.hpp +74 -0
  50. data/rice/cpp_api/String.ipp +120 -0
  51. data/rice/cpp_api/Struct.hpp +113 -0
  52. data/rice/cpp_api/Struct.ipp +92 -0
  53. data/rice/cpp_api/Symbol.hpp +46 -0
  54. data/rice/cpp_api/Symbol.ipp +93 -0
  55. data/rice/cpp_api/shared_methods.hpp +134 -0
  56. data/rice/detail/MethodInfo.hpp +1 -9
  57. data/rice/detail/MethodInfo.ipp +5 -72
  58. data/rice/detail/Native.hpp +3 -2
  59. data/rice/detail/Native.ipp +32 -4
  60. data/rice/detail/NativeAttributeGet.hpp +3 -2
  61. data/rice/detail/NativeAttributeGet.ipp +8 -2
  62. data/rice/detail/NativeAttributeSet.hpp +3 -2
  63. data/rice/detail/NativeAttributeSet.ipp +8 -2
  64. data/rice/detail/NativeCallbackFFI.ipp +1 -1
  65. data/rice/detail/NativeFunction.hpp +17 -6
  66. data/rice/detail/NativeFunction.ipp +168 -64
  67. data/rice/detail/NativeIterator.hpp +3 -2
  68. data/rice/detail/NativeIterator.ipp +8 -2
  69. data/rice/detail/RubyType.hpp +2 -5
  70. data/rice/detail/RubyType.ipp +50 -5
  71. data/rice/detail/Type.hpp +3 -1
  72. data/rice/detail/Type.ipp +61 -31
  73. data/rice/detail/Wrapper.hpp +68 -33
  74. data/rice/detail/Wrapper.ipp +103 -113
  75. data/rice/detail/from_ruby.hpp +5 -4
  76. data/rice/detail/from_ruby.ipp +737 -365
  77. data/rice/detail/to_ruby.ipp +1092 -186
  78. data/rice/global_function.ipp +1 -1
  79. data/rice/libc/file.hpp +11 -0
  80. data/rice/libc/file.ipp +32 -0
  81. data/rice/rice.hpp +23 -16
  82. data/rice/stl/complex.hpp +6 -0
  83. data/rice/stl/complex.ipp +93 -0
  84. data/rice/stl/exception.hpp +11 -0
  85. data/rice/stl/exception.ipp +29 -0
  86. data/rice/stl/exception_ptr.hpp +6 -0
  87. data/rice/stl/exception_ptr.ipp +27 -0
  88. data/rice/stl/map.hpp +12 -0
  89. data/rice/stl/map.ipp +469 -0
  90. data/rice/stl/monostate.hpp +6 -0
  91. data/rice/stl/monostate.ipp +80 -0
  92. data/rice/stl/multimap.hpp +14 -0
  93. data/rice/stl/multimap.ipp +448 -0
  94. data/rice/stl/optional.hpp +6 -0
  95. data/rice/stl/optional.ipp +118 -0
  96. data/rice/stl/pair.hpp +13 -0
  97. data/rice/stl/pair.ipp +155 -0
  98. data/rice/stl/reference_wrapper.hpp +6 -0
  99. data/rice/stl/reference_wrapper.ipp +41 -0
  100. data/rice/stl/set.hpp +12 -0
  101. data/rice/stl/set.ipp +495 -0
  102. data/rice/stl/shared_ptr.hpp +28 -0
  103. data/rice/stl/shared_ptr.ipp +224 -0
  104. data/rice/stl/string.hpp +6 -0
  105. data/rice/stl/string.ipp +158 -0
  106. data/rice/stl/string_view.hpp +6 -0
  107. data/rice/stl/string_view.ipp +65 -0
  108. data/rice/stl/tuple.hpp +6 -0
  109. data/rice/stl/tuple.ipp +128 -0
  110. data/rice/stl/type_index.hpp +6 -0
  111. data/rice/stl/type_index.ipp +30 -0
  112. data/rice/stl/type_info.hpp +6 -0
  113. data/rice/stl/type_info.ipp +29 -0
  114. data/rice/stl/unique_ptr.hpp +22 -0
  115. data/rice/stl/unique_ptr.ipp +139 -0
  116. data/rice/stl/unordered_map.hpp +12 -0
  117. data/rice/stl/unordered_map.ipp +469 -0
  118. data/rice/stl/variant.hpp +6 -0
  119. data/rice/stl/variant.ipp +242 -0
  120. data/rice/stl/vector.hpp +12 -0
  121. data/rice/stl/vector.ipp +590 -0
  122. data/rice/stl.hpp +7 -3
  123. data/rice/traits/attribute_traits.hpp +26 -0
  124. data/rice/traits/function_traits.hpp +95 -0
  125. data/rice/traits/method_traits.hpp +47 -0
  126. data/rice/traits/rice_traits.hpp +160 -0
  127. data/rice.gemspec +85 -0
  128. data/test/embed_ruby.cpp +3 -0
  129. data/test/ruby/test_multiple_extensions_same_class.rb +14 -14
  130. data/test/test_Array.cpp +6 -3
  131. data/test/test_Attribute.cpp +34 -1
  132. data/test/test_Buffer.cpp +285 -0
  133. data/test/test_Callback.cpp +2 -3
  134. data/test/test_Data_Object.cpp +88 -34
  135. data/test/test_Data_Type.cpp +106 -65
  136. data/test/test_Director.cpp +7 -3
  137. data/test/test_Enum.cpp +5 -2
  138. data/test/test_File.cpp +1 -1
  139. data/test/test_From_Ruby.cpp +181 -114
  140. data/test/test_Iterator.cpp +1 -1
  141. data/test/{test_JumpException.cpp → test_Jump_Exception.cpp} +1 -0
  142. data/test/test_Keep_Alive.cpp +7 -18
  143. data/test/test_Keep_Alive_No_Wrapper.cpp +0 -1
  144. data/test/test_Module.cpp +13 -6
  145. data/test/test_Native_Registry.cpp +0 -1
  146. data/test/test_Overloads.cpp +180 -5
  147. data/test/test_Ownership.cpp +100 -57
  148. data/test/test_Proc.cpp +0 -1
  149. data/test/test_Self.cpp +4 -4
  150. data/test/test_Stl_Map.cpp +37 -39
  151. data/test/test_Stl_Multimap.cpp +693 -0
  152. data/test/test_Stl_Pair.cpp +8 -8
  153. data/test/test_Stl_Reference_Wrapper.cpp +4 -2
  154. data/test/test_Stl_Set.cpp +790 -0
  155. data/test/{test_Stl_SmartPointer.cpp → test_Stl_SharedPtr.cpp} +97 -127
  156. data/test/test_Stl_Tuple.cpp +116 -0
  157. data/test/test_Stl_Type.cpp +1 -1
  158. data/test/test_Stl_UniquePtr.cpp +202 -0
  159. data/test/test_Stl_Unordered_Map.cpp +28 -34
  160. data/test/test_Stl_Variant.cpp +217 -89
  161. data/test/test_Stl_Vector.cpp +209 -83
  162. data/test/test_To_Ruby.cpp +373 -1
  163. data/test/test_Type.cpp +85 -14
  164. data/test/test_global_functions.cpp +17 -4
  165. metadata +94 -10
  166. data/rice/detail/TupleIterator.hpp +0 -14
@@ -21,7 +21,7 @@ namespace Rice::detail
21
21
  }
22
22
 
23
23
  template<typename Attribute_T>
24
- inline Resolved NativeAttributeGet<Attribute_T>::matches(int argc, const VALUE* argv, VALUE self)
24
+ inline Resolved NativeAttributeGet<Attribute_T>::matches(size_t argc, const VALUE* argv, VALUE self)
25
25
  {
26
26
  if (argc == 0)
27
27
  return Resolved { Convertible::Exact, 1, this };
@@ -36,7 +36,7 @@ namespace Rice::detail
36
36
  }
37
37
 
38
38
  template<typename Attribute_T>
39
- inline VALUE NativeAttributeGet<Attribute_T>::operator()(int argc, const VALUE* argv, VALUE self)
39
+ inline VALUE NativeAttributeGet<Attribute_T>::operator()(size_t argc, const VALUE* argv, VALUE self)
40
40
  {
41
41
  if constexpr (std::is_member_object_pointer_v<Attribute_T>)
42
42
  {
@@ -48,4 +48,10 @@ namespace Rice::detail
48
48
  return To_Ruby<To_Ruby_T>().convert(*attribute_);
49
49
  }
50
50
  }
51
+
52
+ template<typename Attribute_T>
53
+ inline std::string NativeAttributeGet<Attribute_T>::toString()
54
+ {
55
+ return "";
56
+ }
51
57
  } // Rice
@@ -26,8 +26,9 @@ namespace Rice
26
26
  void operator=(const NativeAttribute_T&) = delete;
27
27
  void operator=(NativeAttribute_T&&) = delete;
28
28
 
29
- Resolved matches(int argc, const VALUE* argv, VALUE self) override;
30
- VALUE operator()(int argc, const VALUE* argv, VALUE self) override;
29
+ Resolved matches(size_t argc, const VALUE* argv, VALUE self) override;
30
+ VALUE operator()(size_t argc, const VALUE* argv, VALUE self) override;
31
+ std::string toString() override;
31
32
 
32
33
  protected:
33
34
  NativeAttributeSet(VALUE klass, std::string name, Attribute_T attr);
@@ -31,7 +31,7 @@ namespace Rice::detail
31
31
  }
32
32
 
33
33
  template<typename Attribute_T>
34
- inline Resolved NativeAttributeSet<Attribute_T>::matches(int argc, const VALUE* argv, VALUE self)
34
+ inline Resolved NativeAttributeSet<Attribute_T>::matches(size_t argc, const VALUE* argv, VALUE self)
35
35
  {
36
36
  if (argc == 1)
37
37
  return Resolved{ Convertible::Exact, 1, this };
@@ -40,7 +40,7 @@ namespace Rice::detail
40
40
  }
41
41
 
42
42
  template<typename Attribute_T>
43
- inline VALUE NativeAttributeSet<Attribute_T>::operator()(int argc, const VALUE* argv, VALUE self)
43
+ inline VALUE NativeAttributeSet<Attribute_T>::operator()(size_t argc, const VALUE* argv, VALUE self)
44
44
  {
45
45
  if constexpr (std::is_fundamental_v<intrinsic_type<Attr_T>> && std::is_pointer_v<Attr_T>)
46
46
  {
@@ -79,4 +79,10 @@ namespace Rice::detail
79
79
 
80
80
  return value;
81
81
  }
82
+
83
+ template<typename Attribute_T>
84
+ inline std::string NativeAttributeSet<Attribute_T>::toString()
85
+ {
86
+ return "";
87
+ }
82
88
  } // Rice
@@ -118,7 +118,7 @@ namespace Rice::detail
118
118
 
119
119
  // Create FFI closure
120
120
  this->closure_ = (ffi_closure *)ffi_closure_alloc(sizeof(ffi_closure) + sizeof(void*), (void**)(&this->callback_));
121
- ffi_status status = ffi_prep_closure_loc(this->closure_, &cif_, ffiCallback, (void*)this, (void*)this->callback_);
121
+ ffi_prep_closure_loc(this->closure_, &cif_, ffiCallback, (void*)this, (void*)this->callback_);
122
122
  }
123
123
 
124
124
  template<typename Return_T, typename ...Arg_Ts>
@@ -64,14 +64,25 @@ namespace Rice::detail
64
64
  void operator=(const NativeFunction_T&) = delete;
65
65
  void operator=(NativeFunction_T&&) = delete;
66
66
 
67
- Resolved matches(int argc, const VALUE* argv, VALUE self) override;
68
- VALUE operator()(int argc, const VALUE* argv, VALUE self) override;
67
+ Resolved matches(size_t argc, const VALUE* argv, VALUE self) override;
68
+ VALUE operator()(size_t argc, const VALUE* argv, VALUE self) override;
69
+ std::string toString() override;
69
70
 
70
71
  NativeFunction(Function_T function);
71
72
  NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo);
73
+
72
74
  protected:
73
75
 
74
76
  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
+ template<std::size_t...I>
84
+ std::vector<std::string> argTypeNames(std::ostringstream& stream, std::index_sequence<I...>& indices);
85
+
75
86
  template<typename T, std::size_t I>
76
87
  From_Ruby<T> createFromRuby();
77
88
 
@@ -83,14 +94,14 @@ namespace Rice::detail
83
94
  To_Ruby<To_Ruby_T> createToRuby();
84
95
 
85
96
  // Convert Ruby argv pointer to Ruby values
86
- std::vector<VALUE> getRubyValues(int argc, const VALUE* argv, bool validate);
97
+ std::vector<std::optional<VALUE>> getRubyValues(size_t argc, const VALUE* argv, bool validate);
87
98
 
88
99
  template<typename Arg_T, int I>
89
- Arg_T getNativeValue(std::vector<VALUE>& values);
100
+ Arg_T getNativeValue(std::vector<std::optional<VALUE>>& values);
90
101
 
91
102
  // Convert Ruby values to C++ values
92
103
  template<typename std::size_t...I>
93
- Arg_Ts getNativeValues(std::vector<VALUE>& values, std::index_sequence<I...>& indices);
104
+ Arg_Ts getNativeValues(std::vector<std::optional<VALUE>>& values, std::index_sequence<I...>& indices);
94
105
 
95
106
  // Figure out what self is
96
107
  Receiver_T getReceiver(VALUE self);
@@ -99,7 +110,7 @@ namespace Rice::detail
99
110
  [[noreturn]] void noWrapper(const VALUE klass, const std::string& wrapper);
100
111
 
101
112
  // Do we need to keep alive any arguments?
102
- void checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues);
113
+ void checkKeepAlive(VALUE self, VALUE returnValue, std::vector<std::optional<VALUE>>& rubyValues);
103
114
 
104
115
  // Call the underlying C++ function
105
116
  VALUE invokeNativeFunction(Arg_Ts&& nativeArgs);
@@ -1,9 +1,8 @@
1
- #include <array>
2
1
  #include <algorithm>
2
+ #include <array>
3
3
  #include <stdexcept>
4
4
  #include <sstream>
5
5
 
6
-
7
6
  namespace Rice::detail
8
7
  {
9
8
  template<typename Class_T, typename Function_T, bool IsMethod>
@@ -29,8 +28,11 @@ namespace Rice::detail
29
28
  template<typename Class_T, typename Function_T, bool IsMethod>
30
29
  VALUE NativeFunction<Class_T, Function_T, IsMethod>::procEntry(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg)
31
30
  {
32
- NativeFunction_T* native = (NativeFunction_T*)callback_arg;
33
- return (*native)(argc, argv, Qnil);
31
+ return cpp_protect([&]
32
+ {
33
+ NativeFunction_T * native = (NativeFunction_T*)callback_arg;
34
+ return (*native)(argc, argv, Qnil);
35
+ });
34
36
  }
35
37
 
36
38
  // Ruby calls this method if an instance f a NativeFunction is owned by a Ruby proc. That happens when C++
@@ -43,7 +45,6 @@ namespace Rice::detail
43
45
  return Qnil;
44
46
  }
45
47
 
46
-
47
48
  template<typename Class_T, typename Function_T, bool IsMethod>
48
49
  NativeFunction<Class_T, Function_T, IsMethod>::NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
49
50
  : klass_(klass), method_name_(method_name), function_(function), methodInfo_(methodInfo)
@@ -64,6 +65,43 @@ namespace Rice::detail
64
65
  {
65
66
  }
66
67
 
68
+ template<typename Class_T, typename Function_T, bool IsMethod>
69
+ template<std::size_t... I>
70
+ std::vector<std::string> NativeFunction<Class_T, Function_T, IsMethod>::argTypeNames(std::ostringstream& stream, std::index_sequence<I...>& indices)
71
+ {
72
+ std::vector<std::string> typeNames;
73
+ (typeNames.push_back(cppClassName(typeName(typeid(typename std::tuple_element<I, Arg_Ts>::type)))), ...);
74
+ return typeNames;
75
+ }
76
+
77
+ template<typename Class_T, typename Function_T, bool IsMethod>
78
+ std::string NativeFunction<Class_T, Function_T, IsMethod>::toString()
79
+ {
80
+ std::ostringstream result;
81
+
82
+ result << cppClassName(typeName(typeid(Return_T))) << " ";
83
+
84
+ if (!std::is_null_pointer_v<Receiver_T>)
85
+ {
86
+ result << cppClassName(typeName(typeid(Receiver_T))) << "::";
87
+ }
88
+
89
+ result << this->method_name_;
90
+
91
+ result << "(";
92
+
93
+ auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
94
+ std::vector<std::string> argTypeNames = this->argTypeNames(result, indices);
95
+ for (size_t i = 0; i < argTypeNames.size(); i++)
96
+ {
97
+ result << argTypeNames[i];
98
+ if (i < argTypeNames.size() - 1)
99
+ result << ", ";
100
+ }
101
+ result << ")";
102
+ return result.str();
103
+ }
104
+
67
105
  template<typename Class_T, typename Function_T, bool IsMethod>
68
106
  To_Ruby<typename NativeFunction<Class_T, Function_T, IsMethod>::To_Ruby_T> NativeFunction<Class_T, Function_T, IsMethod>::createToRuby()
69
107
  {
@@ -101,60 +139,100 @@ namespace Rice::detail
101
139
  }
102
140
 
103
141
  template<typename Class_T, typename Function_T, bool IsMethod>
104
- Resolved NativeFunction<Class_T, Function_T, IsMethod>::matches(int argc, const VALUE* argv, VALUE self)
142
+ template<int I>
143
+ Convertible NativeFunction<Class_T, Function_T, IsMethod>::matchParameter(std::vector<std::optional<VALUE>>& values)
105
144
  {
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
-
145
+ Convertible result = Convertible::None;
112
146
  MethodInfo* methodInfo = this->methodInfo_.get();
113
- int index = 0;
114
-
115
- std::vector<VALUE> rubyValues = this->getRubyValues(argc, argv, false);
147
+ const Arg* arg = methodInfo->arg(I);
148
+ std::optional<VALUE> value = values[I];
116
149
 
117
- // Loop over each FromRuby instance
118
- for_each_tuple(this->fromRubys_,
119
- [&](auto& fromRuby)
150
+ // Is a VALUE being passed directly to C++ ?
151
+ if (value.has_value())
152
+ {
153
+ if (arg->isValue())
120
154
  {
121
- Convertible convertible = Convertible::None;
122
-
123
- const Arg* arg = methodInfo->arg(index);
155
+ result = Convertible::Exact;
156
+ }
157
+ // If index is less than argc then check with FromRuby if the VALUE is convertible
158
+ // to C++.
159
+ else
160
+ {
161
+ VALUE value = values[I].value();
162
+ auto fromRuby = std::get<I>(this->fromRubys_);
163
+ result = fromRuby.is_convertible(value);
124
164
 
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())
165
+ // If this is an exact match check if the const-ness of the value and the parameter match
166
+ if (result == Convertible::Exact && rb_type(value) == RUBY_T_DATA)
139
167
  {
140
- convertible = Convertible::Exact;
168
+ // Check the constness of the Ruby wrapped value and the parameter
169
+ WrapperBase* wrapper = getWrapper(value);
170
+ using Parameter_T = std::tuple_element_t<I, Arg_Ts>;
171
+
172
+ // Do not send a const value to a non-const parameter
173
+ if (wrapper->isConst() && !is_const_any_v<Parameter_T>)
174
+ {
175
+ result = Convertible::None;
176
+ }
177
+ // It is ok to send a non-const value to a const parameter but
178
+ // prefer non-const to non-const by slighly decreasing the convertible value
179
+ else if (!wrapper->isConst() && is_const_any_v<Parameter_T>)
180
+ {
181
+ result = Convertible::Const;
182
+ }
141
183
  }
184
+ }
185
+ }
186
+ // Last check if a default value has been set
187
+ else if (arg->hasDefaultValue())
188
+ {
189
+ result = Convertible::Exact;
190
+ }
191
+
192
+ return result;
193
+ }
194
+
195
+ template<typename Class_T, typename Function_T, bool IsMethod>
196
+ template<std::size_t... I>
197
+ Convertible NativeFunction<Class_T, Function_T, IsMethod>::matchParameters(std::vector<std::optional<VALUE>>& values,
198
+ std::index_sequence<I...>& indices)
199
+ {
200
+ Convertible result = Convertible::Exact;
201
+ ((result = result & this->matchParameter<I>(values)), ...);
202
+ return result;
203
+ }
142
204
 
143
- result.convertible = result.convertible & convertible;
205
+ template<typename Class_T, typename Function_T, bool IsMethod>
206
+ Resolved NativeFunction<Class_T, Function_T, IsMethod>::matches(size_t argc, const VALUE* argv, VALUE self)
207
+ {
208
+ // Return false if Ruby provided more arguments than the C++ method takes
209
+ if (argc > arity)
210
+ return Resolved{ Convertible::None, 0, this };
144
211
 
145
- index++;
146
- });
212
+ Resolved result { Convertible::Exact, 1, this };
213
+
214
+ std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(argc, argv, false);
215
+ auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
216
+ result.convertible = this->matchParameters(rubyValues, indices);
147
217
 
148
218
  if constexpr (arity > 0)
149
- result.parameterMatch = rubyValues.size() / (double)arity;
219
+ {
220
+ int providedValues = std::count_if(rubyValues.begin(), rubyValues.end(), [](std::optional<VALUE>& value)
221
+ {
222
+ return value.has_value();
223
+ });
150
224
 
225
+ result.parameterMatch = providedValues / (double)arity;
226
+ }
151
227
  return result;
152
228
  }
153
229
 
154
230
  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)
231
+ std::vector<std::optional<VALUE>> NativeFunction<Class_T, Function_T, IsMethod>::getRubyValues(size_t argc, const VALUE* argv, bool validate)
156
232
  {
157
- std::vector<VALUE> result;
233
+ #undef max
234
+ int size = std::max((size_t)arity, (size_t)argc);
235
+ std::vector<std::optional<VALUE>> result(size);
158
236
 
159
237
  // Keyword handling
160
238
  if (rb_keyword_given_p())
@@ -165,9 +243,7 @@ namespace Rice::detail
165
243
  VALUE value = argv[actualArgc];
166
244
  Hash keywords(value);
167
245
 
168
- result.resize(actualArgc + keywords.size());
169
-
170
- // Copy over leading arguments
246
+ // Copy over leading non-keyword arguments
171
247
  for (int i = 0; i < actualArgc; i++)
172
248
  {
173
249
  result[i] = argv[i];
@@ -187,20 +263,39 @@ namespace Rice::detail
187
263
  }
188
264
  else
189
265
  {
190
- std::copy(argv, argv + argc, std::back_inserter(result));
266
+ std::copy(argv, argv + argc, result.begin());
191
267
  }
192
268
 
193
269
  // Block handling. If we find a block and the last parameter is missing then
194
270
  // set it to the block
195
- if (rb_block_given_p() && result.size() < std::tuple_size_v<Arg_Ts>)
271
+ if (rb_block_given_p() && result.size() > 0 && !result.back().has_value())
196
272
  {
197
273
  VALUE proc = rb_block_proc();
198
- result.push_back(proc);
274
+ result.back() = proc;
199
275
  }
200
276
 
201
277
  if (validate)
202
278
  {
203
- this->methodInfo_->verifyArgCount(result.size());
279
+ // Protect against user sending too many arguments
280
+ if (argc > arity)
281
+ {
282
+ std::string message = "wrong number of arguments (given " +
283
+ std::to_string(argc) + ", expected " + std::to_string(arity) + ")";
284
+ throw std::invalid_argument(message);
285
+ }
286
+
287
+ for (size_t i=0; i<result.size(); i++)
288
+ {
289
+ std::optional<VALUE> value = result[i];
290
+ Arg* arg = this->methodInfo_->arg(i);
291
+
292
+ if (!arg->hasDefaultValue() && !value.has_value())
293
+ {
294
+ std::string message;
295
+ message = "Missing argument. Name: " + arg->name + ". Index: " + std::to_string(arg->position) + ".";
296
+ throw std::invalid_argument(message);
297
+ }
298
+ }
204
299
  }
205
300
 
206
301
  return result;
@@ -208,7 +303,7 @@ namespace Rice::detail
208
303
 
209
304
  template<typename Class_T, typename Function_T, bool IsMethod>
210
305
  template<typename Arg_T, int I>
211
- Arg_T NativeFunction<Class_T, Function_T, IsMethod>::getNativeValue(std::vector<VALUE>& values)
306
+ Arg_T NativeFunction<Class_T, Function_T, IsMethod>::getNativeValue(std::vector<std::optional<VALUE>>& values)
212
307
  {
213
308
  /* In general the compiler will convert T to const T, but that does not work for converting
214
309
  T** to const T** (see see https://isocpp.org/wiki/faq/const-correctness#constptrptr-conversion)
@@ -218,22 +313,31 @@ namespace Rice::detail
218
313
  the return type. That works but requires a lot more code changes for this one case and is not
219
314
  backwards compatible. */
220
315
 
221
- // If the user did provide a value assume Qnil
222
- VALUE value = I < values.size() ? values[I] : Qnil;
316
+ std::optional<VALUE> value = values[I];
317
+ Arg* arg = this->methodInfo_->arg(I);
223
318
 
224
319
  if constexpr (is_pointer_pointer_v<Arg_T> && !std::is_convertible_v<remove_cv_recursive_t<Arg_T>, Arg_T>)
225
320
  {
226
- return (Arg_T)std::get<I>(this->fromRubys_).convert(value);
321
+ return (Arg_T)std::get<I>(this->fromRubys_).convert(value.value());
227
322
  }
228
- else
323
+ else if (value.has_value())
229
324
  {
230
- return std::get<I>(this->fromRubys_).convert(value);
325
+ return std::get<I>(this->fromRubys_).convert(value.value());
231
326
  }
327
+ else if constexpr (std::is_constructible_v<std::remove_cv_t<Arg_T>, std::remove_cv_t<std::remove_reference_t<Arg_T>>&>)
328
+ {
329
+ if (arg->hasDefaultValue())
330
+ {
331
+ return arg->defaultValue<Arg_T>();
332
+ }
333
+ }
334
+
335
+ throw std::invalid_argument("Could not convert Rubyy value");
232
336
  }
233
337
 
234
338
  template<typename Class_T, typename Function_T, bool IsMethod>
235
339
  template<std::size_t... I>
236
- typename NativeFunction<Class_T, Function_T, IsMethod>::Arg_Ts NativeFunction<Class_T, Function_T, IsMethod>::getNativeValues(std::vector<VALUE>& values,
340
+ typename NativeFunction<Class_T, Function_T, IsMethod>::Arg_Ts NativeFunction<Class_T, Function_T, IsMethod>::getNativeValues(std::vector<std::optional<VALUE>>& values,
237
341
  std::index_sequence<I...>& indices)
238
342
  {
239
343
  /* Loop over each value returned from Ruby and convert it to the appropriate C++ type based
@@ -298,7 +402,7 @@ namespace Rice::detail
298
402
  Return_T nativeResult = std::apply(this->function_, std::forward<Arg_Ts>(nativeArgs));
299
403
 
300
404
  // Return the result
301
- return this->toRuby_.convert(nativeResult);
405
+ return this->toRuby_.convert(std::forward<Return_T>(nativeResult));
302
406
  }
303
407
  }
304
408
 
@@ -343,7 +447,7 @@ namespace Rice::detail
343
447
  }
344
448
  }
345
449
 
346
- return this->toRuby_.convert(nativeResult);
450
+ return this->toRuby_.convert(std::forward<Return_T>(nativeResult));
347
451
  }
348
452
  }
349
453
 
@@ -364,7 +468,7 @@ namespace Rice::detail
364
468
  }
365
469
 
366
470
  template<typename Class_T, typename Function_T, bool IsMethod>
367
- void NativeFunction<Class_T, Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues)
471
+ void NativeFunction<Class_T, Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<std::optional<VALUE>>& rubyValues)
368
472
  {
369
473
  // Self will be Qnil for wrapped procs
370
474
  if (self == Qnil)
@@ -372,7 +476,7 @@ namespace Rice::detail
372
476
 
373
477
  // selfWrapper will be nullptr if this(self) is a builtin type and not an external(wrapped) type
374
478
  // it is highly unlikely that keepAlive is used in this case but we check anyway
375
- Wrapper* selfWrapper = getWrapper(self);
479
+ WrapperBase* selfWrapper = getWrapper(self);
376
480
 
377
481
  // Check function arguments
378
482
  for (const Arg& arg : (*this->methodInfo_))
@@ -383,7 +487,7 @@ namespace Rice::detail
383
487
  {
384
488
  noWrapper(self, "self");
385
489
  }
386
- selfWrapper->addKeepAlive(rubyValues[arg.position]);
490
+ selfWrapper->addKeepAlive(rubyValues[arg.position].value());
387
491
  }
388
492
  }
389
493
 
@@ -396,7 +500,7 @@ namespace Rice::detail
396
500
  }
397
501
 
398
502
  // returnWrapper will be nullptr if returnValue is a built-in type and not an external(wrapped) type
399
- Wrapper* returnWrapper = getWrapper(returnValue);
503
+ WrapperBase* returnWrapper = getWrapper(returnValue);
400
504
  if (returnWrapper == nullptr)
401
505
  {
402
506
  noWrapper(returnValue, "return");
@@ -406,10 +510,10 @@ namespace Rice::detail
406
510
  }
407
511
 
408
512
  template<typename Class_T, typename Function_T, bool IsMethod>
409
- VALUE NativeFunction<Class_T, Function_T, IsMethod>::operator()(int argc, const VALUE* argv, VALUE self)
513
+ VALUE NativeFunction<Class_T, Function_T, IsMethod>::operator()(size_t argc, const VALUE* argv, VALUE self)
410
514
  {
411
515
  // Get the ruby values and make sure we have the correct number
412
- std::vector<VALUE> rubyValues = this->getRubyValues(argc, argv, true);
516
+ std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(argc, argv, true);
413
517
 
414
518
  auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
415
519
 
@@ -26,8 +26,9 @@ namespace Rice::detail
26
26
  void operator=(const NativeIterator_T&) = delete;
27
27
  void operator=(NativeIterator_T&&) = delete;
28
28
 
29
- Resolved matches(int argc, const VALUE* argv, VALUE self) override;
30
- VALUE operator()(int argc, const VALUE* argv, VALUE self) override;
29
+ Resolved matches(size_t argc, const VALUE* argv, VALUE self) override;
30
+ VALUE operator()(size_t argc, const VALUE* argv, VALUE self) override;
31
+ std::string toString() override;
31
32
 
32
33
  protected:
33
34
  NativeIterator(VALUE klass, std::string method_name, Iterator_Func_T begin, Iterator_Func_T end);
@@ -26,7 +26,7 @@ namespace Rice::detail
26
26
  }
27
27
 
28
28
  template<typename T, typename Iterator_Func_T>
29
- inline Resolved NativeIterator<T, Iterator_Func_T>::matches(int argc, const VALUE* argv, VALUE self)
29
+ inline Resolved NativeIterator<T, Iterator_Func_T>::matches(size_t argc, const VALUE* argv, VALUE self)
30
30
  {
31
31
  return Resolved{ Convertible::Exact, 1.0, this };
32
32
  }
@@ -72,7 +72,7 @@ namespace Rice::detail
72
72
  }
73
73
 
74
74
  template<typename T, typename Iterator_Func_T>
75
- inline VALUE NativeIterator<T, Iterator_Func_T>::operator()(int argc, const VALUE* argv, VALUE self)
75
+ inline VALUE NativeIterator<T, Iterator_Func_T>::operator()(size_t argc, const VALUE* argv, VALUE self)
76
76
  {
77
77
  if (!protect(rb_block_given_p))
78
78
  {
@@ -95,4 +95,10 @@ namespace Rice::detail
95
95
  return self;
96
96
  }
97
97
  }
98
+
99
+ template<typename T, typename Iterator_Func_T>
100
+ inline std::string NativeIterator<T, Iterator_Func_T>::toString()
101
+ {
102
+ return "";
103
+ }
98
104
  }
@@ -8,12 +8,9 @@ namespace Rice::detail
8
8
  template <typename T>
9
9
  class RubyType
10
10
  {
11
- public:
12
- static std::set<ruby_value_type> types();
13
- static std::set<ruby_value_type> convertibleFrom();
14
- static T(*converter)(VALUE);
15
- static std::string packTemplate;
16
11
  };
12
+
13
+ void define_ruby_types();
17
14
  }
18
15
 
19
16
  #endif // Rice__detail__ruby__type__hpp_
@@ -89,7 +89,9 @@ namespace Rice::detail
89
89
  static inline FromRuby_T fromRuby = rb_num2int_inline;
90
90
  static inline std::set<ruby_value_type> Exact = { RUBY_T_FIXNUM };
91
91
  static inline std::set<ruby_value_type> Castable = { };
92
- static inline std::set<ruby_value_type> Narrowable = { };
92
+ // We allow bignum to integer because Ruby switches to bignum at about 2 billion on 64 bit systems,
93
+ // while int can go up to 4 billion
94
+ static inline std::set<ruby_value_type> Narrowable = { RUBY_T_BIGNUM };
93
95
  static inline std::string packTemplate = "i*";
94
96
  };
95
97
 
@@ -102,7 +104,9 @@ namespace Rice::detail
102
104
  static inline FromRuby_T fromRuby = RB_NUM2UINT;
103
105
  static inline std::set<ruby_value_type> Exact = { RUBY_T_FIXNUM };
104
106
  static inline std::set<ruby_value_type> Castable = { };
105
- static inline std::set<ruby_value_type> Narrowable = { };
107
+ // We allow bignum to integer because Ruby switches to bignum at about 2 billion on 64 bit systems,
108
+ // while int can go up to 4 billion
109
+ static inline std::set<ruby_value_type> Narrowable = { RUBY_T_BIGNUM };
106
110
  static inline std::string packTemplate = "I*";
107
111
  };
108
112
 
@@ -115,7 +119,7 @@ namespace Rice::detail
115
119
  static inline FromRuby_T fromRuby = rb_num2long_inline;
116
120
  static inline std::set<ruby_value_type> Exact = { RUBY_T_FIXNUM };
117
121
  static inline std::set<ruby_value_type> Castable = { };
118
- static inline std::set<ruby_value_type> Narrowable = { };
122
+ static inline std::set<ruby_value_type> Narrowable = { RUBY_T_BIGNUM };
119
123
  static inline std::string packTemplate = "l_*";
120
124
  };
121
125
 
@@ -128,7 +132,7 @@ namespace Rice::detail
128
132
  static inline FromRuby_T fromRuby = rb_num2ulong_inline;
129
133
  static inline std::set<ruby_value_type> Exact = { RUBY_T_FIXNUM };
130
134
  static inline std::set<ruby_value_type> Castable = { };
131
- static inline std::set<ruby_value_type> Narrowable = { };
135
+ static inline std::set<ruby_value_type> Narrowable = { RUBY_T_BIGNUM};
132
136
  static inline std::string packTemplate = "L_*";
133
137
  };
134
138
 
@@ -158,7 +162,6 @@ namespace Rice::detail
158
162
  static inline std::string packTemplate = "Q_*";
159
163
  };
160
164
 
161
-
162
165
  template<>
163
166
  class RubyType<float>
164
167
  {
@@ -184,4 +187,46 @@ namespace Rice::detail
184
187
  static inline std::set<ruby_value_type> Narrowable = { };
185
188
  static inline std::string packTemplate = "d*";
186
189
  };
190
+
191
+ template<>
192
+ class RubyType<void>
193
+ {
194
+ public:
195
+ static inline std::set<ruby_value_type> Exact = { };
196
+ static inline std::set<ruby_value_type> Castable = { };
197
+ static inline std::set<ruby_value_type> Narrowable = { };
198
+ };
199
+ }
200
+
201
+ namespace Rice::detail
202
+ {
203
+ template<typename T>
204
+ inline Data_Type<T> define_ruby_type()
205
+ {
206
+ std::string name = detail::typeName(typeid(T*));
207
+ std::string klassName = detail::rubyClassName(name);
208
+ Identifier id(klassName);
209
+
210
+ Module rb_mRice = define_module("Rice");
211
+ return define_class_under<T>(rb_mRice, id);
212
+ }
213
+
214
+ inline void define_ruby_types()
215
+ {
216
+ define_ruby_type<bool>();
217
+ define_ruby_type<char>();
218
+ define_ruby_type<signed char>();
219
+ define_ruby_type<unsigned char>();
220
+ define_ruby_type<short>();
221
+ define_ruby_type<unsigned short>();
222
+ define_ruby_type<int>();
223
+ define_ruby_type<unsigned int>();
224
+ define_ruby_type<long>();
225
+ define_ruby_type<unsigned long>();
226
+ define_ruby_type<long long>();
227
+ define_ruby_type<unsigned long long>();
228
+ define_ruby_type<float>();
229
+ define_ruby_type<double>();
230
+ define_ruby_type<void>();
231
+ }
187
232
  }