Skip to content

Commit f8431af

Browse files
committed
Allow to use std::shared_ptr with wrapped class arguments
Fixed a bug to use a reference or shared_ptr to wrapped class as a function argument. Now have to supply `use_shared_ptr` boolean template argument to `call_from_v8` for proper `class_<T, use_shared_ptr>` usage on function argument unwrapping. Added a `convert<T, ref_from_shared_ptr>` specialization to use a reference as a function argument for wrapped with `std::shared_ptr<T>` classes. Added test cases.
1 parent d7b4482 commit f8431af

6 files changed

+96
-30
lines changed

test/test_call_from_v8.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ void w(v8::FunctionCallbackInfo<v8::Value> const& args)
3232
return args.GetReturnValue().Set(args.Length());
3333
}
3434

35+
struct X {};
36+
void class_ref(X&) {}
37+
void class_ptr(X*) {}
38+
void class_sptr(std::shared_ptr<X>) {}
39+
3540
using v8pp::detail::select_call_traits;
3641
using v8pp::detail::call_from_v8_traits;
3742
using v8pp::detail::isolate_arg_call_traits;
@@ -60,6 +65,14 @@ static_assert(std::is_same<select_call_traits<decltype(&z)>,
6065
static_assert(std::is_same<select_call_traits<decltype(&w)>,
6166
v8_args_call_traits<decltype(&w) >> ::value, "");
6267

68+
static_assert(std::is_same<select_call_traits<decltype(&class_ptr), false>::arg_convert<0>::from_type, X*>::value, "class ptr");
69+
static_assert(std::is_same<select_call_traits<decltype(&class_ref), false>::arg_convert<0>::from_type, X&>::value, "class ref");
70+
static_assert(std::is_same<select_call_traits<decltype(&class_sptr), false>::arg_convert<0>::from_type, std::shared_ptr<X>>::value, "class shared_ptr");
71+
72+
static_assert(std::is_same<select_call_traits<decltype(&class_ptr), true>::arg_convert<0>::from_type, std::shared_ptr<X>>::value, "class ptr");
73+
static_assert(std::is_same<select_call_traits<decltype(&class_ref), true>::arg_convert<0>::from_type, X&>::value, "class ref");
74+
static_assert(std::is_same<select_call_traits<decltype(&class_sptr), true>::arg_convert<0>::from_type, std::shared_ptr<X>>::value, "class shared_ptr");
75+
6376
} // unnamed namespace
6477

6578
void test_call_from_v8()

test/test_class.cpp

+8-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ struct Y : X
4444

4545
explicit Y(int x) { var = x; ++instance_count; }
4646
~Y() { --instance_count; }
47+
48+
int useX(X& x) { return var + x.var; }
49+
50+
template<bool use_shared_ptr, typename X_ptr = typename v8pp::class_<X, use_shared_ptr>::object_pointer_type>
51+
int useX_ptr(X_ptr x) { return var + x->var; }
4752
};
4853

4954
int Y::instance_count = 0;
@@ -112,6 +117,8 @@ void test_class_()
112117
Y_class
113118
.template inherit<X>()
114119
.template ctor<int>()
120+
.set("useX", &Y::useX)
121+
.set("useX_ptr", &Y::useX_ptr<use_shared_ptr>)
115122
;
116123

117124
check_ex<std::runtime_error>("already wrapped class X", [isolate]()
@@ -167,7 +174,7 @@ void test_class_()
167174
check("y3_obj", v8pp::to_v8(isolate, y3) == y3_obj);
168175
check_eq("y3.var", y3->var, -3);
169176

170-
run_script<int>(context, "for (i = 0; i < 10; ++i) new Y(i); i");
177+
run_script<int>(context, "x = new X; for (i = 0; i < 10; ++i) { y = new Y(i); y.useX(x); y.useX_ptr(x); }");
171178
check_eq("Y count", Y::instance_count, 10 + 4); // 10 + y + y1 + y2 + y3
172179
run_script<int>(context, "y = null; 0");
173180

v8pp/call_from_v8.hpp

+33-22
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
namespace v8pp { namespace detail {
2020

21-
template<typename F, size_t Offset = 0>
21+
template<typename F, bool use_shared_ptr = false, size_t Offset = 0>
2222
struct call_from_v8_traits
2323
{
2424
static bool const is_mem_fun = std::is_member_function_pointer<F>::value;
@@ -43,15 +43,24 @@ struct call_from_v8_traits
4343
using arg_type = typename tuple_element<Index + is_mem_fun,
4444
Index < (arg_count + Offset)>::type;
4545

46-
template<size_t Index>
47-
using convert_type = decltype(convert<arg_type<Index>>::from_v8(
48-
std::declval<v8::Isolate*>(), std::declval<v8::Handle<v8::Value>>()));
46+
template<size_t Index, typename Arg = arg_type<Index>,
47+
typename T = typename std::remove_reference<Arg>::type,
48+
typename U = typename std::remove_pointer<T>::type
49+
>
50+
using arg_convert = typename std::conditional<
51+
is_wrapped_class<U>::value && use_shared_ptr,
52+
typename std::conditional<std::is_pointer<T>::value,
53+
convert<std::shared_ptr<U>>,
54+
convert<U, ref_from_shared_ptr>
55+
>::type,
56+
convert<Arg>
57+
>::type;
4958

5059
template<size_t Index>
51-
static convert_type<Index>
60+
static decltype(arg_convert<Index>::from_v8(std::declval<v8::Isolate*>(), std::declval<v8::Handle<v8::Value>>()))
5261
arg_from_v8(v8::FunctionCallbackInfo<v8::Value> const& args)
5362
{
54-
return convert<arg_type<Index>>::from_v8(args.GetIsolate(), args[Index - Offset]);
63+
return arg_convert<Index>::from_v8(args.GetIsolate(), args[Index - Offset]);
5564
}
5665

5766
static void check(v8::FunctionCallbackInfo<v8::Value> const& args)
@@ -63,11 +72,11 @@ struct call_from_v8_traits
6372
}
6473
};
6574

66-
template<typename F>
67-
using isolate_arg_call_traits = call_from_v8_traits<F, 1>;
75+
template<typename F, bool use_shared_ptr = false>
76+
using isolate_arg_call_traits = call_from_v8_traits<F, use_shared_ptr, 1>;
6877

6978
template<typename F, size_t Offset = 0>
70-
struct v8_args_call_traits : call_from_v8_traits<F, Offset>
79+
struct v8_args_call_traits : call_from_v8_traits<F, false, Offset>
7180
{
7281
template<size_t Index>
7382
using arg_type = v8::FunctionCallbackInfo<v8::Value> const&;
@@ -102,12 +111,14 @@ using is_first_arg_isolate = std::integral_constant<bool,
102111
std::is_same<typename call_from_v8_traits<F>::template arg_type<0>,
103112
v8::Isolate*>::value>;
104113

105-
template<typename F>
114+
template<typename F, bool use_shared_ptr = false>
106115
using select_call_traits = typename std::conditional<is_first_arg_isolate<F>::value,
107116
typename std::conditional<is_direct_args<F, 1>::value,
108-
isolate_v8_args_call_traits<F>, isolate_arg_call_traits<F>>::type,
117+
isolate_v8_args_call_traits<F>,
118+
isolate_arg_call_traits<F, use_shared_ptr>>::type,
109119
typename std::conditional<is_direct_args<F, 0>::value,
110-
v8_args_call_traits<F>, call_from_v8_traits<F>>::type
120+
v8_args_call_traits<F>,
121+
call_from_v8_traits<F, use_shared_ptr>>::type
111122
>::type;
112123

113124
template<typename F, typename CallTraits, size_t ...Indices>
@@ -126,22 +137,22 @@ call_from_v8_impl(T& obj, F&& func, v8::FunctionCallbackInfo<v8::Value> const& a
126137
return (obj.*func)(CallTraits::template arg_from_v8<Indices>(args)...);
127138
}
128139

129-
template<typename F, size_t ...Indices>
140+
template<typename F, bool use_shared_ptr, size_t ...Indices>
130141
typename function_traits<F>::return_type
131142
call_from_v8_impl(F&& func, v8::FunctionCallbackInfo<v8::Value> const& args,
132-
isolate_arg_call_traits<F>, index_sequence<Indices...>)
143+
isolate_arg_call_traits<F, use_shared_ptr>, index_sequence<Indices...>)
133144
{
134145
return func(args.GetIsolate(),
135-
isolate_arg_call_traits<F>::template arg_from_v8<Indices + 1>(args)...);
146+
isolate_arg_call_traits<F, use_shared_ptr>::template arg_from_v8<Indices + 1>(args)...);
136147
}
137148

138-
template<typename T, typename F, size_t ...Indices>
149+
template<typename T, bool use_shared_ptr, typename F, size_t ...Indices>
139150
typename function_traits<F>::return_type
140151
call_from_v8_impl(T& obj, F&& func, v8::FunctionCallbackInfo<v8::Value> const& args,
141-
isolate_arg_call_traits<F>, index_sequence<Indices...>)
152+
isolate_arg_call_traits<F, use_shared_ptr>, index_sequence<Indices...>)
142153
{
143154
return (obj.*func)(args.GetIsolate(),
144-
isolate_arg_call_traits<F>::template arg_from_v8<Indices + 1>(args)...);
155+
isolate_arg_call_traits<F, use_shared_ptr>::template arg_from_v8<Indices + 1>(args)...);
145156
}
146157

147158
template<typename F, size_t ...Indices>
@@ -160,21 +171,21 @@ call_from_v8_impl(T& obj, F&& func, v8::FunctionCallbackInfo<v8::Value> const& a
160171
return (obj.*func)(args.GetIsolate(), args);
161172
}
162173

163-
template<typename F>
174+
template<typename F, bool use_shared_ptr>
164175
typename function_traits<F>::return_type
165176
call_from_v8(F&& func, v8::FunctionCallbackInfo<v8::Value> const& args)
166177
{
167-
using call_traits = select_call_traits<F>;
178+
using call_traits = select_call_traits<F, use_shared_ptr>;
168179
call_traits::check(args);
169180
return call_from_v8_impl(std::forward<F>(func), args,
170181
call_traits(), make_index_sequence<call_traits::arg_count>());
171182
}
172183

173-
template<typename T, typename F>
184+
template<typename T, typename F, bool use_shared_ptr>
174185
typename function_traits<F>::return_type
175186
call_from_v8(T& obj, F&& func, v8::FunctionCallbackInfo<v8::Value> const& args)
176187
{
177-
using call_traits = select_call_traits<F>;
188+
using call_traits = select_call_traits<F, use_shared_ptr>;
178189
call_traits::check(args);
179190
return call_from_v8_impl(obj, std::forward<F>(func), args,
180191
call_traits(), make_index_sequence<call_traits::arg_count>());

v8pp/class.hpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -553,8 +553,8 @@ class class_
553553
static object_pointer_type call(v8::FunctionCallbackInfo<v8::Value> const& args)
554554
{
555555
using ctor_function = object_pointer_type(*)(v8::Isolate* isolate, Args...);
556-
return detail::call_from_v8(static_cast<ctor_function>(
557-
&factory<T, use_shared_ptr>::create), args);
556+
return detail::call_from_v8<ctor_function, use_shared_ptr>(
557+
&factory<T, use_shared_ptr>::create, args);
558558
}
559559
};
560560

v8pp/convert.hpp

+36-2
Original file line numberDiff line numberDiff line change
@@ -594,8 +594,7 @@ struct convert<T, typename std::enable_if<is_wrapped_class<T>::value>::type>
594594
};
595595

596596
template<typename T>
597-
struct convert<std::shared_ptr<T>,
598-
typename std::enable_if<is_wrapped_class<T>::value>::type>
597+
struct convert<std::shared_ptr<T>, typename std::enable_if<is_wrapped_class<T>::value>::type>
599598
{
600599
using from_type = std::shared_ptr<T>;
601600
using to_type = v8::Handle<v8::Object>;
@@ -621,6 +620,41 @@ struct convert<std::shared_ptr<T>,
621620
}
622621
};
623622

623+
struct ref_from_shared_ptr {};
624+
625+
template<typename T>
626+
struct convert<T, ref_from_shared_ptr>
627+
{
628+
using from_type = T&;
629+
using to_type = v8::Handle<v8::Object>;
630+
631+
static bool is_valid(v8::Isolate* isolate, v8::Handle<v8::Value> value)
632+
{
633+
return convert<std::shared_ptr<T>>::is_valid(isolate, value);
634+
}
635+
636+
static from_type from_v8(v8::Isolate* isolate, v8::Handle<v8::Value> value)
637+
{
638+
if (!is_valid(isolate, value))
639+
{
640+
throw std::invalid_argument("expected Object");
641+
}
642+
if (std::shared_ptr<T> object = convert<std::shared_ptr<T>>::from_v8(isolate, value))
643+
{
644+
// assert(object.use_count() > 1);
645+
return *object;
646+
}
647+
throw std::runtime_error("failed to unwrap C++ object");
648+
}
649+
650+
static to_type to_v8(v8::Isolate* isolate, T const& value)
651+
{
652+
v8::Handle<v8::Object> result = convert<std::shared_ptr<T>>::to_v8(isolate, &value);
653+
if (!result.IsEmpty()) return result;
654+
throw std::runtime_error("failed to wrap C++ object");
655+
}
656+
};
657+
624658
template<typename T>
625659
struct convert<T&> : convert<T> {};
626660

v8pp/function.hpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ typename std::enable_if<is_callable<F>::value,
122122
typename function_traits<F>::return_type>::type
123123
invoke(v8::FunctionCallbackInfo<v8::Value> const& args)
124124
{
125-
return call_from_v8(std::forward<F>(get_external_data<F>(args.Data())), args);
125+
return call_from_v8<F, use_shared_ptr>(std::forward<F>(get_external_data<F>(args.Data())), args);
126126
}
127127

128128
template<typename F, bool use_shared_ptr>
@@ -137,8 +137,9 @@ invoke(v8::FunctionCallbackInfo<v8::Value> const& args)
137137

138138
v8::Isolate* isolate = args.GetIsolate();
139139
v8::Local<v8::Object> obj = args.This();
140-
return call_from_v8(*class_<class_type, use_shared_ptr>::unwrap_object(isolate, obj),
141-
std::forward<F>(get_external_data<F>(args.Data())), args);
140+
return call_from_v8<class_type, F, use_shared_ptr>(
141+
*class_<class_type, use_shared_ptr>::unwrap_object(isolate, obj),
142+
std::forward<F>(get_external_data<F>(args.Data())), args);
142143
}
143144

144145
template<typename F, bool use_shared_ptr>

0 commit comments

Comments
 (0)