rice 4.5.0 → 4.6.1

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 (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -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 +5619 -3129
  9. data/include/rice/stl.hpp +2319 -1234
  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 +168 -0
  20. data/rice/Buffer.ipp +788 -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 +7 -5
  25. data/rice/Data_Type.ipp +79 -52
  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 +20 -3
  62. data/rice/detail/NativeAttributeSet.hpp +3 -2
  63. data/rice/detail/NativeAttributeSet.ipp +11 -23
  64. data/rice/detail/NativeCallbackFFI.ipp +2 -1
  65. data/rice/detail/NativeFunction.hpp +17 -6
  66. data/rice/detail/NativeFunction.ipp +169 -64
  67. data/rice/detail/NativeIterator.hpp +3 -2
  68. data/rice/detail/NativeIterator.ipp +8 -2
  69. data/rice/detail/RubyFunction.ipp +1 -0
  70. data/rice/detail/RubyType.hpp +2 -5
  71. data/rice/detail/RubyType.ipp +50 -5
  72. data/rice/detail/Type.hpp +3 -1
  73. data/rice/detail/Type.ipp +60 -31
  74. data/rice/detail/Wrapper.hpp +68 -33
  75. data/rice/detail/Wrapper.ipp +103 -113
  76. data/rice/detail/from_ruby.hpp +5 -4
  77. data/rice/detail/from_ruby.ipp +737 -365
  78. data/rice/detail/to_ruby.ipp +1092 -186
  79. data/rice/global_function.ipp +1 -1
  80. data/rice/libc/file.hpp +11 -0
  81. data/rice/libc/file.ipp +32 -0
  82. data/rice/rice.hpp +23 -16
  83. data/rice/stl/complex.hpp +6 -0
  84. data/rice/stl/complex.ipp +93 -0
  85. data/rice/stl/exception.hpp +11 -0
  86. data/rice/stl/exception.ipp +29 -0
  87. data/rice/stl/exception_ptr.hpp +6 -0
  88. data/rice/stl/exception_ptr.ipp +27 -0
  89. data/rice/stl/map.hpp +12 -0
  90. data/rice/stl/map.ipp +469 -0
  91. data/rice/stl/monostate.hpp +6 -0
  92. data/rice/stl/monostate.ipp +80 -0
  93. data/rice/stl/multimap.hpp +14 -0
  94. data/rice/stl/multimap.ipp +448 -0
  95. data/rice/stl/optional.hpp +6 -0
  96. data/rice/stl/optional.ipp +118 -0
  97. data/rice/stl/pair.hpp +13 -0
  98. data/rice/stl/pair.ipp +155 -0
  99. data/rice/stl/reference_wrapper.hpp +6 -0
  100. data/rice/stl/reference_wrapper.ipp +41 -0
  101. data/rice/stl/set.hpp +12 -0
  102. data/rice/stl/set.ipp +495 -0
  103. data/rice/stl/shared_ptr.hpp +28 -0
  104. data/rice/stl/shared_ptr.ipp +224 -0
  105. data/rice/stl/string.hpp +6 -0
  106. data/rice/stl/string.ipp +158 -0
  107. data/rice/stl/string_view.hpp +6 -0
  108. data/rice/stl/string_view.ipp +65 -0
  109. data/rice/stl/tuple.hpp +6 -0
  110. data/rice/stl/tuple.ipp +128 -0
  111. data/rice/stl/type_index.hpp +6 -0
  112. data/rice/stl/type_index.ipp +30 -0
  113. data/rice/stl/type_info.hpp +6 -0
  114. data/rice/stl/type_info.ipp +29 -0
  115. data/rice/stl/unique_ptr.hpp +22 -0
  116. data/rice/stl/unique_ptr.ipp +139 -0
  117. data/rice/stl/unordered_map.hpp +12 -0
  118. data/rice/stl/unordered_map.ipp +469 -0
  119. data/rice/stl/variant.hpp +6 -0
  120. data/rice/stl/variant.ipp +242 -0
  121. data/rice/stl/vector.hpp +12 -0
  122. data/rice/stl/vector.ipp +589 -0
  123. data/rice/stl.hpp +7 -3
  124. data/rice/traits/attribute_traits.hpp +26 -0
  125. data/rice/traits/function_traits.hpp +95 -0
  126. data/rice/traits/method_traits.hpp +47 -0
  127. data/rice/traits/rice_traits.hpp +172 -0
  128. data/rice.gemspec +85 -0
  129. data/test/embed_ruby.cpp +3 -0
  130. data/test/ruby/test_multiple_extensions_same_class.rb +14 -14
  131. data/test/test_Array.cpp +6 -3
  132. data/test/test_Attribute.cpp +91 -9
  133. data/test/test_Buffer.cpp +340 -0
  134. data/test/test_Callback.cpp +2 -3
  135. data/test/test_Data_Object.cpp +88 -34
  136. data/test/test_Data_Type.cpp +106 -65
  137. data/test/test_Director.cpp +7 -3
  138. data/test/test_Enum.cpp +5 -2
  139. data/test/test_File.cpp +1 -1
  140. data/test/test_From_Ruby.cpp +180 -113
  141. data/test/test_Iterator.cpp +1 -1
  142. data/test/{test_JumpException.cpp → test_Jump_Exception.cpp} +1 -0
  143. data/test/test_Keep_Alive.cpp +7 -18
  144. data/test/test_Keep_Alive_No_Wrapper.cpp +0 -1
  145. data/test/test_Module.cpp +13 -6
  146. data/test/test_Native_Registry.cpp +0 -1
  147. data/test/test_Overloads.cpp +180 -5
  148. data/test/test_Ownership.cpp +100 -57
  149. data/test/test_Proc.cpp +0 -1
  150. data/test/test_Self.cpp +4 -4
  151. data/test/test_Stl_Map.cpp +37 -39
  152. data/test/test_Stl_Multimap.cpp +693 -0
  153. data/test/test_Stl_Pair.cpp +8 -8
  154. data/test/test_Stl_Reference_Wrapper.cpp +4 -2
  155. data/test/test_Stl_Set.cpp +790 -0
  156. data/test/{test_Stl_SmartPointer.cpp → test_Stl_SharedPtr.cpp} +97 -127
  157. data/test/test_Stl_Tuple.cpp +116 -0
  158. data/test/test_Stl_Type.cpp +1 -1
  159. data/test/test_Stl_UniquePtr.cpp +202 -0
  160. data/test/test_Stl_Unordered_Map.cpp +28 -34
  161. data/test/test_Stl_Variant.cpp +217 -89
  162. data/test/test_Stl_Vector.cpp +209 -83
  163. data/test/test_To_Ruby.cpp +373 -1
  164. data/test/test_Type.cpp +85 -14
  165. data/test/test_global_functions.cpp +17 -4
  166. metadata +94 -10
  167. 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,16 +36,33 @@ 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
  {
43
43
  Receiver_T* nativeSelf = From_Ruby<Receiver_T*>().convert(self);
44
- return To_Ruby<To_Ruby_T>().convert(nativeSelf->*attribute_);
44
+
45
+ if constexpr (std::is_fundamental_v<detail::intrinsic_type<To_Ruby_T>> ||
46
+ (std::is_array_v<To_Ruby_T> && std::is_fundamental_v<std::remove_extent_t<To_Ruby_T>>))
47
+ {
48
+ return To_Ruby<To_Ruby_T>().convert(nativeSelf->*attribute_);
49
+ }
50
+ else
51
+ {
52
+ // If the attribute is an object return a reference to avoid a copy (and avoid issues with
53
+ // attributes that are not assignable, copy constructible or move constructible)
54
+ return To_Ruby<To_Ruby_T&>().convert(nativeSelf->*attribute_);
55
+ }
45
56
  }
46
57
  else
47
58
  {
48
59
  return To_Ruby<To_Ruby_T>().convert(*attribute_);
49
60
  }
50
61
  }
62
+
63
+ template<typename Attribute_T>
64
+ inline std::string NativeAttributeGet<Attribute_T>::toString()
65
+ {
66
+ return "";
67
+ }
51
68
  } // 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,43 +40,31 @@ 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
- if constexpr (std::is_fundamental_v<intrinsic_type<Attr_T>> && std::is_pointer_v<Attr_T>)
46
- {
47
- static_assert(true, "An fundamental value, such as an integer, cannot be assigned to an attribute that is a pointer.");
48
- }
49
- else if constexpr (std::is_same_v<intrinsic_type<Attr_T>, std::string> && std::is_pointer_v<Attr_T>)
50
- {
51
- static_assert(true, "An string cannot be assigned to an attribute that is a pointer.");
52
- }
53
-
54
45
  if (argc != 1)
55
46
  {
56
47
  throw std::runtime_error("Incorrect number of parameters for setting attribute. Attribute: " + this->name_);
57
48
  }
58
49
 
59
50
  VALUE value = argv[0];
60
-
61
- if constexpr (!std::is_null_pointer_v<Receiver_T> &&
62
- !std::is_const_v<Attr_T> &&
63
- (std::is_fundamental_v<Attr_T> || std::is_assignable_v<Attr_T, Attr_T>))
51
+
52
+ if constexpr (!std::is_null_pointer_v<Receiver_T>)
64
53
  {
65
54
  Receiver_T* nativeSelf = From_Ruby<Receiver_T*>().convert(self);
66
55
  nativeSelf->*attribute_ = From_Ruby<T_Unqualified>().convert(value);
67
56
  }
68
- else if constexpr (std::is_null_pointer_v<Receiver_T> &&
69
- !std::is_const_v<Attr_T> &&
70
- (std::is_fundamental_v<Attr_T> || std::is_assignable_v<Attr_T, Attr_T>))
71
- {
72
- *attribute_ = From_Ruby<T_Unqualified>().convert(value);
73
- }
74
57
  else
75
58
  {
76
- // Should never get here because define_attr won't compile this code, but just in case!
77
- throw std::invalid_argument("Could not set attribute. Attribute: " + this->name_);
59
+ *attribute_ = From_Ruby<T_Unqualified>().convert(value);
78
60
  }
79
61
 
80
62
  return value;
81
63
  }
64
+
65
+ template<typename Attribute_T>
66
+ inline std::string NativeAttributeSet<Attribute_T>::toString()
67
+ {
68
+ return "";
69
+ }
82
70
  } // Rice
@@ -1,4 +1,5 @@
1
1
  #ifdef HAVE_LIBFFI
2
+ #include <tuple>
2
3
  #include <ffi.h>
3
4
 
4
5
  namespace Rice::detail
@@ -118,7 +119,7 @@ namespace Rice::detail
118
119
 
119
120
  // Create FFI closure
120
121
  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_);
122
+ ffi_prep_closure_loc(this->closure_, &cif_, ffiCallback, (void*)this, (void*)this->callback_);
122
123
  }
123
124
 
124
125
  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,8 +1,8 @@
1
- #include <array>
2
1
  #include <algorithm>
2
+ #include <array>
3
3
  #include <stdexcept>
4
4
  #include <sstream>
5
-
5
+ #include <tuple>
6
6
 
7
7
  namespace Rice::detail
8
8
  {
@@ -29,8 +29,11 @@ namespace Rice::detail
29
29
  template<typename Class_T, typename Function_T, bool IsMethod>
30
30
  VALUE NativeFunction<Class_T, Function_T, IsMethod>::procEntry(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg)
31
31
  {
32
- NativeFunction_T* native = (NativeFunction_T*)callback_arg;
33
- return (*native)(argc, argv, Qnil);
32
+ return cpp_protect([&]
33
+ {
34
+ NativeFunction_T * native = (NativeFunction_T*)callback_arg;
35
+ return (*native)(argc, argv, Qnil);
36
+ });
34
37
  }
35
38
 
36
39
  // Ruby calls this method if an instance f a NativeFunction is owned by a Ruby proc. That happens when C++
@@ -43,7 +46,6 @@ namespace Rice::detail
43
46
  return Qnil;
44
47
  }
45
48
 
46
-
47
49
  template<typename Class_T, typename Function_T, bool IsMethod>
48
50
  NativeFunction<Class_T, Function_T, IsMethod>::NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
49
51
  : klass_(klass), method_name_(method_name), function_(function), methodInfo_(methodInfo)
@@ -64,6 +66,43 @@ namespace Rice::detail
64
66
  {
65
67
  }
66
68
 
69
+ template<typename Class_T, typename Function_T, bool IsMethod>
70
+ 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)
72
+ {
73
+ std::vector<std::string> typeNames;
74
+ (typeNames.push_back(cppClassName(typeName(typeid(typename std::tuple_element<I, Arg_Ts>::type)))), ...);
75
+ return typeNames;
76
+ }
77
+
78
+ template<typename Class_T, typename Function_T, bool IsMethod>
79
+ std::string NativeFunction<Class_T, Function_T, IsMethod>::toString()
80
+ {
81
+ std::ostringstream result;
82
+
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
+
90
+ result << this->method_name_;
91
+
92
+ result << "(";
93
+
94
+ auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
95
+ std::vector<std::string> argTypeNames = this->argTypeNames(result, indices);
96
+ for (size_t i = 0; i < argTypeNames.size(); i++)
97
+ {
98
+ result << argTypeNames[i];
99
+ if (i < argTypeNames.size() - 1)
100
+ result << ", ";
101
+ }
102
+ result << ")";
103
+ return result.str();
104
+ }
105
+
67
106
  template<typename Class_T, typename Function_T, bool IsMethod>
68
107
  To_Ruby<typename NativeFunction<Class_T, Function_T, IsMethod>::To_Ruby_T> NativeFunction<Class_T, Function_T, IsMethod>::createToRuby()
69
108
  {
@@ -101,60 +140,100 @@ namespace Rice::detail
101
140
  }
102
141
 
103
142
  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)
143
+ template<int I>
144
+ Convertible NativeFunction<Class_T, Function_T, IsMethod>::matchParameter(std::vector<std::optional<VALUE>>& values)
105
145
  {
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
-
146
+ Convertible result = Convertible::None;
112
147
  MethodInfo* methodInfo = this->methodInfo_.get();
113
- int index = 0;
114
-
115
- std::vector<VALUE> rubyValues = this->getRubyValues(argc, argv, false);
148
+ const Arg* arg = methodInfo->arg(I);
149
+ std::optional<VALUE> value = values[I];
116
150
 
117
- // Loop over each FromRuby instance
118
- for_each_tuple(this->fromRubys_,
119
- [&](auto& fromRuby)
151
+ // Is a VALUE being passed directly to C++ ?
152
+ if (value.has_value())
153
+ {
154
+ if (arg->isValue())
120
155
  {
121
- Convertible convertible = Convertible::None;
122
-
123
- const Arg* arg = methodInfo->arg(index);
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);
124
165
 
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())
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)
139
168
  {
140
- convertible = Convertible::Exact;
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
+ }
141
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
+ }
142
205
 
143
- result.convertible = result.convertible & convertible;
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 };
144
212
 
145
- index++;
146
- });
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);
147
218
 
148
219
  if constexpr (arity > 0)
149
- result.parameterMatch = rubyValues.size() / (double)arity;
220
+ {
221
+ int providedValues = std::count_if(rubyValues.begin(), rubyValues.end(), [](std::optional<VALUE>& value)
222
+ {
223
+ return value.has_value();
224
+ });
150
225
 
226
+ result.parameterMatch = providedValues / (double)arity;
227
+ }
151
228
  return result;
152
229
  }
153
230
 
154
231
  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)
232
+ std::vector<std::optional<VALUE>> NativeFunction<Class_T, Function_T, IsMethod>::getRubyValues(size_t argc, const VALUE* argv, bool validate)
156
233
  {
157
- std::vector<VALUE> result;
234
+ #undef max
235
+ int size = std::max((size_t)arity, (size_t)argc);
236
+ std::vector<std::optional<VALUE>> result(size);
158
237
 
159
238
  // Keyword handling
160
239
  if (rb_keyword_given_p())
@@ -165,9 +244,7 @@ namespace Rice::detail
165
244
  VALUE value = argv[actualArgc];
166
245
  Hash keywords(value);
167
246
 
168
- result.resize(actualArgc + keywords.size());
169
-
170
- // Copy over leading arguments
247
+ // Copy over leading non-keyword arguments
171
248
  for (int i = 0; i < actualArgc; i++)
172
249
  {
173
250
  result[i] = argv[i];
@@ -187,20 +264,39 @@ namespace Rice::detail
187
264
  }
188
265
  else
189
266
  {
190
- std::copy(argv, argv + argc, std::back_inserter(result));
267
+ std::copy(argv, argv + argc, result.begin());
191
268
  }
192
269
 
193
270
  // Block handling. If we find a block and the last parameter is missing then
194
271
  // set it to the block
195
- if (rb_block_given_p() && result.size() < std::tuple_size_v<Arg_Ts>)
272
+ if (rb_block_given_p() && result.size() > 0 && !result.back().has_value())
196
273
  {
197
274
  VALUE proc = rb_block_proc();
198
- result.push_back(proc);
275
+ result.back() = proc;
199
276
  }
200
277
 
201
278
  if (validate)
202
279
  {
203
- this->methodInfo_->verifyArgCount(result.size());
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
+ }
204
300
  }
205
301
 
206
302
  return result;
@@ -208,7 +304,7 @@ namespace Rice::detail
208
304
 
209
305
  template<typename Class_T, typename Function_T, bool IsMethod>
210
306
  template<typename Arg_T, int I>
211
- Arg_T NativeFunction<Class_T, Function_T, IsMethod>::getNativeValue(std::vector<VALUE>& values)
307
+ Arg_T NativeFunction<Class_T, Function_T, IsMethod>::getNativeValue(std::vector<std::optional<VALUE>>& values)
212
308
  {
213
309
  /* In general the compiler will convert T to const T, but that does not work for converting
214
310
  T** to const T** (see see https://isocpp.org/wiki/faq/const-correctness#constptrptr-conversion)
@@ -218,22 +314,31 @@ namespace Rice::detail
218
314
  the return type. That works but requires a lot more code changes for this one case and is not
219
315
  backwards compatible. */
220
316
 
221
- // If the user did provide a value assume Qnil
222
- VALUE value = I < values.size() ? values[I] : Qnil;
317
+ std::optional<VALUE> value = values[I];
318
+ Arg* arg = this->methodInfo_->arg(I);
223
319
 
224
320
  if constexpr (is_pointer_pointer_v<Arg_T> && !std::is_convertible_v<remove_cv_recursive_t<Arg_T>, Arg_T>)
225
321
  {
226
- return (Arg_T)std::get<I>(this->fromRubys_).convert(value);
322
+ return (Arg_T)std::get<I>(this->fromRubys_).convert(value.value());
227
323
  }
228
- else
324
+ else if (value.has_value())
229
325
  {
230
- return std::get<I>(this->fromRubys_).convert(value);
326
+ return std::get<I>(this->fromRubys_).convert(value.value());
231
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");
232
337
  }
233
338
 
234
339
  template<typename Class_T, typename Function_T, bool IsMethod>
235
340
  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,
341
+ typename NativeFunction<Class_T, Function_T, IsMethod>::Arg_Ts NativeFunction<Class_T, Function_T, IsMethod>::getNativeValues(std::vector<std::optional<VALUE>>& values,
237
342
  std::index_sequence<I...>& indices)
238
343
  {
239
344
  /* Loop over each value returned from Ruby and convert it to the appropriate C++ type based
@@ -298,7 +403,7 @@ namespace Rice::detail
298
403
  Return_T nativeResult = std::apply(this->function_, std::forward<Arg_Ts>(nativeArgs));
299
404
 
300
405
  // Return the result
301
- return this->toRuby_.convert(nativeResult);
406
+ return this->toRuby_.convert(std::forward<Return_T>(nativeResult));
302
407
  }
303
408
  }
304
409
 
@@ -343,7 +448,7 @@ namespace Rice::detail
343
448
  }
344
449
  }
345
450
 
346
- return this->toRuby_.convert(nativeResult);
451
+ return this->toRuby_.convert(std::forward<Return_T>(nativeResult));
347
452
  }
348
453
  }
349
454
 
@@ -364,7 +469,7 @@ namespace Rice::detail
364
469
  }
365
470
 
366
471
  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)
472
+ void NativeFunction<Class_T, Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<std::optional<VALUE>>& rubyValues)
368
473
  {
369
474
  // Self will be Qnil for wrapped procs
370
475
  if (self == Qnil)
@@ -372,7 +477,7 @@ namespace Rice::detail
372
477
 
373
478
  // selfWrapper will be nullptr if this(self) is a builtin type and not an external(wrapped) type
374
479
  // it is highly unlikely that keepAlive is used in this case but we check anyway
375
- Wrapper* selfWrapper = getWrapper(self);
480
+ WrapperBase* selfWrapper = getWrapper(self);
376
481
 
377
482
  // Check function arguments
378
483
  for (const Arg& arg : (*this->methodInfo_))
@@ -383,7 +488,7 @@ namespace Rice::detail
383
488
  {
384
489
  noWrapper(self, "self");
385
490
  }
386
- selfWrapper->addKeepAlive(rubyValues[arg.position]);
491
+ selfWrapper->addKeepAlive(rubyValues[arg.position].value());
387
492
  }
388
493
  }
389
494
 
@@ -396,7 +501,7 @@ namespace Rice::detail
396
501
  }
397
502
 
398
503
  // returnWrapper will be nullptr if returnValue is a built-in type and not an external(wrapped) type
399
- Wrapper* returnWrapper = getWrapper(returnValue);
504
+ WrapperBase* returnWrapper = getWrapper(returnValue);
400
505
  if (returnWrapper == nullptr)
401
506
  {
402
507
  noWrapper(returnValue, "return");
@@ -406,10 +511,10 @@ namespace Rice::detail
406
511
  }
407
512
 
408
513
  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)
514
+ VALUE NativeFunction<Class_T, Function_T, IsMethod>::operator()(size_t argc, const VALUE* argv, VALUE self)
410
515
  {
411
516
  // Get the ruby values and make sure we have the correct number
412
- std::vector<VALUE> rubyValues = this->getRubyValues(argc, argv, true);
517
+ std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(argc, argv, true);
413
518
 
414
519
  auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
415
520
 
@@ -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
  }
@@ -1,5 +1,6 @@
1
1
 
2
2
  #include <any>
3
+ #include <tuple>
3
4
 
4
5
  namespace Rice::detail
5
6
  {
@@ -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_