Skip to content

Commit

Permalink
allow auto wrapping temporary C++ objects (implementation of #113)
Browse files Browse the repository at this point in the history
Added `class_::auto_wrap_objects()` function to set an `auto_wrap_objects_`
flag in the `object_registry` for a wrapped class.

Added `class_::find_object(obj)` overloaded function for an object reference, to
create a clone of the `obj` with `Traits::clone()` and to wrap the cloned object
for the JavaScript side, if no such an instance was found.

Added `clone(T const& src)` template function into `raw_ptr_traits, `shared_ptr_traits`,
to create a copy of `src` with a copy constructor of class T.

Using the added `class_::find_object(obj)` overloading in `convert<T>` specializations
for a wrapped class T references, both for `raw_ptr_traits and `shared_ptr_traits`.

Added a simple test case with a function returning an unwrapped C++ object, and
accessing it from JavaScript. The returned objects shall be valid in JavaScript,
since the `vpp8:class_.auto_wrap_objects(true)` for the test class.
  • Loading branch information
pmed committed Jul 21, 2019
1 parent d7ed5a3 commit 8ab2770
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 7 deletions.
30 changes: 30 additions & 0 deletions test/test_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,33 @@ void test_const_instance_in_module()
check_eq("xconst.f()", run_script<int>(context, "api.xconst.f(1)"), 1);
}

template<typename Traits>
void test_auto_wrap_objects()
{
struct X
{
int x;
explicit X(int x) : x(x) {}
int get_x() const { return x; }
};

v8pp::context context;
v8::Isolate* isolate = context.isolate();
v8::HandleScope scope(isolate);

v8pp::class_<X, Traits> X_class(isolate);
X_class
.template ctor<int>()
.auto_wrap_objects(true)
.set("x", v8pp::property(&X::get_x));

auto f0 = [](int x) { return X(x); };
auto f = v8pp::wrap_function<decltype(f0), Traits>(isolate, "f", std::move(f0));
context.set("X", X_class);
context.set("f", f);
check_eq("return X object", run_script<int>(context, "obj = f(123); obj.x"), 123);
}

void test_class()
{
test_class_<v8pp::raw_ptr_traits>();
Expand All @@ -417,4 +444,7 @@ void test_class()

test_const_instance_in_module<v8pp::raw_ptr_traits>();
test_const_instance_in_module<v8pp::shared_ptr_traits>();

test_auto_wrap_objects<v8pp::raw_ptr_traits>();
test_auto_wrap_objects<v8pp::shared_ptr_traits>();
}
29 changes: 29 additions & 0 deletions v8pp/class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ class object_registry final : public class_info
return to_local(isolate_, js_func_);
}

void set_auto_wrap_objects(bool auto_wrap) { auto_wrap_objects_ = auto_wrap; }
bool auto_wrap_objects() const { return auto_wrap_objects_; }

void set_ctor(ctor_function&& ctor) { ctor_ = std::move(ctor); }

void add_base(object_registry& info, cast_function cast);
Expand Down Expand Up @@ -118,6 +121,7 @@ class object_registry final : public class_info

ctor_function ctor_;
dtor_function dtor_;
bool auto_wrap_objects_;
};

class classes
Expand Down Expand Up @@ -216,6 +220,13 @@ class class_
return *this;
}

/// Enable new C++ objects auto-wrapping
class_& auto_wrap_objects(bool auto_wrap = true)
{
class_info_.set_auto_wrap_objects(auto_wrap);
return *this;
}

/// Set C++ class member function
template<typename Method>
typename std::enable_if<
Expand Down Expand Up @@ -388,6 +399,24 @@ class class_
.find_v8_object(Traits::const_pointer_cast(obj));
}

/// Find V8 object handle for a wrapped C++ object, may return empty handle on fail
/// or wrap a copy of the obj if class_.auto_wrap_objects()
static v8::Local<v8::Object> find_object(v8::Isolate* isolate, T const& obj)
{
using namespace detail;
detail::object_registry<Traits>& class_info = classes::find<Traits>(isolate, type_id<T>());
v8::Local<v8::Object> wrapped_object = class_info.find_v8_object(Traits::key(const_cast<T*>(&obj)));
if (wrapped_object.IsEmpty() && class_info.auto_wrap_objects())
{
object_pointer_type clone = Traits::clone(obj);
if (clone)
{
wrapped_object = class_info.wrap_object(clone, true);
}
}
return wrapped_object;
}

/// Destroy wrapped C++ object
static void destroy_object(v8::Isolate* isolate, object_pointer_type const& obj)
{
Expand Down
1 change: 1 addition & 0 deletions v8pp/class.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ V8PP_IMPL object_registry<Traits>::object_registry(v8::Isolate* isolate, type_in
, isolate_(isolate)
, ctor_() // no wrapped class constructor available by default
, dtor_(std::move(dtor))
, auto_wrap_objects_(false)
{
v8::HandleScope scope(isolate_);

Expand Down
12 changes: 8 additions & 4 deletions v8pp/convert.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,7 @@ struct convert<T, typename std::enable_if<is_wrapped_class<T>::value>::type>
{
using from_type = T&;
using to_type = v8::Local<v8::Object>;
using class_type = typename std::remove_cv<T>::type;

static bool is_valid(v8::Isolate* isolate, v8::Local<v8::Value> value)
{
Expand All @@ -539,7 +540,8 @@ struct convert<T, typename std::enable_if<is_wrapped_class<T>::value>::type>
{
throw invalid_argument(isolate, value, "Object");
}
if (T* object = convert<T*>::from_v8(isolate, value))
T* object = class_<class_type, raw_ptr_traits>::unwrap_object(isolate, value);
if (object)
{
return *object;
}
Expand All @@ -548,7 +550,7 @@ struct convert<T, typename std::enable_if<is_wrapped_class<T>::value>::type>

static to_type to_v8(v8::Isolate* isolate, T const& value)
{
v8::Local<v8::Object> result = convert<T*>::to_v8(isolate, &value);
v8::Local<v8::Object> result = class_<class_type, raw_ptr_traits>::find_object(isolate, value);
if (!result.IsEmpty()) return result;
throw std::runtime_error("failed to wrap C++ object");
}
Expand Down Expand Up @@ -586,6 +588,7 @@ struct convert<T, ref_from_shared_ptr>
{
using from_type = T&;
using to_type = v8::Local<v8::Object>;
using class_type = typename std::remove_cv<T>::type;

static bool is_valid(v8::Isolate* isolate, v8::Local<v8::Value> value)
{
Expand All @@ -598,7 +601,8 @@ struct convert<T, ref_from_shared_ptr>
{
throw invalid_argument(isolate, value, "Object");
}
if (std::shared_ptr<T> object = convert<std::shared_ptr<T>>::from_v8(isolate, value))
std::shared_ptr<T> object = class_<class_type, shared_ptr_traits>::unwrap_object(isolate, value);
if (object)
{
// assert(object.use_count() > 1);
return *object;
Expand All @@ -608,7 +612,7 @@ struct convert<T, ref_from_shared_ptr>

static to_type to_v8(v8::Isolate* isolate, T const& value)
{
v8::Local<v8::Object> result = convert<std::shared_ptr<T>>::to_v8(isolate, &value);
v8::Local<v8::Object> result = class_<class_type, shared_ptr_traits>::find_object(isolate, value);
if (!result.IsEmpty()) return result;
throw std::runtime_error("failed to wrap C++ object");
}
Expand Down
2 changes: 1 addition & 1 deletion v8pp/function.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ template<typename Traits, typename F>
void forward_ret(v8::FunctionCallbackInfo<v8::Value> const& args, std::false_type /*is_void_return*/)
{
using return_type = typename function_traits<F>::return_type;
using converter = call_from_v8_traits<F>::arg_converter<return_type, Traits>;
using converter = typename call_from_v8_traits<F>::template arg_converter<return_type, Traits>;
args.GetReturnValue().Set(converter::to_v8(args.GetIsolate(),
invoke<Traits, F>(args, std::is_member_function_pointer<F>())));
}
Expand Down
16 changes: 14 additions & 2 deletions v8pp/ptr_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,15 @@ struct raw_ptr_traits
}

template<typename T>
static void destroy(object_pointer_type<T> const& object)
static object_pointer_type<T> clone(T const& src)
{
delete object;
return new T(src);
}

template<typename T>
static void destroy(object_pointer_type<T> const& ptr)
{
delete ptr;
}

template<typename T>
Expand Down Expand Up @@ -91,6 +97,12 @@ struct shared_ptr_traits
return std::make_shared<T>(std::forward<Args>(args)...);
}

template<typename T>
static object_pointer_type<T> clone(T const& src)
{
return std::make_shared<T>(src);
}

template<typename T>
static void destroy(object_pointer_type<T> const&)
{
Expand Down

0 comments on commit 8ab2770

Please sign in to comment.