rice 4.0.4 → 4.1.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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/Rakefile +1 -1
  4. data/include/rice/rice.hpp +2596 -1771
  5. data/include/rice/stl.hpp +1580 -271
  6. data/lib/mkmf-rice.rb +5 -2
  7. data/lib/version.rb +1 -1
  8. data/rice/Arg.hpp +6 -6
  9. data/rice/Arg.ipp +8 -9
  10. data/rice/Constructor.hpp +2 -2
  11. data/rice/Data_Object.ipp +69 -15
  12. data/rice/Data_Object_defn.hpp +1 -15
  13. data/rice/Data_Type.ipp +56 -86
  14. data/rice/Data_Type_defn.hpp +14 -17
  15. data/rice/Director.hpp +0 -1
  16. data/rice/Enum.ipp +31 -22
  17. data/rice/Exception.ipp +2 -3
  18. data/rice/Exception_defn.hpp +5 -5
  19. data/rice/HandlerRegistration.hpp +15 -0
  20. data/rice/Return.hpp +5 -4
  21. data/rice/Return.ipp +8 -3
  22. data/rice/detail/ExceptionHandler.hpp +8 -0
  23. data/rice/detail/ExceptionHandler.ipp +28 -0
  24. data/rice/detail/{Exception_Handler_defn.hpp → ExceptionHandler_defn.hpp} +17 -21
  25. data/rice/detail/HandlerRegistry.hpp +51 -0
  26. data/rice/detail/HandlerRegistry.ipp +20 -0
  27. data/rice/detail/InstanceRegistry.hpp +34 -0
  28. data/rice/detail/InstanceRegistry.ipp +50 -0
  29. data/rice/detail/MethodInfo.ipp +1 -1
  30. data/rice/detail/NativeAttribute.hpp +26 -15
  31. data/rice/detail/NativeAttribute.ipp +76 -47
  32. data/rice/detail/NativeFunction.hpp +60 -13
  33. data/rice/detail/NativeFunction.ipp +103 -85
  34. data/rice/detail/NativeIterator.hpp +49 -0
  35. data/rice/detail/NativeIterator.ipp +102 -0
  36. data/rice/detail/NativeRegistry.hpp +31 -0
  37. data/rice/detail/{method_data.ipp → NativeRegistry.ipp} +20 -16
  38. data/rice/detail/Registries.hpp +26 -0
  39. data/rice/detail/Registries.ipp +23 -0
  40. data/rice/detail/RubyFunction.hpp +6 -11
  41. data/rice/detail/RubyFunction.ipp +10 -22
  42. data/rice/detail/Type.hpp +1 -1
  43. data/rice/detail/Type.ipp +2 -2
  44. data/rice/detail/TypeRegistry.hpp +8 -11
  45. data/rice/detail/TypeRegistry.ipp +3 -28
  46. data/rice/detail/Wrapper.hpp +0 -2
  47. data/rice/detail/Wrapper.ipp +73 -23
  48. data/rice/detail/cpp_protect.hpp +93 -0
  49. data/rice/detail/default_allocation_func.ipp +1 -1
  50. data/rice/detail/from_ruby.ipp +206 -2
  51. data/rice/detail/to_ruby.ipp +39 -5
  52. data/rice/detail/to_ruby_defn.hpp +1 -1
  53. data/rice/forward_declares.ipp +6 -0
  54. data/rice/global_function.hpp +0 -4
  55. data/rice/global_function.ipp +0 -6
  56. data/rice/rice.hpp +29 -24
  57. data/rice/stl.hpp +6 -1
  58. data/test/embed_ruby.cpp +0 -15
  59. data/test/test_Array.cpp +20 -24
  60. data/test/test_Class.cpp +8 -47
  61. data/test/test_Constructor.cpp +0 -2
  62. data/test/test_Data_Object.cpp +25 -11
  63. data/test/test_Data_Type.cpp +124 -28
  64. data/test/test_Director.cpp +12 -13
  65. data/test/test_Enum.cpp +65 -26
  66. data/test/test_Inheritance.cpp +9 -9
  67. data/test/test_Iterator.cpp +134 -5
  68. data/test/test_Keep_Alive.cpp +7 -7
  69. data/test/test_Memory_Management.cpp +1 -1
  70. data/test/test_Module.cpp +25 -62
  71. data/test/test_Object.cpp +66 -3
  72. data/test/test_Ownership.cpp +12 -13
  73. data/test/test_Self.cpp +12 -13
  74. data/test/test_Stl_Map.cpp +696 -0
  75. data/test/test_Stl_Optional.cpp +3 -3
  76. data/test/test_Stl_Pair.cpp +38 -2
  77. data/test/test_Stl_Reference_Wrapper.cpp +102 -0
  78. data/test/test_Stl_SmartPointer.cpp +5 -5
  79. data/test/test_Stl_Unordered_Map.cpp +697 -0
  80. data/test/test_Stl_Variant.cpp +301 -0
  81. data/test/test_Stl_Vector.cpp +200 -41
  82. data/test/test_Struct.cpp +3 -3
  83. data/test/test_To_From_Ruby.cpp +6 -0
  84. data/test/test_Tracking.cpp +239 -0
  85. data/test/unittest.hpp +13 -4
  86. metadata +23 -13
  87. data/rice/detail/Exception_Handler.hpp +0 -8
  88. data/rice/detail/Exception_Handler.ipp +0 -28
  89. data/rice/detail/Iterator.hpp +0 -23
  90. data/rice/detail/Iterator.ipp +0 -47
  91. data/rice/detail/function_traits.hpp +0 -124
  92. data/rice/detail/method_data.hpp +0 -29
  93. data/rice/detail/rice_traits.hpp +0 -116
  94. data/rice/ruby_try_catch.hpp +0 -86
@@ -1,83 +1,112 @@
1
1
  #include <array>
2
2
  #include <algorithm>
3
3
 
4
- #include "rice_traits.hpp"
5
- #include "method_data.hpp"
4
+ #include "../traits/rice_traits.hpp"
5
+ #include "NativeRegistry.hpp"
6
6
  #include "to_ruby_defn.hpp"
7
- #include "../ruby_try_catch.hpp"
7
+ #include "cpp_protect.hpp"
8
8
 
9
9
  namespace Rice::detail
10
10
  {
11
- template<typename Return_T, typename Attr_T, typename Self_T>
12
- inline VALUE NativeAttribute<Return_T, Attr_T, Self_T>::get(VALUE self)
11
+ template<typename Attribute_T>
12
+ void NativeAttribute<Attribute_T>::define(VALUE klass, std::string name, Attribute_T attribute, AttrAccess access)
13
13
  {
14
- RUBY_TRY
14
+ // Create a NativeAttribute that Ruby will call to read/write C++ variables
15
+ NativeAttribute_T* native = new NativeAttribute_T(klass, name, std::forward<Attribute_T>(attribute), access);
16
+
17
+ if (access == AttrAccess::ReadWrite || access == AttrAccess::Read)
15
18
  {
16
- using Native_Attr_T = NativeAttribute<Return_T, Attr_T, Self_T>;
17
- Native_Attr_T* attr = detail::MethodData::data<Native_Attr_T*>();
18
- return attr->read(self);
19
+ // Tell Ruby to invoke the static method read to get the attribute value
20
+ detail::protect(rb_define_method, klass, name.c_str(), (RUBY_METHOD_FUNC)&NativeAttribute_T::get, 0);
21
+
22
+ // Add to native registry
23
+ detail::Registries::instance.natives.add(klass, Identifier(name).id(), native);
19
24
  }
20
- RUBY_CATCH
25
+
26
+ if (access == AttrAccess::ReadWrite || access == AttrAccess::Write)
27
+ {
28
+ if (std::is_const_v<std::remove_pointer_t<T>>)
29
+ {
30
+ throw std::runtime_error(name + " is readonly");
31
+ }
32
+
33
+ // Define the write method name
34
+ std::string setter = name + "=";
35
+
36
+ // Tell Ruby to invoke the static method write to get the attribute value
37
+ detail::protect(rb_define_method, klass, setter.c_str(), (RUBY_METHOD_FUNC)&NativeAttribute_T::set, 1);
38
+
39
+ // Add to native registry
40
+ detail::Registries::instance.natives.add(klass, Identifier(setter).id(), native);
41
+ }
42
+ }
43
+
44
+ template<typename Attribute_T>
45
+ inline VALUE NativeAttribute<Attribute_T>::get(VALUE self)
46
+ {
47
+ return cpp_protect([&]
48
+ {
49
+ using Native_Attr_T = NativeAttribute<Attribute_T>;
50
+ Native_Attr_T* attr = detail::Registries::instance.natives.lookup<Native_Attr_T*>();
51
+ return attr->read(self);
52
+ });
21
53
  }
22
54
 
23
- template<typename Return_T, typename Attr_T, typename Self_T>
24
- inline VALUE NativeAttribute<Return_T, Attr_T, Self_T>::set(VALUE self, VALUE value)
55
+ template<typename Attribute_T>
56
+ inline VALUE NativeAttribute<Attribute_T>::set(VALUE self, VALUE value)
25
57
  {
26
- RUBY_TRY
58
+ return cpp_protect([&]
27
59
  {
28
- using Native_Attr_T = NativeAttribute<Return_T, Attr_T, Self_T>;
29
- Native_Attr_T* attr = detail::MethodData::data<Native_Attr_T*>();
60
+ using Native_Attr_T = NativeAttribute<Attribute_T>;
61
+ Native_Attr_T* attr = detail::Registries::instance.natives.lookup<Native_Attr_T*>();
30
62
  return attr->write(self, value);
31
- }
32
- RUBY_CATCH
63
+ });
33
64
  }
34
65
 
35
- template<typename Return_T, typename Attr_T, typename Self_T>
36
- NativeAttribute<Return_T, Attr_T, Self_T>::NativeAttribute(Attr_T attr, AttrAccess access)
37
- : attr_(attr), access_(access)
66
+ template<typename Attribute_T>
67
+ NativeAttribute<Attribute_T>::NativeAttribute(VALUE klass, std::string name,
68
+ Attribute_T attribute, AttrAccess access)
69
+ : klass_(klass), name_(name), attribute_(attribute), access_(access)
38
70
  {
39
71
  }
40
72
 
41
- template<typename Return_T, typename Attr_T, typename Self_T>
42
- inline VALUE NativeAttribute<Return_T, Attr_T, Self_T>::read(VALUE self)
73
+ template<typename Attribute_T>
74
+ inline VALUE NativeAttribute<Attribute_T>::read(VALUE self)
43
75
  {
44
- using Unqualified_T = remove_cv_recursive_t<Return_T>;
45
- if constexpr (std::is_member_object_pointer_v<Attr_T>)
76
+ using T_Unqualified = remove_cv_recursive_t<T>;
77
+ if constexpr (std::is_member_object_pointer_v<Attribute_T>)
46
78
  {
47
- Self_T* nativeSelf = From_Ruby<Self_T*>().convert(self);
48
- return To_Ruby<Unqualified_T>().convert(nativeSelf->*attr_);
79
+ Receiver_T* nativeSelf = From_Ruby<Receiver_T*>().convert(self);
80
+ return To_Ruby<T_Unqualified>().convert(nativeSelf->*attribute_);
49
81
  }
50
82
  else
51
83
  {
52
- return To_Ruby<Unqualified_T>().convert(*attr_);
84
+ return To_Ruby<T_Unqualified>().convert(*attribute_);
53
85
  }
54
86
  }
55
87
 
56
- template<typename Return_T, typename Attr_T, typename Self_T>
57
- inline VALUE NativeAttribute<Return_T, Attr_T, Self_T>::write(VALUE self, VALUE value)
88
+ template<typename Attribute_T>
89
+ inline VALUE NativeAttribute<Attribute_T>::write(VALUE self, VALUE value)
58
90
  {
59
- if constexpr (!std::is_const_v<std::remove_pointer_t<Attr_T>> && std::is_member_object_pointer_v<Attr_T>)
91
+ if constexpr (std::is_fundamental_v<intrinsic_type<T>> && std::is_pointer_v<T>)
60
92
  {
61
- Self_T* nativeSelf = From_Ruby<Self_T*>().convert(self);
62
- nativeSelf->*attr_ = From_Ruby<Return_T>().convert(value);
93
+ static_assert(true, "An fundamental value, such as an integer, cannot be assigned to an attribute that is a pointer");
63
94
  }
64
- else if constexpr (!std::is_const_v<std::remove_pointer_t<Attr_T>>)
95
+ else if constexpr (std::is_same_v<intrinsic_type<T>, std::string> && std::is_pointer_v<T>)
65
96
  {
66
- *attr_ = From_Ruby<Return_T>().convert(value);
97
+ static_assert(true, "An string cannot be assigned to an attribute that is a pointer");
98
+ }
99
+
100
+ if constexpr (!std::is_null_pointer_v<Receiver_T>)
101
+ {
102
+ Receiver_T* nativeSelf = From_Ruby<Receiver_T*>().convert(self);
103
+ nativeSelf->*attribute_ = From_Ruby<T_Unqualified>().convert(value);
104
+ }
105
+ else if constexpr (!std::is_const_v<std::remove_pointer_t<T>>)
106
+ {
107
+ *attribute_ = From_Ruby<T_Unqualified>().convert(value);
67
108
  }
68
- return value;
69
- }
70
-
71
- template<typename T>
72
- auto* Make_Native_Attribute(T* attr, AttrAccess access)
73
- {
74
- return new NativeAttribute<T, T*>(attr, access);
75
- }
76
109
 
77
- template<typename Class_T, typename T>
78
- auto* Make_Native_Attribute(T Class_T::* attr, AttrAccess access)
79
- {
80
- using Attr_T = T Class_T::*;
81
- return new NativeAttribute<T, Attr_T, Class_T>(attr, access);
110
+ return value;
82
111
  }
83
112
  } // Rice
@@ -2,40 +2,86 @@
2
2
  #define Rice__detail__Native_Function__hpp_
3
3
 
4
4
  #include "ruby.hpp"
5
- #include "Exception_Handler_defn.hpp"
5
+ #include "ExceptionHandler_defn.hpp"
6
6
  #include "MethodInfo.hpp"
7
- #include "function_traits.hpp"
7
+ #include "../traits/function_traits.hpp"
8
+ #include "../traits/method_traits.hpp"
8
9
  #include "from_ruby.hpp"
9
10
 
10
11
  namespace Rice::detail
11
12
  {
12
- template<typename Function_T, bool IsMethod>
13
+ //! The NativeFunction class calls C++ functions/methods/lambdas on behalf of Ruby
14
+ /*! The NativeFunction class is an intermediate between Ruby and C++. Every method
15
+ * defined in Rice is associated with a NativeFuntion instance that is stored in
16
+ * a unordered_map maintained by the MethodData class. The key is the Ruby class
17
+ * and method.
18
+ *
19
+ * When Ruby calls into C++ it invokes the static NativeFunction.call method. This
20
+ * method then looks up the NativeFunction instance and calls its ->() operator.
21
+ *
22
+ * The instance then converts each of the arguments passed from Ruby into their
23
+ * C++ equivalents. It then retrieves the C++ object (if there is one, Ruby could
24
+ * be calling a free standing method or lambda). Then it calls the C++ method
25
+ * and gets back the result. If there is a result (so not void), it is converted
26
+ * from a C++ object to a Ruby object and returned back to Ruby.
27
+ *
28
+ * This class make heavy use of C++ Template metaprogramming to determine
29
+ * the types and parameters a method takes. It then uses that information
30
+ * to perform type conversion Ruby to C++.
31
+ *
32
+ * @tparam From_Ruby_T - The type of C++ class wrapped by Ruby. Note
33
+ * this may be different than the Class of Function_T. For example,
34
+ * std::map has a size() method but that is actually implemented on
35
+ * an ancestor class _Tree. Thus From_Ruby_T is std::map but
36
+ * Function_T::Class_T is _Tree. This typename must be specified
37
+ * by the calling code.
38
+ * @tparam Function_T - A template that represents the C++ function
39
+ * to call. This typename is automatically deduced by the compiler.
40
+ * @tparam IsMethod - A boolean specifying whether the function has
41
+ * a self parameter or not. Rice differentiates these two cases by
42
+ * calling them methods (self) or functions (no self).
43
+ */
44
+
45
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
13
46
  class NativeFunction
14
47
  {
15
48
  public:
49
+ using NativeFunction_T = NativeFunction<From_Ruby_T, Function_T, IsMethod>;
50
+
16
51
  // We remove const to avoid an explosion of To_Ruby specializations and Ruby doesn't
17
52
  // have the concept of constants anyways
18
53
  using Return_T = remove_cv_recursive_t<typename function_traits<Function_T>::return_type>;
19
- using Self_T = typename method_traits<Function_T, IsMethod>::Self_T;
54
+ using Class_T = typename method_traits<Function_T, IsMethod>::Class_T;
20
55
  using Arg_Ts = typename method_traits<Function_T, IsMethod>::Arg_Ts;
21
- using From_Ruby_Ts = typename tuple_map<From_Ruby, Arg_Ts>::type;
56
+ using From_Ruby_Args_Ts = typename tuple_map<From_Ruby, Arg_Ts>::type;
57
+
58
+ // Register function with Ruby
59
+ static void define(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo);
22
60
 
23
61
  // Static member function that Ruby calls
24
62
  static VALUE call(int argc, VALUE* argv, VALUE self);
25
63
 
26
64
  public:
27
- NativeFunction(Function_T func, std::shared_ptr<Exception_Handler> handler, MethodInfo* methodInfo);
65
+ // Disallow creating/copying/moving
66
+ NativeFunction() = delete;
67
+ NativeFunction(const NativeFunction_T&) = delete;
68
+ NativeFunction(NativeFunction_T&&) = delete;
69
+ void operator=(const NativeFunction_T&) = delete;
70
+ void operator=(NativeFunction_T&&) = delete;
28
71
 
29
72
  // Invokes the wrapped function
30
73
  VALUE operator()(int argc, VALUE* argv, VALUE self);
31
74
 
75
+ protected:
76
+ NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo);
77
+
32
78
  private:
33
79
  template<typename T, std::size_t I>
34
80
  From_Ruby<T> createFromRuby();
35
81
 
36
82
  // Create NativeArgs which are used to convert values from Ruby to C++
37
83
  template<std::size_t...I>
38
- From_Ruby_Ts createFromRuby(std::index_sequence<I...>& indices);
84
+ From_Ruby_Args_Ts createFromRuby(std::index_sequence<I...>& indices);
39
85
 
40
86
  To_Ruby<Return_T> createToRuby();
41
87
 
@@ -47,20 +93,21 @@ namespace Rice::detail
47
93
  Arg_Ts getNativeValues(std::vector<VALUE>& values, std::index_sequence<I...>& indices);
48
94
 
49
95
  // Figure out what self is
50
- Self_T getSelf(VALUE self);
96
+ Class_T getReceiver(VALUE self);
51
97
 
52
98
  // Do we need to keep alive any arguments?
53
99
  void checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues);
54
100
 
55
101
  // Call the underlying C++ function
56
- VALUE invokeNativeFunction(Arg_Ts& nativeArgs);
57
- VALUE invokeNativeMethod(VALUE self, Arg_Ts& nativeArgs);
102
+ VALUE invokeNativeFunction(const Arg_Ts& nativeArgs);
103
+ VALUE invokeNativeMethod(VALUE self, const Arg_Ts& nativeArgs);
58
104
 
59
105
  private:
60
- Function_T func_;
61
- From_Ruby_Ts fromRubys_;
106
+ VALUE klass_;
107
+ std::string method_name_;
108
+ Function_T function_;
109
+ From_Ruby_Args_Ts fromRubys_;
62
110
  To_Ruby<Return_T> toRuby_;
63
- std::shared_ptr<Exception_Handler> handler_;
64
111
  std::unique_ptr<MethodInfo> methodInfo_;
65
112
  };
66
113
  }
@@ -2,25 +2,43 @@
2
2
  #include <algorithm>
3
3
  #include <stdexcept>
4
4
 
5
- #include "method_data.hpp"
5
+ #include "cpp_protect.hpp"
6
6
  #include "to_ruby_defn.hpp"
7
- #include "../ruby_try_catch.hpp"
7
+ #include "NativeRegistry.hpp"
8
8
 
9
9
  namespace Rice::detail
10
10
  {
11
- template<typename Function_T, bool IsMethod>
12
- VALUE NativeFunction<Function_T, IsMethod>::call(int argc, VALUE* argv, VALUE self)
11
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
12
+ void NativeFunction<From_Ruby_T, Function_T, IsMethod>::define(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
13
13
  {
14
- using Wrapper_T = NativeFunction<Function_T, IsMethod>;
15
- Wrapper_T* wrapper = detail::MethodData::data<Wrapper_T*>();
16
- return wrapper->operator()(argc, argv, self);
14
+ // Tell Ruby to invoke the static method call on this class
15
+ detail::protect(rb_define_method, klass, method_name.c_str(), (RUBY_METHOD_FUNC)&NativeFunction_T::call, -1);
16
+
17
+ // Now create a NativeFunction instance and save it to the natives registry keyed on
18
+ // Ruby klass and method id. There may be multiple NativeFunction instances
19
+ // because the same C++ method could be mapped to multiple Ruby methods.
20
+ NativeFunction_T* native = new NativeFunction_T(klass, method_name, std::forward<Function_T>(function), methodInfo);
21
+ detail::Registries::instance.natives.add(klass, Identifier(method_name).id(), native);
22
+ }
23
+
24
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
25
+ VALUE NativeFunction<From_Ruby_T, Function_T, IsMethod>::call(int argc, VALUE* argv, VALUE self)
26
+ {
27
+ // Look up the native function based on the Ruby klass and method id
28
+ NativeFunction_T* nativeFunction = detail::Registries::instance.natives.lookup<NativeFunction_T*>();
29
+
30
+ // Execute the function but make sure to catch any C++ exceptions!
31
+ return cpp_protect([&]
32
+ {
33
+ return nativeFunction->operator()(argc, argv, self);
34
+ });
17
35
  }
18
36
 
19
- template<typename Function_T, bool IsMethod>
20
- NativeFunction<Function_T, IsMethod>::NativeFunction(Function_T func, std::shared_ptr<Exception_Handler> handler, MethodInfo* methodInfo)
21
- : func_(func), handler_(handler), methodInfo_(methodInfo)
37
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
38
+ NativeFunction<From_Ruby_T, Function_T, IsMethod>::NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
39
+ : klass_(klass), method_name_(method_name), function_(function), methodInfo_(methodInfo)
22
40
  {
23
- // Create a tuple of NativeArgs that will convert the Ruby values to native values. For
41
+ // Create a tuple of NativeArgs that will convert the Ruby values to native values. For
24
42
  // builtin types NativeArgs will keep a copy of the native value so that it
25
43
  // can be passed by reference or pointer to the native function. For non-builtin types
26
44
  // it will just pass the value through.
@@ -30,9 +48,9 @@ namespace Rice::detail
30
48
  this->toRuby_ = this->createToRuby();
31
49
  }
32
50
 
33
- template<typename Function_T, bool IsMethod>
51
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
34
52
  template<typename T, std::size_t I>
35
- From_Ruby<T> NativeFunction<Function_T, IsMethod>::createFromRuby()
53
+ From_Ruby<T> NativeFunction<From_Ruby_T, Function_T, IsMethod>::createFromRuby()
36
54
  {
37
55
  // Does the From_Ruby instantiation work with Arg?
38
56
  if constexpr (std::is_constructible_v<From_Ruby<T>, Arg*>)
@@ -45,8 +63,8 @@ namespace Rice::detail
45
63
  }
46
64
  }
47
65
 
48
- template<typename Function_T, bool IsMethod>
49
- To_Ruby<typename NativeFunction<Function_T, IsMethod>::Return_T> NativeFunction<Function_T, IsMethod>::createToRuby()
66
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
67
+ To_Ruby<typename NativeFunction<From_Ruby_T, Function_T, IsMethod>::Return_T> NativeFunction<From_Ruby_T, Function_T, IsMethod>::createToRuby()
50
68
  {
51
69
  // Does the From_Ruby instantiation work with ReturnInfo?
52
70
  if constexpr (std::is_constructible_v<To_Ruby<Return_T>, Return*>)
@@ -59,41 +77,41 @@ namespace Rice::detail
59
77
  }
60
78
  }
61
79
 
62
- template<typename Function_T, bool IsMethod>
80
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
63
81
  template<std::size_t... I>
64
- typename NativeFunction<Function_T, IsMethod>::From_Ruby_Ts NativeFunction<Function_T, IsMethod>::createFromRuby(std::index_sequence<I...>& indices)
82
+ typename NativeFunction<From_Ruby_T, Function_T, IsMethod>::From_Ruby_Args_Ts NativeFunction<From_Ruby_T, Function_T, IsMethod>::createFromRuby(std::index_sequence<I...>& indices)
65
83
  {
66
84
  return std::make_tuple(createFromRuby<remove_cv_recursive_t<typename std::tuple_element<I, Arg_Ts>::type>, I>()...);
67
85
  }
68
86
 
69
- template<typename Function_T, bool IsMethod>
70
- std::vector<VALUE> NativeFunction<Function_T, IsMethod>::getRubyValues(int argc, VALUE* argv)
87
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
88
+ std::vector<VALUE> NativeFunction<From_Ruby_T, Function_T, IsMethod>::getRubyValues(int argc, VALUE* argv)
71
89
  {
72
- // Setup a tuple to contain required methodInfo to rb_scan_args
90
+ // Setup a tuple for the leading rb_scan_args arguments
73
91
  std::string scanFormat = this->methodInfo_->formatString();
74
- std::tuple<int, VALUE*, const char*> rbScanMandatory = std::forward_as_tuple(argc, argv, scanFormat.c_str());
92
+ std::tuple<int, VALUE*, const char*> rbScanArgs = std::forward_as_tuple(argc, argv, scanFormat.c_str());
75
93
 
76
- // Create a vector to store the variable number of Ruby Values
77
- std::vector<VALUE> rbScanArgsOptional(std::tuple_size_v<Arg_Ts>, Qnil);
94
+ // Create a vector to store the VALUEs that will be returned by rb_scan_args
95
+ std::vector<VALUE> rbScanValues(std::tuple_size_v<Arg_Ts>, Qnil);
78
96
 
79
- // Convert the vector to an array so it can then be concatenated to a tuple
80
- std::array<VALUE*, std::tuple_size_v<Arg_Ts>> rbScanArgsOptionalPointers;
81
- std::transform(rbScanArgsOptional.begin(), rbScanArgsOptional.end(), rbScanArgsOptionalPointers.begin(),
97
+ // Convert the vector to an array so it can be concatenated to a tuple. As importantly
98
+ // fill it with pointers to rbScanValues
99
+ std::array<VALUE*, std::tuple_size_v<Arg_Ts>> rbScanValuePointers;
100
+ std::transform(rbScanValues.begin(), rbScanValues.end(), rbScanValuePointers.begin(),
82
101
  [](VALUE& value)
83
102
  {
84
103
  return &value;
85
104
  });
86
105
 
87
106
  // Combine the tuples and call rb_scan_args
88
- auto rbScanArgs = std::tuple_cat(rbScanMandatory, rbScanArgsOptionalPointers);
89
- std::apply(rb_scan_args, rbScanArgs);
107
+ std::apply(rb_scan_args, std::tuple_cat(rbScanArgs, rbScanValuePointers));
90
108
 
91
- return rbScanArgsOptional;
109
+ return rbScanValues;
92
110
  }
93
111
 
94
- template<typename Function_T, bool IsMethod>
112
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
95
113
  template<std::size_t... I>
96
- typename NativeFunction<Function_T, IsMethod>::Arg_Ts NativeFunction<Function_T, IsMethod>::getNativeValues(std::vector<VALUE>& values,
114
+ typename NativeFunction<From_Ruby_T, Function_T, IsMethod>::Arg_Ts NativeFunction<From_Ruby_T, Function_T, IsMethod>::getNativeValues(std::vector<VALUE>& values,
97
115
  std::index_sequence<I...>& indices)
98
116
  {
99
117
  // Convert each Ruby value to its native value by calling the appropriate fromRuby instance.
@@ -102,80 +120,91 @@ namespace Rice::detail
102
120
  return std::forward_as_tuple(std::get<I>(this->fromRubys_).convert(values[I])...);
103
121
  }
104
122
 
105
- template<typename Function_T, bool IsMethod>
106
- typename NativeFunction<Function_T, IsMethod>::Self_T NativeFunction<Function_T, IsMethod>::getSelf(VALUE self)
123
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
124
+ typename NativeFunction<From_Ruby_T, Function_T, IsMethod>::Class_T NativeFunction<From_Ruby_T, Function_T, IsMethod>::getReceiver(VALUE self)
107
125
  {
108
126
  // There is no self parameter
109
- if constexpr (std::is_same_v<Self_T, std::nullptr_t>)
127
+ if constexpr (std::is_same_v<Class_T, std::nullptr_t>)
110
128
  {
111
129
  return nullptr;
112
130
  }
113
131
  // Self parameter is a Ruby VALUE so no conversion is needed
114
- else if constexpr (std::is_same_v<Self_T, VALUE>)
132
+ else if constexpr (std::is_same_v<Class_T, VALUE>)
115
133
  {
116
134
  return self;
117
135
  }
118
- // Self parameter could be derived from Object or it is an C++ instdance and
136
+ /* This case happens when a class wrapped by Rice is calling a method
137
+ defined on an ancestor class. For example, the std::map size method
138
+ is defined on _Tree not map. Rice needs to know the actual type
139
+ that was wrapped so it can correctly extract the C++ object from
140
+ the Ruby object. */
141
+ else if constexpr (!std::is_same_v<intrinsic_type<Class_T>, From_Ruby_T> &&
142
+ std::is_base_of_v<intrinsic_type<Class_T>, From_Ruby_T>)
143
+ {
144
+ From_Ruby_T* instance = From_Ruby<From_Ruby_T*>().convert(self);
145
+ return dynamic_cast<Class_T>(instance);
146
+ }
147
+ // Self parameter could be derived from Object or it is an C++ instance and
119
148
  // needs to be unwrapped from Ruby
120
149
  else
121
150
  {
122
- return From_Ruby<Self_T>().convert(self);
151
+ return From_Ruby<Class_T>().convert(self);
123
152
  }
124
153
  }
125
154
 
126
- template<typename Function_T, bool IsMethod>
127
- VALUE NativeFunction<Function_T, IsMethod>::invokeNativeFunction(Arg_Ts& nativeArgs)
155
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
156
+ VALUE NativeFunction<From_Ruby_T, Function_T, IsMethod>::invokeNativeFunction(const Arg_Ts& nativeArgs)
128
157
  {
129
158
  if constexpr (std::is_void_v<Return_T>)
130
159
  {
131
- std::apply(this->func_, nativeArgs);
160
+ std::apply(this->function_, nativeArgs);
132
161
  return Qnil;
133
162
  }
134
163
  else
135
164
  {
136
165
  // Call the native method and get the result
137
- Return_T nativeResult = std::apply(this->func_, nativeArgs);
166
+ Return_T nativeResult = std::apply(this->function_, nativeArgs);
138
167
 
139
168
  // Return the result
140
169
  return this->toRuby_.convert(nativeResult);
141
170
  }
142
171
  }
143
172
 
144
- template<typename Function_T, bool IsMethod>
145
- VALUE NativeFunction<Function_T, IsMethod>::invokeNativeMethod(VALUE self, Arg_Ts& nativeArgs)
173
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
174
+ VALUE NativeFunction<From_Ruby_T, Function_T, IsMethod>::invokeNativeMethod(VALUE self, const Arg_Ts& nativeArgs)
146
175
  {
147
- Self_T receiver = this->getSelf(self);
176
+ Class_T receiver = this->getReceiver(self);
148
177
  auto selfAndNativeArgs = std::tuple_cat(std::forward_as_tuple(receiver), nativeArgs);
149
178
 
150
179
  if constexpr (std::is_void_v<Return_T>)
151
180
  {
152
- std::apply(this->func_, selfAndNativeArgs);
181
+ std::apply(this->function_, selfAndNativeArgs);
153
182
  return Qnil;
154
183
  }
155
184
  else
156
185
  {
157
- Return_T nativeResult = (Return_T)std::apply(this->func_, selfAndNativeArgs);
186
+ Return_T nativeResult = (Return_T)std::apply(this->function_, selfAndNativeArgs);
158
187
 
159
188
  // Special handling if the method returns self. If so we do not want
160
189
  // to create a new Ruby wrapper object and instead return self.
161
- if constexpr (std::is_same_v<intrinsic_type<Return_T>, intrinsic_type<Self_T>>)
190
+ if constexpr (std::is_same_v<intrinsic_type<Return_T>, intrinsic_type<Class_T>>)
162
191
  {
163
- if constexpr (std::is_pointer_v<Return_T> && std::is_pointer_v<Self_T>)
192
+ if constexpr (std::is_pointer_v<Return_T> && std::is_pointer_v<Class_T>)
164
193
  {
165
194
  if (nativeResult == receiver)
166
195
  return self;
167
196
  }
168
- else if constexpr (std::is_pointer_v<Return_T> && std::is_reference_v<Self_T>)
197
+ else if constexpr (std::is_pointer_v<Return_T> && std::is_reference_v<Class_T>)
169
198
  {
170
199
  if (nativeResult == &receiver)
171
200
  return self;
172
201
  }
173
- else if constexpr (std::is_reference_v<Return_T> && std::is_pointer_v<Self_T>)
202
+ else if constexpr (std::is_reference_v<Return_T> && std::is_pointer_v<Class_T>)
174
203
  {
175
204
  if (&nativeResult == receiver)
176
205
  return self;
177
206
  }
178
- else if constexpr (std::is_reference_v<Return_T> && std::is_reference_v<Self_T>)
207
+ else if constexpr (std::is_reference_v<Return_T> && std::is_reference_v<Class_T>)
179
208
  {
180
209
  if (&nativeResult == &receiver)
181
210
  return self;
@@ -186,63 +215,52 @@ namespace Rice::detail
186
215
  }
187
216
  }
188
217
 
189
- template<typename Function_T, bool IsMethod>
190
- void NativeFunction<Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues)
218
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
219
+ void NativeFunction<From_Ruby_T, Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues)
191
220
  {
192
221
  // Check function arguments
193
222
  Wrapper* selfWrapper = getWrapper(self);
194
223
  for (const Arg& arg : (*this->methodInfo_))
195
224
  {
196
- if (arg.isKeepAlive)
225
+ if (arg.isKeepAlive())
197
226
  {
198
227
  selfWrapper->addKeepAlive(rubyValues[arg.position]);
199
228
  }
200
229
  }
201
230
 
202
231
  // Check return value
203
- if (this->methodInfo_->returnInfo.isKeepAlive)
232
+ if (this->methodInfo_->returnInfo.isKeepAlive())
204
233
  {
205
234
  Wrapper* returnWrapper = getWrapper(returnValue);
206
235
  returnWrapper->addKeepAlive(self);
207
236
  }
208
237
  }
209
238
 
210
- template<typename Function_T, bool IsMethod>
211
- VALUE NativeFunction<Function_T, IsMethod>::operator()(int argc, VALUE* argv, VALUE self)
239
+ template<typename From_Ruby_T, typename Function_T, bool IsMethod>
240
+ VALUE NativeFunction<From_Ruby_T, Function_T, IsMethod>::operator()(int argc, VALUE* argv, VALUE self)
212
241
  {
213
- try
214
- {
215
- // Get the ruby values
216
- std::vector<VALUE> rubyValues = this->getRubyValues(argc, argv);
217
-
218
- auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
219
-
220
- // Convert the Ruby values to native values
221
- Arg_Ts nativeValues = this->getNativeValues(rubyValues, indices);
242
+ // Get the ruby values
243
+ std::vector<VALUE> rubyValues = this->getRubyValues(argc, argv);
222
244
 
223
- // Now call the native method
224
- VALUE result = Qnil;
225
- if constexpr (std::is_same_v<Self_T, std::nullptr_t>)
226
- {
227
- result = this->invokeNativeFunction(nativeValues);
228
- }
229
- else
230
- {
231
- result = this->invokeNativeMethod(self, nativeValues);
232
- }
245
+ auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
233
246
 
234
- // Check if any function arguments or return values need to have their lifetimes tied to the receiver
235
- this->checkKeepAlive(self, result, rubyValues);
247
+ // Convert the Ruby values to native values
248
+ Arg_Ts nativeValues = this->getNativeValues(rubyValues, indices);
236
249
 
237
- return result;
250
+ // Now call the native method
251
+ VALUE result = Qnil;
252
+ if constexpr (std::is_same_v<Class_T, std::nullptr_t>)
253
+ {
254
+ result = this->invokeNativeFunction(nativeValues);
238
255
  }
239
- catch (...)
256
+ else
240
257
  {
241
- RUBY_TRY
242
- {
243
- return this->handler_->handle_exception();
244
- }
245
- RUBY_CATCH
258
+ result = this->invokeNativeMethod(self, nativeValues);
246
259
  }
260
+
261
+ // Check if any function arguments or return values need to have their lifetimes tied to the receiver
262
+ this->checkKeepAlive(self, result, rubyValues);
263
+
264
+ return result;
247
265
  }
248
266
  }