Skip to content

Commit

Permalink
Allow to use std::shared_ptr with wrapped class arguments
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
pmed committed Oct 29, 2017
1 parent d7b4482 commit f8431af
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 30 deletions.
13 changes: 13 additions & 0 deletions test/test_call_from_v8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ void w(v8::FunctionCallbackInfo<v8::Value> const& args)
return args.GetReturnValue().Set(args.Length());
}

struct X {};
void class_ref(X&) {}
void class_ptr(X*) {}
void class_sptr(std::shared_ptr<X>) {}

using v8pp::detail::select_call_traits;
using v8pp::detail::call_from_v8_traits;
using v8pp::detail::isolate_arg_call_traits;
Expand Down Expand Up @@ -60,6 +65,14 @@ static_assert(std::is_same<select_call_traits<decltype(&z)>,
static_assert(std::is_same<select_call_traits<decltype(&w)>,
v8_args_call_traits<decltype(&w) >> ::value, "");

static_assert(std::is_same<select_call_traits<decltype(&class_ptr), false>::arg_convert<0>::from_type, X*>::value, "class ptr");
static_assert(std::is_same<select_call_traits<decltype(&class_ref), false>::arg_convert<0>::from_type, X&>::value, "class ref");
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");

static_assert(std::is_same<select_call_traits<decltype(&class_ptr), true>::arg_convert<0>::from_type, std::shared_ptr<X>>::value, "class ptr");
static_assert(std::is_same<select_call_traits<decltype(&class_ref), true>::arg_convert<0>::from_type, X&>::value, "class ref");
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");

} // unnamed namespace

void test_call_from_v8()
Expand Down
9 changes: 8 additions & 1 deletion test/test_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ struct Y : X

explicit Y(int x) { var = x; ++instance_count; }
~Y() { --instance_count; }

int useX(X& x) { return var + x.var; }

template<bool use_shared_ptr, typename X_ptr = typename v8pp::class_<X, use_shared_ptr>::object_pointer_type>
int useX_ptr(X_ptr x) { return var + x->var; }
};

int Y::instance_count = 0;
Expand Down Expand Up @@ -112,6 +117,8 @@ void test_class_()
Y_class
.template inherit<X>()
.template ctor<int>()
.set("useX", &Y::useX)
.set("useX_ptr", &Y::useX_ptr<use_shared_ptr>)
;

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

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

Expand Down
55 changes: 33 additions & 22 deletions v8pp/call_from_v8.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

namespace v8pp { namespace detail {

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

template<size_t Index>
using convert_type = decltype(convert<arg_type<Index>>::from_v8(
std::declval<v8::Isolate*>(), std::declval<v8::Handle<v8::Value>>()));
template<size_t Index, typename Arg = arg_type<Index>,
typename T = typename std::remove_reference<Arg>::type,
typename U = typename std::remove_pointer<T>::type
>
using arg_convert = typename std::conditional<
is_wrapped_class<U>::value && use_shared_ptr,
typename std::conditional<std::is_pointer<T>::value,
convert<std::shared_ptr<U>>,
convert<U, ref_from_shared_ptr>
>::type,
convert<Arg>
>::type;

template<size_t Index>
static convert_type<Index>
static decltype(arg_convert<Index>::from_v8(std::declval<v8::Isolate*>(), std::declval<v8::Handle<v8::Value>>()))
arg_from_v8(v8::FunctionCallbackInfo<v8::Value> const& args)
{
return convert<arg_type<Index>>::from_v8(args.GetIsolate(), args[Index - Offset]);
return arg_convert<Index>::from_v8(args.GetIsolate(), args[Index - Offset]);
}

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

template<typename F>
using isolate_arg_call_traits = call_from_v8_traits<F, 1>;
template<typename F, bool use_shared_ptr = false>
using isolate_arg_call_traits = call_from_v8_traits<F, use_shared_ptr, 1>;

template<typename F, size_t Offset = 0>
struct v8_args_call_traits : call_from_v8_traits<F, Offset>
struct v8_args_call_traits : call_from_v8_traits<F, false, Offset>
{
template<size_t Index>
using arg_type = v8::FunctionCallbackInfo<v8::Value> const&;
Expand Down Expand Up @@ -102,12 +111,14 @@ using is_first_arg_isolate = std::integral_constant<bool,
std::is_same<typename call_from_v8_traits<F>::template arg_type<0>,
v8::Isolate*>::value>;

template<typename F>
template<typename F, bool use_shared_ptr = false>
using select_call_traits = typename std::conditional<is_first_arg_isolate<F>::value,
typename std::conditional<is_direct_args<F, 1>::value,
isolate_v8_args_call_traits<F>, isolate_arg_call_traits<F>>::type,
isolate_v8_args_call_traits<F>,
isolate_arg_call_traits<F, use_shared_ptr>>::type,
typename std::conditional<is_direct_args<F, 0>::value,
v8_args_call_traits<F>, call_from_v8_traits<F>>::type
v8_args_call_traits<F>,
call_from_v8_traits<F, use_shared_ptr>>::type
>::type;

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

template<typename F, size_t ...Indices>
template<typename F, bool use_shared_ptr, size_t ...Indices>
typename function_traits<F>::return_type
call_from_v8_impl(F&& func, v8::FunctionCallbackInfo<v8::Value> const& args,
isolate_arg_call_traits<F>, index_sequence<Indices...>)
isolate_arg_call_traits<F, use_shared_ptr>, index_sequence<Indices...>)
{
return func(args.GetIsolate(),
isolate_arg_call_traits<F>::template arg_from_v8<Indices + 1>(args)...);
isolate_arg_call_traits<F, use_shared_ptr>::template arg_from_v8<Indices + 1>(args)...);
}

template<typename T, typename F, size_t ...Indices>
template<typename T, bool use_shared_ptr, typename F, size_t ...Indices>
typename function_traits<F>::return_type
call_from_v8_impl(T& obj, F&& func, v8::FunctionCallbackInfo<v8::Value> const& args,
isolate_arg_call_traits<F>, index_sequence<Indices...>)
isolate_arg_call_traits<F, use_shared_ptr>, index_sequence<Indices...>)
{
return (obj.*func)(args.GetIsolate(),
isolate_arg_call_traits<F>::template arg_from_v8<Indices + 1>(args)...);
isolate_arg_call_traits<F, use_shared_ptr>::template arg_from_v8<Indices + 1>(args)...);
}

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

template<typename F>
template<typename F, bool use_shared_ptr>
typename function_traits<F>::return_type
call_from_v8(F&& func, v8::FunctionCallbackInfo<v8::Value> const& args)
{
using call_traits = select_call_traits<F>;
using call_traits = select_call_traits<F, use_shared_ptr>;
call_traits::check(args);
return call_from_v8_impl(std::forward<F>(func), args,
call_traits(), make_index_sequence<call_traits::arg_count>());
}

template<typename T, typename F>
template<typename T, typename F, bool use_shared_ptr>
typename function_traits<F>::return_type
call_from_v8(T& obj, F&& func, v8::FunctionCallbackInfo<v8::Value> const& args)
{
using call_traits = select_call_traits<F>;
using call_traits = select_call_traits<F, use_shared_ptr>;
call_traits::check(args);
return call_from_v8_impl(obj, std::forward<F>(func), args,
call_traits(), make_index_sequence<call_traits::arg_count>());
Expand Down
4 changes: 2 additions & 2 deletions v8pp/class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -553,8 +553,8 @@ class class_
static object_pointer_type call(v8::FunctionCallbackInfo<v8::Value> const& args)
{
using ctor_function = object_pointer_type(*)(v8::Isolate* isolate, Args...);
return detail::call_from_v8(static_cast<ctor_function>(
&factory<T, use_shared_ptr>::create), args);
return detail::call_from_v8<ctor_function, use_shared_ptr>(
&factory<T, use_shared_ptr>::create, args);
}
};

Expand Down
38 changes: 36 additions & 2 deletions v8pp/convert.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -594,8 +594,7 @@ struct convert<T, typename std::enable_if<is_wrapped_class<T>::value>::type>
};

template<typename T>
struct convert<std::shared_ptr<T>,
typename std::enable_if<is_wrapped_class<T>::value>::type>
struct convert<std::shared_ptr<T>, typename std::enable_if<is_wrapped_class<T>::value>::type>
{
using from_type = std::shared_ptr<T>;
using to_type = v8::Handle<v8::Object>;
Expand All @@ -621,6 +620,41 @@ struct convert<std::shared_ptr<T>,
}
};

struct ref_from_shared_ptr {};

template<typename T>
struct convert<T, ref_from_shared_ptr>
{
using from_type = T&;
using to_type = v8::Handle<v8::Object>;

static bool is_valid(v8::Isolate* isolate, v8::Handle<v8::Value> value)
{
return convert<std::shared_ptr<T>>::is_valid(isolate, value);
}

static from_type from_v8(v8::Isolate* isolate, v8::Handle<v8::Value> value)
{
if (!is_valid(isolate, value))
{
throw std::invalid_argument("expected Object");
}
if (std::shared_ptr<T> object = convert<std::shared_ptr<T>>::from_v8(isolate, value))
{
// assert(object.use_count() > 1);
return *object;
}
throw std::runtime_error("failed to unwrap C++ object");
}

static to_type to_v8(v8::Isolate* isolate, T const& value)
{
v8::Handle<v8::Object> result = convert<std::shared_ptr<T>>::to_v8(isolate, &value);
if (!result.IsEmpty()) return result;
throw std::runtime_error("failed to wrap C++ object");
}
};

template<typename T>
struct convert<T&> : convert<T> {};

Expand Down
7 changes: 4 additions & 3 deletions v8pp/function.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ typename std::enable_if<is_callable<F>::value,
typename function_traits<F>::return_type>::type
invoke(v8::FunctionCallbackInfo<v8::Value> const& args)
{
return call_from_v8(std::forward<F>(get_external_data<F>(args.Data())), args);
return call_from_v8<F, use_shared_ptr>(std::forward<F>(get_external_data<F>(args.Data())), args);
}

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

v8::Isolate* isolate = args.GetIsolate();
v8::Local<v8::Object> obj = args.This();
return call_from_v8(*class_<class_type, use_shared_ptr>::unwrap_object(isolate, obj),
std::forward<F>(get_external_data<F>(args.Data())), args);
return call_from_v8<class_type, F, use_shared_ptr>(
*class_<class_type, use_shared_ptr>::unwrap_object(isolate, obj),
std::forward<F>(get_external_data<F>(args.Data())), args);
}

template<typename F, bool use_shared_ptr>
Expand Down

0 comments on commit f8431af

Please sign in to comment.