rice 4.0.4 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -0
- data/CONTRIBUTORS.md +2 -0
- data/Rakefile +1 -1
- data/include/rice/rice.hpp +2851 -1955
- data/include/rice/stl.hpp +1654 -287
- data/lib/mkmf-rice.rb +5 -2
- data/lib/version.rb +1 -1
- data/rice/Arg.hpp +6 -6
- data/rice/Arg.ipp +8 -9
- data/rice/Constructor.hpp +2 -2
- data/rice/Data_Object.ipp +69 -15
- data/rice/Data_Object_defn.hpp +1 -15
- data/rice/Data_Type.ipp +56 -86
- data/rice/Data_Type_defn.hpp +14 -17
- data/rice/Director.hpp +0 -1
- data/rice/Enum.ipp +31 -22
- data/rice/Exception.ipp +2 -3
- data/rice/Exception_defn.hpp +5 -5
- data/rice/HandlerRegistration.hpp +15 -0
- data/rice/Return.hpp +5 -4
- data/rice/Return.ipp +8 -3
- data/rice/detail/ExceptionHandler.hpp +8 -0
- data/rice/detail/ExceptionHandler.ipp +28 -0
- data/rice/detail/{Exception_Handler_defn.hpp → ExceptionHandler_defn.hpp} +17 -21
- data/rice/detail/HandlerRegistry.hpp +51 -0
- data/rice/detail/HandlerRegistry.ipp +20 -0
- data/rice/detail/InstanceRegistry.hpp +34 -0
- data/rice/detail/InstanceRegistry.ipp +50 -0
- data/rice/detail/MethodInfo.ipp +1 -1
- data/rice/detail/NativeAttribute.hpp +26 -15
- data/rice/detail/NativeAttribute.ipp +76 -47
- data/rice/detail/NativeFunction.hpp +64 -14
- data/rice/detail/NativeFunction.ipp +138 -86
- data/rice/detail/NativeIterator.hpp +49 -0
- data/rice/detail/NativeIterator.ipp +102 -0
- data/rice/detail/NativeRegistry.hpp +31 -0
- data/rice/detail/{method_data.ipp → NativeRegistry.ipp} +20 -16
- data/rice/detail/Registries.hpp +26 -0
- data/rice/detail/Registries.ipp +23 -0
- data/rice/detail/RubyFunction.hpp +6 -11
- data/rice/detail/RubyFunction.ipp +10 -22
- data/rice/detail/Type.hpp +1 -1
- data/rice/detail/Type.ipp +2 -2
- data/rice/detail/TypeRegistry.hpp +8 -11
- data/rice/detail/TypeRegistry.ipp +3 -28
- data/rice/detail/Wrapper.hpp +0 -2
- data/rice/detail/Wrapper.ipp +74 -24
- data/rice/detail/cpp_protect.hpp +93 -0
- data/rice/detail/default_allocation_func.ipp +1 -1
- data/rice/detail/from_ruby.ipp +206 -2
- data/rice/detail/to_ruby.ipp +39 -5
- data/rice/detail/to_ruby_defn.hpp +1 -1
- data/rice/forward_declares.ipp +6 -0
- data/rice/global_function.hpp +0 -4
- data/rice/global_function.ipp +0 -6
- data/rice/rice.hpp +29 -24
- data/rice/stl.hpp +6 -1
- data/sample/callbacks/extconf.rb +0 -1
- data/sample/enum/extconf.rb +0 -1
- data/sample/inheritance/extconf.rb +0 -1
- data/sample/map/extconf.rb +0 -1
- data/test/embed_ruby.cpp +6 -15
- data/test/ext/t1/extconf.rb +0 -1
- data/test/ext/t2/extconf.rb +0 -1
- data/test/extconf.rb +0 -1
- data/test/test_Array.cpp +20 -24
- data/test/test_Attribute.cpp +6 -6
- data/test/test_Class.cpp +8 -47
- data/test/test_Constructor.cpp +0 -2
- data/test/test_Data_Object.cpp +25 -11
- data/test/test_Data_Type.cpp +124 -28
- data/test/test_Director.cpp +12 -13
- data/test/test_Enum.cpp +65 -26
- data/test/test_Inheritance.cpp +9 -9
- data/test/test_Iterator.cpp +134 -5
- data/test/test_Keep_Alive.cpp +7 -7
- data/test/test_Keep_Alive_No_Wrapper.cpp +80 -0
- data/test/test_Memory_Management.cpp +1 -1
- data/test/test_Module.cpp +25 -62
- data/test/test_Object.cpp +75 -3
- data/test/test_Ownership.cpp +12 -13
- data/test/test_Self.cpp +12 -13
- data/test/test_Stl_Map.cpp +696 -0
- data/test/test_Stl_Optional.cpp +3 -3
- data/test/test_Stl_Pair.cpp +38 -2
- data/test/test_Stl_Reference_Wrapper.cpp +102 -0
- data/test/test_Stl_SmartPointer.cpp +49 -9
- data/test/test_Stl_String.cpp +5 -2
- data/test/test_Stl_Unordered_Map.cpp +697 -0
- data/test/test_Stl_Variant.cpp +346 -0
- data/test/test_Stl_Vector.cpp +200 -41
- data/test/test_Struct.cpp +3 -3
- data/test/test_To_From_Ruby.cpp +8 -2
- data/test/test_Tracking.cpp +239 -0
- data/test/unittest.hpp +21 -4
- metadata +24 -13
- data/rice/detail/Exception_Handler.hpp +0 -8
- data/rice/detail/Exception_Handler.ipp +0 -28
- data/rice/detail/Iterator.hpp +0 -23
- data/rice/detail/Iterator.ipp +0 -47
- data/rice/detail/function_traits.hpp +0 -124
- data/rice/detail/method_data.hpp +0 -29
- data/rice/detail/rice_traits.hpp +0 -116
- 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 "
|
4
|
+
#include "../traits/rice_traits.hpp"
|
5
|
+
#include "NativeRegistry.hpp"
|
6
6
|
#include "to_ruby_defn.hpp"
|
7
|
-
#include "
|
7
|
+
#include "cpp_protect.hpp"
|
8
8
|
|
9
9
|
namespace Rice::detail
|
10
10
|
{
|
11
|
-
template<typename
|
12
|
-
|
11
|
+
template<typename Attribute_T>
|
12
|
+
void NativeAttribute<Attribute_T>::define(VALUE klass, std::string name, Attribute_T attribute, AttrAccess access)
|
13
13
|
{
|
14
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
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
|
24
|
-
inline VALUE NativeAttribute<
|
55
|
+
template<typename Attribute_T>
|
56
|
+
inline VALUE NativeAttribute<Attribute_T>::set(VALUE self, VALUE value)
|
25
57
|
{
|
26
|
-
|
58
|
+
return cpp_protect([&]
|
27
59
|
{
|
28
|
-
using Native_Attr_T = NativeAttribute<
|
29
|
-
Native_Attr_T* attr = detail::
|
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
|
36
|
-
NativeAttribute<
|
37
|
-
|
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
|
42
|
-
inline VALUE NativeAttribute<
|
73
|
+
template<typename Attribute_T>
|
74
|
+
inline VALUE NativeAttribute<Attribute_T>::read(VALUE self)
|
43
75
|
{
|
44
|
-
using
|
45
|
-
if constexpr (std::is_member_object_pointer_v<
|
76
|
+
using T_Unqualified = remove_cv_recursive_t<T>;
|
77
|
+
if constexpr (std::is_member_object_pointer_v<Attribute_T>)
|
46
78
|
{
|
47
|
-
|
48
|
-
return To_Ruby<
|
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<
|
84
|
+
return To_Ruby<T_Unqualified>().convert(*attribute_);
|
53
85
|
}
|
54
86
|
}
|
55
87
|
|
56
|
-
template<typename
|
57
|
-
inline VALUE NativeAttribute<
|
88
|
+
template<typename Attribute_T>
|
89
|
+
inline VALUE NativeAttribute<Attribute_T>::write(VALUE self, VALUE value)
|
58
90
|
{
|
59
|
-
if constexpr (
|
91
|
+
if constexpr (std::is_fundamental_v<intrinsic_type<T>> && std::is_pointer_v<T>)
|
60
92
|
{
|
61
|
-
|
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 (
|
95
|
+
else if constexpr (std::is_same_v<intrinsic_type<T>, std::string> && std::is_pointer_v<T>)
|
65
96
|
{
|
66
|
-
|
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
|
-
|
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 "
|
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
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
84
|
+
From_Ruby_Args_Ts createFromRuby(std::index_sequence<I...>& indices);
|
39
85
|
|
40
86
|
To_Ruby<Return_T> createToRuby();
|
41
87
|
|
@@ -47,23 +93,27 @@ 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
|
-
|
96
|
+
Class_T getReceiver(VALUE self);
|
97
|
+
|
98
|
+
// Throw an exception when wrapper cannot be extracted
|
99
|
+
[[noreturn]] void noWrapper(const VALUE klass, const std::string& wrapper);
|
51
100
|
|
52
101
|
// Do we need to keep alive any arguments?
|
53
102
|
void checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues);
|
54
103
|
|
55
104
|
// Call the underlying C++ function
|
56
|
-
VALUE invokeNativeFunction(Arg_Ts& nativeArgs);
|
57
|
-
VALUE invokeNativeMethod(VALUE self, Arg_Ts& nativeArgs);
|
105
|
+
VALUE invokeNativeFunction(const Arg_Ts& nativeArgs);
|
106
|
+
VALUE invokeNativeMethod(VALUE self, const Arg_Ts& nativeArgs);
|
58
107
|
|
59
108
|
private:
|
60
|
-
|
61
|
-
|
109
|
+
VALUE klass_;
|
110
|
+
std::string method_name_;
|
111
|
+
Function_T function_;
|
112
|
+
From_Ruby_Args_Ts fromRubys_;
|
62
113
|
To_Ruby<Return_T> toRuby_;
|
63
|
-
std::shared_ptr<Exception_Handler> handler_;
|
64
114
|
std::unique_ptr<MethodInfo> methodInfo_;
|
65
115
|
};
|
66
116
|
}
|
67
117
|
#include "NativeFunction.ipp"
|
68
118
|
|
69
|
-
#endif // Rice__detail__Native_Function__hpp_
|
119
|
+
#endif // Rice__detail__Native_Function__hpp_
|
@@ -1,24 +1,43 @@
|
|
1
1
|
#include <array>
|
2
2
|
#include <algorithm>
|
3
3
|
#include <stdexcept>
|
4
|
+
#include <sstream>
|
4
5
|
|
5
|
-
#include "
|
6
|
+
#include "cpp_protect.hpp"
|
6
7
|
#include "to_ruby_defn.hpp"
|
7
|
-
#include "
|
8
|
+
#include "NativeRegistry.hpp"
|
8
9
|
|
9
10
|
namespace Rice::detail
|
10
11
|
{
|
11
|
-
template<typename Function_T, bool IsMethod>
|
12
|
-
|
12
|
+
template<typename From_Ruby_T, typename Function_T, bool IsMethod>
|
13
|
+
void NativeFunction<From_Ruby_T, Function_T, IsMethod>::define(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
|
13
14
|
{
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
// Tell Ruby to invoke the static method call on this class
|
16
|
+
detail::protect(rb_define_method, klass, method_name.c_str(), (RUBY_METHOD_FUNC)&NativeFunction_T::call, -1);
|
17
|
+
|
18
|
+
// Now create a NativeFunction instance and save it to the natives registry keyed on
|
19
|
+
// Ruby klass and method id. There may be multiple NativeFunction instances
|
20
|
+
// because the same C++ method could be mapped to multiple Ruby methods.
|
21
|
+
NativeFunction_T* native = new NativeFunction_T(klass, method_name, std::forward<Function_T>(function), methodInfo);
|
22
|
+
detail::Registries::instance.natives.add(klass, Identifier(method_name).id(), native);
|
23
|
+
}
|
24
|
+
|
25
|
+
template<typename From_Ruby_T, typename Function_T, bool IsMethod>
|
26
|
+
VALUE NativeFunction<From_Ruby_T, Function_T, IsMethod>::call(int argc, VALUE* argv, VALUE self)
|
27
|
+
{
|
28
|
+
// Look up the native function based on the Ruby klass and method id
|
29
|
+
NativeFunction_T* nativeFunction = detail::Registries::instance.natives.lookup<NativeFunction_T*>();
|
30
|
+
|
31
|
+
// Execute the function but make sure to catch any C++ exceptions!
|
32
|
+
return cpp_protect([&]
|
33
|
+
{
|
34
|
+
return nativeFunction->operator()(argc, argv, self);
|
35
|
+
});
|
17
36
|
}
|
18
37
|
|
19
|
-
template<typename Function_T, bool IsMethod>
|
20
|
-
NativeFunction<Function_T, IsMethod>::NativeFunction(
|
21
|
-
:
|
38
|
+
template<typename From_Ruby_T, typename Function_T, bool IsMethod>
|
39
|
+
NativeFunction<From_Ruby_T, Function_T, IsMethod>::NativeFunction(VALUE klass, std::string method_name, Function_T function, MethodInfo* methodInfo)
|
40
|
+
: klass_(klass), method_name_(method_name), function_(function), methodInfo_(methodInfo)
|
22
41
|
{
|
23
42
|
// Create a tuple of NativeArgs that will convert the Ruby values to native values. For
|
24
43
|
// builtin types NativeArgs will keep a copy of the native value so that it
|
@@ -30,9 +49,9 @@ namespace Rice::detail
|
|
30
49
|
this->toRuby_ = this->createToRuby();
|
31
50
|
}
|
32
51
|
|
33
|
-
template<typename Function_T, bool IsMethod>
|
52
|
+
template<typename From_Ruby_T, typename Function_T, bool IsMethod>
|
34
53
|
template<typename T, std::size_t I>
|
35
|
-
From_Ruby<T> NativeFunction<Function_T, IsMethod>::createFromRuby()
|
54
|
+
From_Ruby<T> NativeFunction<From_Ruby_T, Function_T, IsMethod>::createFromRuby()
|
36
55
|
{
|
37
56
|
// Does the From_Ruby instantiation work with Arg?
|
38
57
|
if constexpr (std::is_constructible_v<From_Ruby<T>, Arg*>)
|
@@ -45,8 +64,8 @@ namespace Rice::detail
|
|
45
64
|
}
|
46
65
|
}
|
47
66
|
|
48
|
-
template<typename Function_T, bool IsMethod>
|
49
|
-
To_Ruby<typename NativeFunction<Function_T, IsMethod>::Return_T> NativeFunction<Function_T, IsMethod>::createToRuby()
|
67
|
+
template<typename From_Ruby_T, typename Function_T, bool IsMethod>
|
68
|
+
To_Ruby<typename NativeFunction<From_Ruby_T, Function_T, IsMethod>::Return_T> NativeFunction<From_Ruby_T, Function_T, IsMethod>::createToRuby()
|
50
69
|
{
|
51
70
|
// Does the From_Ruby instantiation work with ReturnInfo?
|
52
71
|
if constexpr (std::is_constructible_v<To_Ruby<Return_T>, Return*>)
|
@@ -59,41 +78,41 @@ namespace Rice::detail
|
|
59
78
|
}
|
60
79
|
}
|
61
80
|
|
62
|
-
template<typename Function_T, bool IsMethod>
|
81
|
+
template<typename From_Ruby_T, typename Function_T, bool IsMethod>
|
63
82
|
template<std::size_t... I>
|
64
|
-
typename NativeFunction<Function_T, IsMethod>::
|
83
|
+
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
84
|
{
|
66
85
|
return std::make_tuple(createFromRuby<remove_cv_recursive_t<typename std::tuple_element<I, Arg_Ts>::type>, I>()...);
|
67
86
|
}
|
68
87
|
|
69
|
-
template<typename Function_T, bool IsMethod>
|
70
|
-
std::vector<VALUE> NativeFunction<Function_T, IsMethod>::getRubyValues(int argc, VALUE* argv)
|
88
|
+
template<typename From_Ruby_T, typename Function_T, bool IsMethod>
|
89
|
+
std::vector<VALUE> NativeFunction<From_Ruby_T, Function_T, IsMethod>::getRubyValues(int argc, VALUE* argv)
|
71
90
|
{
|
72
|
-
// Setup a tuple
|
91
|
+
// Setup a tuple for the leading rb_scan_args arguments
|
73
92
|
std::string scanFormat = this->methodInfo_->formatString();
|
74
|
-
std::tuple<int, VALUE*, const char*>
|
93
|
+
std::tuple<int, VALUE*, const char*> rbScanArgs = std::forward_as_tuple(argc, argv, scanFormat.c_str());
|
75
94
|
|
76
|
-
// Create a vector to store the
|
77
|
-
std::vector<VALUE>
|
95
|
+
// Create a vector to store the VALUEs that will be returned by rb_scan_args
|
96
|
+
std::vector<VALUE> rbScanValues(std::tuple_size_v<Arg_Ts>, Qnil);
|
78
97
|
|
79
|
-
// Convert the vector to an array so it can
|
80
|
-
|
81
|
-
std::
|
98
|
+
// Convert the vector to an array so it can be concatenated to a tuple. As importantly
|
99
|
+
// fill it with pointers to rbScanValues
|
100
|
+
std::array<VALUE*, std::tuple_size_v<Arg_Ts>> rbScanValuePointers;
|
101
|
+
std::transform(rbScanValues.begin(), rbScanValues.end(), rbScanValuePointers.begin(),
|
82
102
|
[](VALUE& value)
|
83
103
|
{
|
84
104
|
return &value;
|
85
105
|
});
|
86
106
|
|
87
107
|
// Combine the tuples and call rb_scan_args
|
88
|
-
|
89
|
-
std::apply(rb_scan_args, rbScanArgs);
|
108
|
+
std::apply(rb_scan_args, std::tuple_cat(rbScanArgs, rbScanValuePointers));
|
90
109
|
|
91
|
-
return
|
110
|
+
return rbScanValues;
|
92
111
|
}
|
93
112
|
|
94
|
-
template<typename Function_T, bool IsMethod>
|
113
|
+
template<typename From_Ruby_T, typename Function_T, bool IsMethod>
|
95
114
|
template<std::size_t... I>
|
96
|
-
typename NativeFunction<Function_T, IsMethod>::Arg_Ts NativeFunction<Function_T, IsMethod>::getNativeValues(std::vector<VALUE>& values,
|
115
|
+
typename NativeFunction<From_Ruby_T, Function_T, IsMethod>::Arg_Ts NativeFunction<From_Ruby_T, Function_T, IsMethod>::getNativeValues(std::vector<VALUE>& values,
|
97
116
|
std::index_sequence<I...>& indices)
|
98
117
|
{
|
99
118
|
// Convert each Ruby value to its native value by calling the appropriate fromRuby instance.
|
@@ -102,80 +121,91 @@ namespace Rice::detail
|
|
102
121
|
return std::forward_as_tuple(std::get<I>(this->fromRubys_).convert(values[I])...);
|
103
122
|
}
|
104
123
|
|
105
|
-
template<typename Function_T, bool IsMethod>
|
106
|
-
typename NativeFunction<Function_T, IsMethod>::
|
124
|
+
template<typename From_Ruby_T, typename Function_T, bool IsMethod>
|
125
|
+
typename NativeFunction<From_Ruby_T, Function_T, IsMethod>::Class_T NativeFunction<From_Ruby_T, Function_T, IsMethod>::getReceiver(VALUE self)
|
107
126
|
{
|
108
127
|
// There is no self parameter
|
109
|
-
if constexpr (std::is_same_v<
|
128
|
+
if constexpr (std::is_same_v<Class_T, std::nullptr_t>)
|
110
129
|
{
|
111
130
|
return nullptr;
|
112
131
|
}
|
113
132
|
// Self parameter is a Ruby VALUE so no conversion is needed
|
114
|
-
else if constexpr (std::is_same_v<
|
133
|
+
else if constexpr (std::is_same_v<Class_T, VALUE>)
|
115
134
|
{
|
116
135
|
return self;
|
117
136
|
}
|
118
|
-
|
137
|
+
/* This case happens when a class wrapped by Rice is calling a method
|
138
|
+
defined on an ancestor class. For example, the std::map size method
|
139
|
+
is defined on _Tree not map. Rice needs to know the actual type
|
140
|
+
that was wrapped so it can correctly extract the C++ object from
|
141
|
+
the Ruby object. */
|
142
|
+
else if constexpr (!std::is_same_v<intrinsic_type<Class_T>, From_Ruby_T> &&
|
143
|
+
std::is_base_of_v<intrinsic_type<Class_T>, From_Ruby_T>)
|
144
|
+
{
|
145
|
+
From_Ruby_T* instance = From_Ruby<From_Ruby_T*>().convert(self);
|
146
|
+
return dynamic_cast<Class_T>(instance);
|
147
|
+
}
|
148
|
+
// Self parameter could be derived from Object or it is an C++ instance and
|
119
149
|
// needs to be unwrapped from Ruby
|
120
150
|
else
|
121
151
|
{
|
122
|
-
return From_Ruby<
|
152
|
+
return From_Ruby<Class_T>().convert(self);
|
123
153
|
}
|
124
154
|
}
|
125
155
|
|
126
|
-
template<typename Function_T, bool IsMethod>
|
127
|
-
VALUE NativeFunction<Function_T, IsMethod>::invokeNativeFunction(Arg_Ts& nativeArgs)
|
156
|
+
template<typename From_Ruby_T, typename Function_T, bool IsMethod>
|
157
|
+
VALUE NativeFunction<From_Ruby_T, Function_T, IsMethod>::invokeNativeFunction(const Arg_Ts& nativeArgs)
|
128
158
|
{
|
129
159
|
if constexpr (std::is_void_v<Return_T>)
|
130
160
|
{
|
131
|
-
std::apply(this->
|
161
|
+
std::apply(this->function_, nativeArgs);
|
132
162
|
return Qnil;
|
133
163
|
}
|
134
164
|
else
|
135
165
|
{
|
136
166
|
// Call the native method and get the result
|
137
|
-
Return_T nativeResult = std::apply(this->
|
138
|
-
|
167
|
+
Return_T nativeResult = std::apply(this->function_, nativeArgs);
|
168
|
+
|
139
169
|
// Return the result
|
140
170
|
return this->toRuby_.convert(nativeResult);
|
141
171
|
}
|
142
172
|
}
|
143
173
|
|
144
|
-
template<typename Function_T, bool IsMethod>
|
145
|
-
VALUE NativeFunction<Function_T, IsMethod>::invokeNativeMethod(VALUE self, Arg_Ts& nativeArgs)
|
174
|
+
template<typename From_Ruby_T, typename Function_T, bool IsMethod>
|
175
|
+
VALUE NativeFunction<From_Ruby_T, Function_T, IsMethod>::invokeNativeMethod(VALUE self, const Arg_Ts& nativeArgs)
|
146
176
|
{
|
147
|
-
|
177
|
+
Class_T receiver = this->getReceiver(self);
|
148
178
|
auto selfAndNativeArgs = std::tuple_cat(std::forward_as_tuple(receiver), nativeArgs);
|
149
179
|
|
150
180
|
if constexpr (std::is_void_v<Return_T>)
|
151
181
|
{
|
152
|
-
std::apply(this->
|
182
|
+
std::apply(this->function_, selfAndNativeArgs);
|
153
183
|
return Qnil;
|
154
184
|
}
|
155
185
|
else
|
156
186
|
{
|
157
|
-
Return_T nativeResult = (Return_T)std::apply(this->
|
187
|
+
Return_T nativeResult = (Return_T)std::apply(this->function_, selfAndNativeArgs);
|
158
188
|
|
159
189
|
// Special handling if the method returns self. If so we do not want
|
160
190
|
// to create a new Ruby wrapper object and instead return self.
|
161
|
-
if constexpr (std::is_same_v<intrinsic_type<Return_T>, intrinsic_type<
|
191
|
+
if constexpr (std::is_same_v<intrinsic_type<Return_T>, intrinsic_type<Class_T>>)
|
162
192
|
{
|
163
|
-
if constexpr (std::is_pointer_v<Return_T> && std::is_pointer_v<
|
193
|
+
if constexpr (std::is_pointer_v<Return_T> && std::is_pointer_v<Class_T>)
|
164
194
|
{
|
165
195
|
if (nativeResult == receiver)
|
166
196
|
return self;
|
167
197
|
}
|
168
|
-
else if constexpr (std::is_pointer_v<Return_T> && std::is_reference_v<
|
198
|
+
else if constexpr (std::is_pointer_v<Return_T> && std::is_reference_v<Class_T>)
|
169
199
|
{
|
170
200
|
if (nativeResult == &receiver)
|
171
201
|
return self;
|
172
202
|
}
|
173
|
-
else if constexpr (std::is_reference_v<Return_T> && std::is_pointer_v<
|
203
|
+
else if constexpr (std::is_reference_v<Return_T> && std::is_pointer_v<Class_T>)
|
174
204
|
{
|
175
205
|
if (&nativeResult == receiver)
|
176
206
|
return self;
|
177
207
|
}
|
178
|
-
else if constexpr (std::is_reference_v<Return_T> && std::is_reference_v<
|
208
|
+
else if constexpr (std::is_reference_v<Return_T> && std::is_reference_v<Class_T>)
|
179
209
|
{
|
180
210
|
if (&nativeResult == &receiver)
|
181
211
|
return self;
|
@@ -186,63 +216,85 @@ namespace Rice::detail
|
|
186
216
|
}
|
187
217
|
}
|
188
218
|
|
189
|
-
template<typename Function_T, bool IsMethod>
|
190
|
-
void NativeFunction<Function_T, IsMethod>::
|
219
|
+
template<typename From_Ruby_T, typename Function_T, bool IsMethod>
|
220
|
+
void NativeFunction<From_Ruby_T, Function_T, IsMethod>::noWrapper(const VALUE klass, const std::string& wrapper)
|
191
221
|
{
|
192
|
-
|
222
|
+
std::stringstream message;
|
223
|
+
|
224
|
+
message << "When calling the method `";
|
225
|
+
message << this->method_name_;
|
226
|
+
message << "' we could not find the wrapper for the '";
|
227
|
+
message << rb_obj_classname(klass);
|
228
|
+
message << "' ";
|
229
|
+
message << wrapper;
|
230
|
+
message << " type. You should not use keepAlive() on a Return or Arg that is a builtin Rice type.";
|
231
|
+
|
232
|
+
throw std::runtime_error(message.str());
|
233
|
+
}
|
234
|
+
|
235
|
+
template<typename From_Ruby_T, typename Function_T, bool IsMethod>
|
236
|
+
void NativeFunction<From_Ruby_T, Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues)
|
237
|
+
{
|
238
|
+
// selfWrapper will be nullptr if this(self) is a builtin type and not an external(wrapped) type
|
239
|
+
// it is highly unlikely that keepAlive is used in this case but we check anyway
|
193
240
|
Wrapper* selfWrapper = getWrapper(self);
|
241
|
+
|
242
|
+
// Check function arguments
|
194
243
|
for (const Arg& arg : (*this->methodInfo_))
|
195
244
|
{
|
196
|
-
if (arg.isKeepAlive)
|
245
|
+
if (arg.isKeepAlive())
|
197
246
|
{
|
247
|
+
if (selfWrapper == nullptr)
|
248
|
+
{
|
249
|
+
noWrapper(self, "self");
|
250
|
+
}
|
198
251
|
selfWrapper->addKeepAlive(rubyValues[arg.position]);
|
199
252
|
}
|
200
253
|
}
|
201
254
|
|
202
255
|
// Check return value
|
203
|
-
if (this->methodInfo_->returnInfo.isKeepAlive)
|
256
|
+
if (this->methodInfo_->returnInfo.isKeepAlive())
|
204
257
|
{
|
258
|
+
if (selfWrapper == nullptr)
|
259
|
+
{
|
260
|
+
noWrapper(self, "self");
|
261
|
+
}
|
262
|
+
|
263
|
+
// returnWrapper will be nullptr if returnValue is a built-in type and not an external(wrapped) type
|
205
264
|
Wrapper* returnWrapper = getWrapper(returnValue);
|
265
|
+
if (returnWrapper == nullptr)
|
266
|
+
{
|
267
|
+
noWrapper(returnValue, "return");
|
268
|
+
}
|
206
269
|
returnWrapper->addKeepAlive(self);
|
207
270
|
}
|
208
271
|
}
|
209
272
|
|
210
|
-
template<typename Function_T, bool IsMethod>
|
211
|
-
VALUE NativeFunction<Function_T, IsMethod>::operator()(int argc, VALUE* argv, VALUE self)
|
273
|
+
template<typename From_Ruby_T, typename Function_T, bool IsMethod>
|
274
|
+
VALUE NativeFunction<From_Ruby_T, Function_T, IsMethod>::operator()(int argc, VALUE* argv, VALUE self)
|
212
275
|
{
|
213
|
-
|
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);
|
276
|
+
// Get the ruby values
|
277
|
+
std::vector<VALUE> rubyValues = this->getRubyValues(argc, argv);
|
222
278
|
|
223
|
-
|
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
|
-
}
|
279
|
+
auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
|
233
280
|
|
234
|
-
|
235
|
-
|
281
|
+
// Convert the Ruby values to native values
|
282
|
+
Arg_Ts nativeValues = this->getNativeValues(rubyValues, indices);
|
236
283
|
|
237
|
-
|
284
|
+
// Now call the native method
|
285
|
+
VALUE result = Qnil;
|
286
|
+
if constexpr (std::is_same_v<Class_T, std::nullptr_t>)
|
287
|
+
{
|
288
|
+
result = this->invokeNativeFunction(nativeValues);
|
238
289
|
}
|
239
|
-
|
290
|
+
else
|
240
291
|
{
|
241
|
-
|
242
|
-
{
|
243
|
-
return this->handler_->handle_exception();
|
244
|
-
}
|
245
|
-
RUBY_CATCH
|
292
|
+
result = this->invokeNativeMethod(self, nativeValues);
|
246
293
|
}
|
294
|
+
|
295
|
+
// Check if any function arguments or return values need to have their lifetimes tied to the receiver
|
296
|
+
this->checkKeepAlive(self, result, rubyValues);
|
297
|
+
|
298
|
+
return result;
|
247
299
|
}
|
248
300
|
}
|