rice 3.0.0 → 4.0.3
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 +121 -0
- data/CONTRIBUTORS.md +19 -0
- data/Gemfile +3 -0
- data/README.md +44 -1025
- data/Rakefile +95 -12
- data/include/rice/rice.hpp +7766 -0
- data/include/rice/stl.hpp +1113 -0
- data/lib/mkmf-rice.rb +127 -0
- data/lib/version.rb +3 -0
- data/rice/Address_Registration_Guard.ipp +75 -32
- data/rice/Address_Registration_Guard_defn.hpp +60 -56
- data/rice/Arg.hpp +80 -4
- data/rice/Arg.ipp +51 -0
- data/rice/Constructor.hpp +12 -14
- data/rice/Data_Object.ipp +234 -107
- data/rice/Data_Object_defn.hpp +77 -117
- data/rice/Data_Type.hpp +1 -2
- data/rice/Data_Type.ipp +251 -295
- data/rice/Data_Type_defn.hpp +175 -243
- data/rice/Director.hpp +11 -6
- data/rice/Enum.hpp +54 -104
- data/rice/Enum.ipp +104 -230
- data/rice/Exception.hpp +2 -8
- data/rice/Exception.ipp +65 -0
- data/rice/Exception_defn.hpp +46 -47
- data/rice/Identifier.hpp +28 -28
- data/rice/Identifier.ipp +23 -27
- data/rice/Return.hpp +39 -0
- data/rice/Return.ipp +33 -0
- data/rice/detail/Exception_Handler.ipp +22 -62
- data/rice/detail/Exception_Handler_defn.hpp +76 -91
- data/rice/detail/Iterator.hpp +18 -88
- data/rice/detail/Iterator.ipp +47 -0
- data/rice/detail/Jump_Tag.hpp +21 -0
- data/rice/detail/MethodInfo.hpp +44 -0
- data/rice/detail/MethodInfo.ipp +78 -0
- data/rice/detail/NativeAttribute.hpp +53 -0
- data/rice/detail/NativeAttribute.ipp +83 -0
- data/rice/detail/NativeFunction.hpp +69 -0
- data/rice/detail/NativeFunction.ipp +248 -0
- data/rice/detail/RubyFunction.hpp +39 -0
- data/rice/detail/RubyFunction.ipp +92 -0
- data/rice/detail/Type.hpp +29 -0
- data/rice/detail/Type.ipp +138 -0
- data/rice/detail/TypeRegistry.hpp +50 -0
- data/rice/detail/TypeRegistry.ipp +106 -0
- data/rice/detail/Wrapper.hpp +51 -0
- data/rice/detail/Wrapper.ipp +151 -0
- data/rice/detail/default_allocation_func.hpp +8 -19
- data/rice/detail/default_allocation_func.ipp +9 -8
- data/rice/detail/from_ruby.hpp +2 -37
- data/rice/detail/from_ruby.ipp +1020 -46
- data/rice/detail/from_ruby_defn.hpp +38 -0
- data/rice/detail/function_traits.hpp +124 -0
- data/rice/detail/method_data.hpp +23 -15
- data/rice/detail/method_data.ipp +53 -0
- data/rice/detail/rice_traits.hpp +116 -0
- data/rice/detail/ruby.hpp +9 -46
- data/rice/detail/to_ruby.hpp +3 -17
- data/rice/detail/to_ruby.ipp +409 -31
- data/rice/detail/to_ruby_defn.hpp +48 -0
- data/rice/forward_declares.ipp +82 -0
- data/rice/global_function.hpp +16 -20
- data/rice/global_function.ipp +8 -17
- data/rice/rice.hpp +59 -0
- data/rice/ruby_mark.hpp +5 -3
- data/rice/ruby_try_catch.hpp +4 -4
- data/rice/stl.hpp +11 -0
- data/sample/callbacks/extconf.rb +3 -0
- data/sample/callbacks/sample_callbacks.cpp +10 -13
- data/sample/enum/extconf.rb +3 -0
- data/sample/enum/sample_enum.cpp +3 -17
- data/sample/enum/test.rb +2 -2
- data/sample/inheritance/animals.cpp +8 -24
- data/sample/inheritance/extconf.rb +3 -0
- data/sample/inheritance/test.rb +1 -1
- data/sample/map/extconf.rb +3 -0
- data/sample/map/map.cpp +10 -18
- data/sample/map/test.rb +1 -1
- data/test/embed_ruby.cpp +18 -5
- data/test/ext/t1/extconf.rb +3 -0
- data/test/ext/t1/t1.cpp +1 -3
- data/test/ext/t2/extconf.rb +3 -0
- data/test/ext/t2/t2.cpp +1 -1
- data/test/extconf.rb +23 -0
- data/test/ruby/test_callbacks_sample.rb +28 -0
- data/test/ruby/test_multiple_extensions.rb +18 -0
- data/test/ruby/test_multiple_extensions_same_class.rb +14 -0
- data/test/ruby/test_multiple_extensions_with_inheritance.rb +20 -0
- data/test/test_Address_Registration_Guard.cpp +23 -10
- data/test/test_Array.cpp +129 -73
- data/test/test_Attribute.cpp +147 -0
- data/test/test_Builtin_Object.cpp +34 -14
- data/test/test_Class.cpp +149 -275
- data/test/test_Constructor.cpp +10 -9
- data/test/test_Data_Object.cpp +133 -192
- data/test/test_Data_Type.cpp +322 -252
- data/test/test_Director.cpp +54 -41
- data/test/test_Enum.cpp +228 -103
- data/test/test_Exception.cpp +5 -6
- data/test/test_Hash.cpp +31 -30
- data/test/test_Identifier.cpp +4 -5
- data/test/test_Inheritance.cpp +221 -0
- data/test/test_Iterator.cpp +161 -0
- data/test/test_Jump_Tag.cpp +1 -1
- data/test/test_Keep_Alive.cpp +161 -0
- data/test/test_Memory_Management.cpp +2 -4
- data/test/test_Module.cpp +167 -110
- data/test/test_Object.cpp +41 -21
- data/test/test_Ownership.cpp +275 -0
- data/test/test_Self.cpp +205 -0
- data/test/test_Stl_Optional.cpp +90 -0
- data/test/test_Stl_Pair.cpp +144 -0
- data/test/test_Stl_SmartPointer.cpp +200 -0
- data/test/test_Stl_String.cpp +74 -0
- data/test/test_Stl_Vector.cpp +652 -0
- data/test/test_String.cpp +1 -2
- data/test/test_Struct.cpp +29 -39
- data/test/test_Symbol.cpp +1 -2
- data/test/test_To_From_Ruby.cpp +249 -285
- data/test/test_global_functions.cpp +39 -19
- data/test/unittest.hpp +0 -4
- metadata +70 -141
- data/Doxyfile +0 -2268
- data/Makefile.am +0 -26
- data/Makefile.in +0 -931
- data/README.mingw +0 -8
- data/aclocal.m4 +0 -1085
- data/ax_cxx_compile_stdcxx.m4 +0 -951
- data/bootstrap +0 -8
- data/config.guess +0 -1421
- data/config.sub +0 -1807
- data/configure +0 -7792
- data/configure.ac +0 -55
- data/depcomp +0 -791
- data/doxygen.ac +0 -314
- data/doxygen.am +0 -186
- data/extconf.rb +0 -70
- data/install-sh +0 -501
- data/missing +0 -215
- data/post-autoconf.rb +0 -22
- data/post-automake.rb +0 -28
- data/rice/Address_Registration_Guard.cpp +0 -22
- data/rice/Arg_impl.hpp +0 -129
- data/rice/Arg_operators.cpp +0 -21
- data/rice/Arg_operators.hpp +0 -19
- data/rice/Array.hpp +0 -214
- data/rice/Array.ipp +0 -256
- data/rice/Builtin_Object.hpp +0 -8
- data/rice/Builtin_Object.ipp +0 -50
- data/rice/Builtin_Object_defn.hpp +0 -50
- data/rice/Class.cpp +0 -57
- data/rice/Class.hpp +0 -8
- data/rice/Class.ipp +0 -6
- data/rice/Class_defn.hpp +0 -84
- data/rice/Data_Type.cpp +0 -54
- data/rice/Data_Type_fwd.hpp +0 -12
- data/rice/Director.cpp +0 -13
- data/rice/Exception.cpp +0 -54
- data/rice/Exception_Base.hpp +0 -8
- data/rice/Exception_Base.ipp +0 -13
- data/rice/Exception_Base_defn.hpp +0 -27
- data/rice/Hash.hpp +0 -230
- data/rice/Hash.ipp +0 -329
- data/rice/Identifier.cpp +0 -8
- data/rice/Jump_Tag.hpp +0 -24
- data/rice/Makefile.am +0 -121
- data/rice/Makefile.in +0 -884
- data/rice/Module.cpp +0 -84
- data/rice/Module.hpp +0 -8
- data/rice/Module.ipp +0 -6
- data/rice/Module_defn.hpp +0 -88
- data/rice/Module_impl.hpp +0 -281
- data/rice/Module_impl.ipp +0 -345
- data/rice/Object.cpp +0 -169
- data/rice/Object.hpp +0 -8
- data/rice/Object.ipp +0 -33
- data/rice/Object_defn.hpp +0 -214
- data/rice/Require_Guard.hpp +0 -21
- data/rice/String.cpp +0 -89
- data/rice/String.hpp +0 -91
- data/rice/Struct.cpp +0 -117
- data/rice/Struct.hpp +0 -162
- data/rice/Struct.ipp +0 -26
- data/rice/Symbol.cpp +0 -25
- data/rice/Symbol.hpp +0 -66
- data/rice/Symbol.ipp +0 -44
- data/rice/config.hpp +0 -47
- data/rice/config.hpp.in +0 -46
- data/rice/detail/Arguments.hpp +0 -118
- data/rice/detail/Auto_Function_Wrapper.hpp +0 -898
- data/rice/detail/Auto_Function_Wrapper.ipp +0 -3181
- data/rice/detail/Auto_Member_Function_Wrapper.hpp +0 -897
- data/rice/detail/Auto_Member_Function_Wrapper.ipp +0 -2501
- data/rice/detail/Caster.hpp +0 -103
- data/rice/detail/Not_Copyable.hpp +0 -25
- data/rice/detail/Wrapped_Function.hpp +0 -33
- data/rice/detail/cfp.hpp +0 -24
- data/rice/detail/cfp.ipp +0 -51
- data/rice/detail/check_ruby_type.cpp +0 -27
- data/rice/detail/check_ruby_type.hpp +0 -23
- data/rice/detail/creation_funcs.hpp +0 -37
- data/rice/detail/creation_funcs.ipp +0 -36
- data/rice/detail/define_method_and_auto_wrap.hpp +0 -31
- data/rice/detail/define_method_and_auto_wrap.ipp +0 -30
- data/rice/detail/demangle.cpp +0 -56
- data/rice/detail/demangle.hpp +0 -19
- data/rice/detail/env.hpp +0 -11
- data/rice/detail/method_data.cpp +0 -92
- data/rice/detail/node.hpp +0 -13
- data/rice/detail/protect.cpp +0 -29
- data/rice/detail/protect.hpp +0 -34
- data/rice/detail/ruby_version_code.hpp +0 -6
- data/rice/detail/ruby_version_code.hpp.in +0 -6
- data/rice/detail/st.hpp +0 -22
- data/rice/detail/win32.hpp +0 -16
- data/rice/detail/wrap_function.hpp +0 -66
- data/rice/protect.hpp +0 -38
- data/rice/protect.ipp +0 -1134
- data/rice/rubypp.rb +0 -97
- data/rice/to_from_ruby.hpp +0 -8
- data/rice/to_from_ruby.ipp +0 -418
- data/rice/to_from_ruby_defn.hpp +0 -70
- data/ruby/Makefile.am +0 -1
- data/ruby/Makefile.in +0 -628
- data/ruby/lib/Makefile.am +0 -3
- data/ruby/lib/Makefile.in +0 -506
- data/ruby/lib/mkmf-rice.rb.in +0 -217
- data/ruby/lib/version.rb +0 -3
- data/ruby.ac +0 -135
- data/sample/Makefile.am +0 -53
- data/sample/Makefile.in +0 -495
- data/test/Makefile.am +0 -73
- data/test/Makefile.in +0 -1219
- data/test/ext/Makefile.am +0 -41
- data/test/ext/Makefile.in +0 -483
- data/test/test_rice.rb +0 -45
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#include <array>
|
|
2
|
+
#include <algorithm>
|
|
3
|
+
|
|
4
|
+
#include "rice_traits.hpp"
|
|
5
|
+
#include "method_data.hpp"
|
|
6
|
+
#include "to_ruby_defn.hpp"
|
|
7
|
+
#include "../ruby_try_catch.hpp"
|
|
8
|
+
|
|
9
|
+
namespace Rice::detail
|
|
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)
|
|
13
|
+
{
|
|
14
|
+
RUBY_TRY
|
|
15
|
+
{
|
|
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
|
+
}
|
|
20
|
+
RUBY_CATCH
|
|
21
|
+
}
|
|
22
|
+
|
|
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)
|
|
25
|
+
{
|
|
26
|
+
RUBY_TRY
|
|
27
|
+
{
|
|
28
|
+
using Native_Attr_T = NativeAttribute<Return_T, Attr_T, Self_T>;
|
|
29
|
+
Native_Attr_T* attr = detail::MethodData::data<Native_Attr_T*>();
|
|
30
|
+
return attr->write(self, value);
|
|
31
|
+
}
|
|
32
|
+
RUBY_CATCH
|
|
33
|
+
}
|
|
34
|
+
|
|
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)
|
|
38
|
+
{
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
template<typename Return_T, typename Attr_T, typename Self_T>
|
|
42
|
+
inline VALUE NativeAttribute<Return_T, Attr_T, Self_T>::read(VALUE self)
|
|
43
|
+
{
|
|
44
|
+
using Unqualified_T = remove_cv_recursive_t<Return_T>;
|
|
45
|
+
if constexpr (std::is_member_object_pointer_v<Attr_T>)
|
|
46
|
+
{
|
|
47
|
+
Self_T* nativeSelf = From_Ruby<Self_T*>().convert(self);
|
|
48
|
+
return To_Ruby<Unqualified_T>().convert(nativeSelf->*attr_);
|
|
49
|
+
}
|
|
50
|
+
else
|
|
51
|
+
{
|
|
52
|
+
return To_Ruby<Unqualified_T>().convert(*attr_);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
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)
|
|
58
|
+
{
|
|
59
|
+
if constexpr (!std::is_const_v<std::remove_pointer_t<Attr_T>> && std::is_member_object_pointer_v<Attr_T>)
|
|
60
|
+
{
|
|
61
|
+
Self_T* nativeSelf = From_Ruby<Self_T*>().convert(self);
|
|
62
|
+
nativeSelf->*attr_ = From_Ruby<Return_T>().convert(value);
|
|
63
|
+
}
|
|
64
|
+
else if constexpr (!std::is_const_v<std::remove_pointer_t<Attr_T>>)
|
|
65
|
+
{
|
|
66
|
+
*attr_ = From_Ruby<Return_T>().convert(value);
|
|
67
|
+
}
|
|
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
|
+
|
|
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);
|
|
82
|
+
}
|
|
83
|
+
} // Rice
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#ifndef Rice__detail__Native_Function__hpp_
|
|
2
|
+
#define Rice__detail__Native_Function__hpp_
|
|
3
|
+
|
|
4
|
+
#include "ruby.hpp"
|
|
5
|
+
#include "Exception_Handler_defn.hpp"
|
|
6
|
+
#include "MethodInfo.hpp"
|
|
7
|
+
#include "function_traits.hpp"
|
|
8
|
+
#include "from_ruby.hpp"
|
|
9
|
+
|
|
10
|
+
namespace Rice::detail
|
|
11
|
+
{
|
|
12
|
+
template<typename Function_T, bool IsMethod>
|
|
13
|
+
class NativeFunction
|
|
14
|
+
{
|
|
15
|
+
public:
|
|
16
|
+
// We remove const to avoid an explosion of To_Ruby specializations and Ruby doesn't
|
|
17
|
+
// have the concept of constants anyways
|
|
18
|
+
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;
|
|
20
|
+
using Arg_Ts = typename method_traits<Function_T, IsMethod>::Arg_Ts;
|
|
21
|
+
using From_Ruby_Ts = typename tuple_map<From_Ruby, Arg_Ts>::type;
|
|
22
|
+
|
|
23
|
+
// Static member function that Ruby calls
|
|
24
|
+
static VALUE call(int argc, VALUE* argv, VALUE self);
|
|
25
|
+
|
|
26
|
+
public:
|
|
27
|
+
NativeFunction(Function_T func, std::shared_ptr<Exception_Handler> handler, MethodInfo* methodInfo);
|
|
28
|
+
|
|
29
|
+
// Invokes the wrapped function
|
|
30
|
+
VALUE operator()(int argc, VALUE* argv, VALUE self);
|
|
31
|
+
|
|
32
|
+
private:
|
|
33
|
+
template<typename T, std::size_t I>
|
|
34
|
+
From_Ruby<T> createFromRuby();
|
|
35
|
+
|
|
36
|
+
// Create NativeArgs which are used to convert values from Ruby to C++
|
|
37
|
+
template<std::size_t...I>
|
|
38
|
+
From_Ruby_Ts createFromRuby(std::index_sequence<I...>& indices);
|
|
39
|
+
|
|
40
|
+
To_Ruby<Return_T> createToRuby();
|
|
41
|
+
|
|
42
|
+
// Convert Ruby argv pointer to Ruby values
|
|
43
|
+
std::vector<VALUE> getRubyValues(int argc, VALUE* argv);
|
|
44
|
+
|
|
45
|
+
// Convert Ruby values to C++ values
|
|
46
|
+
template<typename std::size_t...I>
|
|
47
|
+
Arg_Ts getNativeValues(std::vector<VALUE>& values, std::index_sequence<I...>& indices);
|
|
48
|
+
|
|
49
|
+
// Figure out what self is
|
|
50
|
+
Self_T getSelf(VALUE self);
|
|
51
|
+
|
|
52
|
+
// Do we need to keep alive any arguments?
|
|
53
|
+
void checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues);
|
|
54
|
+
|
|
55
|
+
// Call the underlying C++ function
|
|
56
|
+
VALUE invokeNativeFunction(Arg_Ts& nativeArgs);
|
|
57
|
+
VALUE invokeNativeMethod(VALUE self, Arg_Ts& nativeArgs);
|
|
58
|
+
|
|
59
|
+
private:
|
|
60
|
+
Function_T func_;
|
|
61
|
+
From_Ruby_Ts fromRubys_;
|
|
62
|
+
To_Ruby<Return_T> toRuby_;
|
|
63
|
+
std::shared_ptr<Exception_Handler> handler_;
|
|
64
|
+
std::unique_ptr<MethodInfo> methodInfo_;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
#include "NativeFunction.ipp"
|
|
68
|
+
|
|
69
|
+
#endif // Rice__detail__Native_Function__hpp_
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#include <array>
|
|
2
|
+
#include <algorithm>
|
|
3
|
+
#include <stdexcept>
|
|
4
|
+
|
|
5
|
+
#include "method_data.hpp"
|
|
6
|
+
#include "to_ruby_defn.hpp"
|
|
7
|
+
#include "../ruby_try_catch.hpp"
|
|
8
|
+
|
|
9
|
+
namespace Rice::detail
|
|
10
|
+
{
|
|
11
|
+
template<typename Function_T, bool IsMethod>
|
|
12
|
+
VALUE NativeFunction<Function_T, IsMethod>::call(int argc, VALUE* argv, VALUE self)
|
|
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);
|
|
17
|
+
}
|
|
18
|
+
|
|
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)
|
|
22
|
+
{
|
|
23
|
+
// Create a tuple of NativeArgs that will convert the Ruby values to native values. For
|
|
24
|
+
// builtin types NativeArgs will keep a copy of the native value so that it
|
|
25
|
+
// can be passed by reference or pointer to the native function. For non-builtin types
|
|
26
|
+
// it will just pass the value through.
|
|
27
|
+
auto indices = std::make_index_sequence<std::tuple_size_v<Arg_Ts>>{};
|
|
28
|
+
this->fromRubys_ = this->createFromRuby(indices);
|
|
29
|
+
|
|
30
|
+
this->toRuby_ = this->createToRuby();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
template<typename Function_T, bool IsMethod>
|
|
34
|
+
template<typename T, std::size_t I>
|
|
35
|
+
From_Ruby<T> NativeFunction<Function_T, IsMethod>::createFromRuby()
|
|
36
|
+
{
|
|
37
|
+
// Does the From_Ruby instantiation work with Arg?
|
|
38
|
+
if constexpr (std::is_constructible_v<From_Ruby<T>, Arg*>)
|
|
39
|
+
{
|
|
40
|
+
return From_Ruby<T>(&this->methodInfo_->arg(I));
|
|
41
|
+
}
|
|
42
|
+
else
|
|
43
|
+
{
|
|
44
|
+
return From_Ruby<T>();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
template<typename Function_T, bool IsMethod>
|
|
49
|
+
To_Ruby<typename NativeFunction<Function_T, IsMethod>::Return_T> NativeFunction<Function_T, IsMethod>::createToRuby()
|
|
50
|
+
{
|
|
51
|
+
// Does the From_Ruby instantiation work with ReturnInfo?
|
|
52
|
+
if constexpr (std::is_constructible_v<To_Ruby<Return_T>, Return*>)
|
|
53
|
+
{
|
|
54
|
+
return To_Ruby<Return_T>(&this->methodInfo_->returnInfo);
|
|
55
|
+
}
|
|
56
|
+
else
|
|
57
|
+
{
|
|
58
|
+
return To_Ruby<Return_T>();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
template<typename Function_T, bool IsMethod>
|
|
63
|
+
template<std::size_t... I>
|
|
64
|
+
typename NativeFunction<Function_T, IsMethod>::From_Ruby_Ts NativeFunction<Function_T, IsMethod>::createFromRuby(std::index_sequence<I...>& indices)
|
|
65
|
+
{
|
|
66
|
+
return std::make_tuple(createFromRuby<remove_cv_recursive_t<typename std::tuple_element<I, Arg_Ts>::type>, I>()...);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
template<typename Function_T, bool IsMethod>
|
|
70
|
+
std::vector<VALUE> NativeFunction<Function_T, IsMethod>::getRubyValues(int argc, VALUE* argv)
|
|
71
|
+
{
|
|
72
|
+
// Setup a tuple to contain required methodInfo to rb_scan_args
|
|
73
|
+
std::string scanFormat = this->methodInfo_->formatString();
|
|
74
|
+
std::tuple<int, VALUE*, const char*> rbScanMandatory = std::forward_as_tuple(argc, argv, scanFormat.c_str());
|
|
75
|
+
|
|
76
|
+
// Create a vector to store the variable number of Ruby Values
|
|
77
|
+
std::vector<VALUE> rbScanArgsOptional(std::tuple_size_v<Arg_Ts>, Qnil);
|
|
78
|
+
|
|
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(),
|
|
82
|
+
[](VALUE& value)
|
|
83
|
+
{
|
|
84
|
+
return &value;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Combine the tuples and call rb_scan_args
|
|
88
|
+
auto rbScanArgs = std::tuple_cat(rbScanMandatory, rbScanArgsOptionalPointers);
|
|
89
|
+
std::apply(rb_scan_args, rbScanArgs);
|
|
90
|
+
|
|
91
|
+
return rbScanArgsOptional;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
template<typename Function_T, bool IsMethod>
|
|
95
|
+
template<std::size_t... I>
|
|
96
|
+
typename NativeFunction<Function_T, IsMethod>::Arg_Ts NativeFunction<Function_T, IsMethod>::getNativeValues(std::vector<VALUE>& values,
|
|
97
|
+
std::index_sequence<I...>& indices)
|
|
98
|
+
{
|
|
99
|
+
// Convert each Ruby value to its native value by calling the appropriate fromRuby instance.
|
|
100
|
+
// Note that for fundamental types From_Ruby<Arg_Ts> will keep a copy of the native value
|
|
101
|
+
// so it can be passed by reference or pointer to a native function.
|
|
102
|
+
return std::forward_as_tuple(std::get<I>(this->fromRubys_).convert(values[I])...);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
template<typename Function_T, bool IsMethod>
|
|
106
|
+
typename NativeFunction<Function_T, IsMethod>::Self_T NativeFunction<Function_T, IsMethod>::getSelf(VALUE self)
|
|
107
|
+
{
|
|
108
|
+
// There is no self parameter
|
|
109
|
+
if constexpr (std::is_same_v<Self_T, std::nullptr_t>)
|
|
110
|
+
{
|
|
111
|
+
return nullptr;
|
|
112
|
+
}
|
|
113
|
+
// Self parameter is a Ruby VALUE so no conversion is needed
|
|
114
|
+
else if constexpr (std::is_same_v<Self_T, VALUE>)
|
|
115
|
+
{
|
|
116
|
+
return self;
|
|
117
|
+
}
|
|
118
|
+
// Self parameter could be derived from Object or it is an C++ instdance and
|
|
119
|
+
// needs to be unwrapped from Ruby
|
|
120
|
+
else
|
|
121
|
+
{
|
|
122
|
+
return From_Ruby<Self_T>().convert(self);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
template<typename Function_T, bool IsMethod>
|
|
127
|
+
VALUE NativeFunction<Function_T, IsMethod>::invokeNativeFunction(Arg_Ts& nativeArgs)
|
|
128
|
+
{
|
|
129
|
+
if constexpr (std::is_void_v<Return_T>)
|
|
130
|
+
{
|
|
131
|
+
std::apply(this->func_, nativeArgs);
|
|
132
|
+
return Qnil;
|
|
133
|
+
}
|
|
134
|
+
else
|
|
135
|
+
{
|
|
136
|
+
// Call the native method and get the result
|
|
137
|
+
Return_T nativeResult = std::apply(this->func_, nativeArgs);
|
|
138
|
+
|
|
139
|
+
// Return the result
|
|
140
|
+
return this->toRuby_.convert(nativeResult);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
template<typename Function_T, bool IsMethod>
|
|
145
|
+
VALUE NativeFunction<Function_T, IsMethod>::invokeNativeMethod(VALUE self, Arg_Ts& nativeArgs)
|
|
146
|
+
{
|
|
147
|
+
Self_T receiver = this->getSelf(self);
|
|
148
|
+
auto selfAndNativeArgs = std::tuple_cat(std::forward_as_tuple(receiver), nativeArgs);
|
|
149
|
+
|
|
150
|
+
if constexpr (std::is_void_v<Return_T>)
|
|
151
|
+
{
|
|
152
|
+
std::apply(this->func_, selfAndNativeArgs);
|
|
153
|
+
return Qnil;
|
|
154
|
+
}
|
|
155
|
+
else
|
|
156
|
+
{
|
|
157
|
+
Return_T nativeResult = (Return_T)std::apply(this->func_, selfAndNativeArgs);
|
|
158
|
+
|
|
159
|
+
// Special handling if the method returns self. If so we do not want
|
|
160
|
+
// 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>>)
|
|
162
|
+
{
|
|
163
|
+
if constexpr (std::is_pointer_v<Return_T> && std::is_pointer_v<Self_T>)
|
|
164
|
+
{
|
|
165
|
+
if (nativeResult == receiver)
|
|
166
|
+
return self;
|
|
167
|
+
}
|
|
168
|
+
else if constexpr (std::is_pointer_v<Return_T> && std::is_reference_v<Self_T>)
|
|
169
|
+
{
|
|
170
|
+
if (nativeResult == &receiver)
|
|
171
|
+
return self;
|
|
172
|
+
}
|
|
173
|
+
else if constexpr (std::is_reference_v<Return_T> && std::is_pointer_v<Self_T>)
|
|
174
|
+
{
|
|
175
|
+
if (&nativeResult == receiver)
|
|
176
|
+
return self;
|
|
177
|
+
}
|
|
178
|
+
else if constexpr (std::is_reference_v<Return_T> && std::is_reference_v<Self_T>)
|
|
179
|
+
{
|
|
180
|
+
if (&nativeResult == &receiver)
|
|
181
|
+
return self;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return this->toRuby_.convert(nativeResult);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
template<typename Function_T, bool IsMethod>
|
|
190
|
+
void NativeFunction<Function_T, IsMethod>::checkKeepAlive(VALUE self, VALUE returnValue, std::vector<VALUE>& rubyValues)
|
|
191
|
+
{
|
|
192
|
+
// Check function arguments
|
|
193
|
+
Wrapper* selfWrapper = getWrapper(self);
|
|
194
|
+
for (const Arg& arg : (*this->methodInfo_))
|
|
195
|
+
{
|
|
196
|
+
if (arg.isKeepAlive)
|
|
197
|
+
{
|
|
198
|
+
selfWrapper->addKeepAlive(rubyValues[arg.position]);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Check return value
|
|
203
|
+
if (this->methodInfo_->returnInfo.isKeepAlive)
|
|
204
|
+
{
|
|
205
|
+
Wrapper* returnWrapper = getWrapper(returnValue);
|
|
206
|
+
returnWrapper->addKeepAlive(self);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
template<typename Function_T, bool IsMethod>
|
|
211
|
+
VALUE NativeFunction<Function_T, IsMethod>::operator()(int argc, VALUE* argv, VALUE self)
|
|
212
|
+
{
|
|
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);
|
|
222
|
+
|
|
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
|
+
}
|
|
233
|
+
|
|
234
|
+
// Check if any function arguments or return values need to have their lifetimes tied to the receiver
|
|
235
|
+
this->checkKeepAlive(self, result, rubyValues);
|
|
236
|
+
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
catch (...)
|
|
240
|
+
{
|
|
241
|
+
RUBY_TRY
|
|
242
|
+
{
|
|
243
|
+
return this->handler_->handle_exception();
|
|
244
|
+
}
|
|
245
|
+
RUBY_CATCH
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#ifndef Rice__detail__ruby_function__hpp_
|
|
2
|
+
#define Rice__detail__ruby_function__hpp_
|
|
3
|
+
|
|
4
|
+
#include "ruby.hpp"
|
|
5
|
+
|
|
6
|
+
namespace Rice::detail
|
|
7
|
+
{
|
|
8
|
+
/* This is functor class that wraps calls to a Ruby C API method. It is needed because
|
|
9
|
+
rb_protect only supports calling methods that take one argument. Thus
|
|
10
|
+
we invoke rb_protect telling it to invoke Ruby_Function::call with an
|
|
11
|
+
instance of a Ruby_Function. That instance then in turn calls the original
|
|
12
|
+
Ruby method passing along its required arguments. */
|
|
13
|
+
|
|
14
|
+
template<typename Function_T, typename Return_T, typename...Arg_Ts>
|
|
15
|
+
class RubyFunction
|
|
16
|
+
{
|
|
17
|
+
public:
|
|
18
|
+
RubyFunction(Function_T func, const Arg_Ts&... args);
|
|
19
|
+
Return_T operator()();
|
|
20
|
+
|
|
21
|
+
private:
|
|
22
|
+
Function_T func_;
|
|
23
|
+
std::tuple<Arg_Ts...> args_;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
template<typename Return_T, typename ...Arg_Ts>
|
|
27
|
+
Return_T protect(Return_T(*func)(Arg_Ts...), Arg_Ts...args);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
namespace Rice
|
|
31
|
+
{
|
|
32
|
+
template<typename Return_T, typename ...Arg_Ts>
|
|
33
|
+
[[deprecated("Please use detail::protect")]]
|
|
34
|
+
Return_T protect(Return_T(*func)(Arg_Ts...), Arg_Ts...args);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
#include "RubyFunction.ipp"
|
|
38
|
+
|
|
39
|
+
#endif // Rice__detail__ruby_function__hpp_
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#include "Jump_Tag.hpp"
|
|
2
|
+
#include "../Exception_defn.hpp"
|
|
3
|
+
|
|
4
|
+
#include <any>
|
|
5
|
+
|
|
6
|
+
namespace Rice::detail
|
|
7
|
+
{
|
|
8
|
+
template<typename Function_T, typename Return_T, typename...Arg_Ts>
|
|
9
|
+
inline RubyFunction<Function_T, Return_T, Arg_Ts...>::RubyFunction(Function_T func, const Arg_Ts&... args)
|
|
10
|
+
: func_(func), args_(std::forward_as_tuple(args...))
|
|
11
|
+
{
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
template<typename Function_T, typename Return_T, typename...Arg_Ts>
|
|
15
|
+
inline Return_T RubyFunction<Function_T, Return_T, Arg_Ts...>::operator()()
|
|
16
|
+
{
|
|
17
|
+
const int TAG_RAISE = 0x6; // From Ruby header files
|
|
18
|
+
int state = 0;
|
|
19
|
+
|
|
20
|
+
// Setup a thread local variable to capture the result of the Ruby function call.
|
|
21
|
+
// We use thread_local because the lambda has to be captureless so it can
|
|
22
|
+
// be converted to a function pointer callable by C.
|
|
23
|
+
// The thread local variable avoids having to cast the result to VALUE and then
|
|
24
|
+
// back again to Return_T. The problem with that is the translation is not lossless
|
|
25
|
+
// in some cases - for example a double with value of -1.0 does not roundrip.
|
|
26
|
+
//
|
|
27
|
+
thread_local std::any result;
|
|
28
|
+
|
|
29
|
+
// Callback that will invoke the Ruby function
|
|
30
|
+
using Functor_T = RubyFunction<Function_T, Return_T, Arg_Ts...>;
|
|
31
|
+
auto callback = [](VALUE value)
|
|
32
|
+
{
|
|
33
|
+
Functor_T* functor = (Functor_T*)value;
|
|
34
|
+
|
|
35
|
+
if constexpr (std::is_same_v<Return_T, void>)
|
|
36
|
+
{
|
|
37
|
+
std::apply(functor->func_, functor->args_);
|
|
38
|
+
}
|
|
39
|
+
else
|
|
40
|
+
{
|
|
41
|
+
result = std::apply(functor->func_, functor->args_);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return Qnil;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Now call rb_protect which will invoke the callback lambda above
|
|
48
|
+
rb_protect(callback, (VALUE)this, &state);
|
|
49
|
+
|
|
50
|
+
// Did anything go wrong?
|
|
51
|
+
if (state == 0)
|
|
52
|
+
{
|
|
53
|
+
if constexpr (!std::is_same_v<Return_T, void>)
|
|
54
|
+
{
|
|
55
|
+
return std::any_cast<Return_T>(result);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else
|
|
59
|
+
{
|
|
60
|
+
VALUE err = rb_errinfo();
|
|
61
|
+
if (state == TAG_RAISE && RTEST(err))
|
|
62
|
+
{
|
|
63
|
+
rb_set_errinfo(Qnil);
|
|
64
|
+
throw Rice::Exception(err);
|
|
65
|
+
}
|
|
66
|
+
else
|
|
67
|
+
{
|
|
68
|
+
throw Jump_Tag(state);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Create a functor for calling a Ruby function and define some aliases for readability.
|
|
74
|
+
template<typename Return_T, typename ...Arg_Ts>
|
|
75
|
+
inline Return_T protect(Return_T(*func)(Arg_Ts...), Arg_Ts...args)
|
|
76
|
+
{
|
|
77
|
+
using Function_T = Return_T(*)(Arg_Ts...);
|
|
78
|
+
auto rubyFunction = RubyFunction<Function_T, Return_T, Arg_Ts...>(func, args...);
|
|
79
|
+
return rubyFunction();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
namespace Rice
|
|
84
|
+
{
|
|
85
|
+
template<typename Return_T, typename ...Arg_Ts>
|
|
86
|
+
inline Return_T protect(Return_T(*func)(Arg_Ts...), Arg_Ts...args)
|
|
87
|
+
{
|
|
88
|
+
using Function_T = Return_T(*)(Arg_Ts...);
|
|
89
|
+
auto rubyFunction = detail::RubyFunction<Function_T, Return_T, Arg_Ts...>(func, args...);
|
|
90
|
+
return rubyFunction();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#ifndef Rice__Type__hpp_
|
|
2
|
+
#define Rice__Type__hpp_
|
|
3
|
+
|
|
4
|
+
#include <string>
|
|
5
|
+
#include <typeinfo>
|
|
6
|
+
#include "rice_traits.hpp"
|
|
7
|
+
|
|
8
|
+
namespace Rice::detail
|
|
9
|
+
{
|
|
10
|
+
template<typename T>
|
|
11
|
+
struct Type
|
|
12
|
+
{
|
|
13
|
+
static bool verify();
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Return the name of a type
|
|
17
|
+
std::string typeName(const std::type_info& typeInfo);
|
|
18
|
+
std::string makeClassName(const std::type_info& typeInfo);
|
|
19
|
+
|
|
20
|
+
template<typename T>
|
|
21
|
+
void verifyType();
|
|
22
|
+
|
|
23
|
+
template<typename Tuple_T>
|
|
24
|
+
void verifyTypes();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
#include "Type.ipp"
|
|
28
|
+
|
|
29
|
+
#endif // Rice__Type__hpp_
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#include "rice_traits.hpp"
|
|
2
|
+
|
|
3
|
+
#include <iosfwd>
|
|
4
|
+
#include <numeric>
|
|
5
|
+
#include <regex>
|
|
6
|
+
#include <sstream>
|
|
7
|
+
#include <tuple>
|
|
8
|
+
|
|
9
|
+
#ifdef __GNUC__
|
|
10
|
+
#include <cxxabi.h>
|
|
11
|
+
#include <cstdlib>
|
|
12
|
+
#include <cstring>
|
|
13
|
+
#endif
|
|
14
|
+
|
|
15
|
+
namespace Rice::detail
|
|
16
|
+
{
|
|
17
|
+
template<>
|
|
18
|
+
struct Type<void>
|
|
19
|
+
{
|
|
20
|
+
static bool verify()
|
|
21
|
+
{
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
template<typename T>
|
|
27
|
+
void verifyType()
|
|
28
|
+
{
|
|
29
|
+
Type<intrinsic_type<T>>::verify();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
template<typename Tuple_T, size_t...Is>
|
|
33
|
+
void verifyTypesImpl()
|
|
34
|
+
{
|
|
35
|
+
(Type<intrinsic_type<typename std::tuple_element<Is, Tuple_T>::type>>::verify(), ...);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
template<typename Tuple_T>
|
|
39
|
+
void verifyTypes()
|
|
40
|
+
{
|
|
41
|
+
if constexpr (std::tuple_size<Tuple_T>::value > 0)
|
|
42
|
+
{
|
|
43
|
+
verifyTypesImpl<Tuple_T, std::tuple_size<Tuple_T>::value - 1>();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
inline std::string demangle(char const* mangled_name)
|
|
48
|
+
{
|
|
49
|
+
#ifdef __GNUC__
|
|
50
|
+
struct Helper
|
|
51
|
+
{
|
|
52
|
+
Helper(
|
|
53
|
+
char const* mangled_name)
|
|
54
|
+
: name_(0)
|
|
55
|
+
{
|
|
56
|
+
int status = 0;
|
|
57
|
+
name_ = abi::__cxa_demangle(mangled_name, 0, 0, &status);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
~Helper()
|
|
61
|
+
{
|
|
62
|
+
std::free(name_);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
char* name_;
|
|
66
|
+
|
|
67
|
+
private:
|
|
68
|
+
Helper(Helper const&);
|
|
69
|
+
void operator=(Helper const&);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
Helper helper(mangled_name);
|
|
73
|
+
if (helper.name_)
|
|
74
|
+
{
|
|
75
|
+
return helper.name_;
|
|
76
|
+
}
|
|
77
|
+
else
|
|
78
|
+
{
|
|
79
|
+
return mangled_name;
|
|
80
|
+
}
|
|
81
|
+
#else
|
|
82
|
+
return mangled_name;
|
|
83
|
+
#endif
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
inline std::string typeName(const std::type_info& typeInfo)
|
|
87
|
+
{
|
|
88
|
+
return demangle(typeInfo.name());
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
inline std::string makeClassName(const std::type_info& typeInfo)
|
|
92
|
+
{
|
|
93
|
+
std::string base = demangle(typeInfo.name());
|
|
94
|
+
|
|
95
|
+
// Remove class keyword
|
|
96
|
+
auto classRegex = std::regex("class +");
|
|
97
|
+
base = std::regex_replace(base, classRegex, "");
|
|
98
|
+
|
|
99
|
+
// Remove struct keyword
|
|
100
|
+
auto structRegex = std::regex("struct +");
|
|
101
|
+
base = std::regex_replace(base, structRegex, "");
|
|
102
|
+
|
|
103
|
+
// Remove std::__[^:]*::
|
|
104
|
+
auto stdClangRegex = std::regex("std::__[^:]+::");
|
|
105
|
+
base = std::regex_replace(base, stdClangRegex, "");
|
|
106
|
+
|
|
107
|
+
// Remove std::
|
|
108
|
+
auto stdRegex = std::regex("std::");
|
|
109
|
+
base = std::regex_replace(base, stdRegex, "");
|
|
110
|
+
|
|
111
|
+
// Replace > >
|
|
112
|
+
auto trailingAngleBracketSpaceRegex = std::regex(" >");
|
|
113
|
+
base = std::regex_replace(base, trailingAngleBracketSpaceRegex, ">");
|
|
114
|
+
|
|
115
|
+
// Replace < and >
|
|
116
|
+
auto angleBracketRegex = std::regex("<|>");
|
|
117
|
+
base = std::regex_replace(base, angleBracketRegex, "__");
|
|
118
|
+
|
|
119
|
+
// Replace ,
|
|
120
|
+
auto commaRegex = std::regex(", *");
|
|
121
|
+
base = std::regex_replace(base, commaRegex, "_");
|
|
122
|
+
|
|
123
|
+
// Now create a vector of strings split on whitespace
|
|
124
|
+
std::istringstream stream(base);
|
|
125
|
+
std::vector<std::string> words{ std::istream_iterator<std::string>{stream},
|
|
126
|
+
std::istream_iterator<std::string>{} };
|
|
127
|
+
|
|
128
|
+
std::string result = std::accumulate(words.begin(), words.end(), std::string(),
|
|
129
|
+
[](const std::string& memo, const std::string& word) -> std::string
|
|
130
|
+
{
|
|
131
|
+
std::string capitalized = word;
|
|
132
|
+
capitalized[0] = toupper(capitalized[0]);
|
|
133
|
+
return memo + capitalized;
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
}
|