diff --git a/test/test_class.cpp b/test/test_class.cpp index 2b683bb4..3f39f3c7 100644 --- a/test/test_class.cpp +++ b/test/test_class.cpp @@ -407,6 +407,33 @@ void test_const_instance_in_module() check_eq("xconst.f()", run_script(context, "api.xconst.f(1)"), 1); } +template +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_class(isolate); + X_class + .ctor() + .auto_wrap_objects(true) + .set("x", v8pp::property(&X::get_x)); + + auto f0 = [](int x) { return X(x); }; + auto f = v8pp::wrap_function(isolate, "f", std::move(f0)); + context.set("X", X_class); + context.set("f", f); + check_eq("return X object", run_script(context, "obj = f(123); obj.x"), 123); +} + void test_class() { test_class_(); @@ -417,4 +444,7 @@ void test_class() test_const_instance_in_module(); test_const_instance_in_module(); + + test_auto_wrap_objects(); + test_auto_wrap_objects(); } diff --git a/v8pp/class.hpp b/v8pp/class.hpp index 5b40f454..3cb226d2 100644 --- a/v8pp/class.hpp +++ b/v8pp/class.hpp @@ -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); @@ -118,6 +121,7 @@ class object_registry final : public class_info ctor_function ctor_; dtor_function dtor_; + bool auto_wrap_objects_; }; class classes @@ -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 std::enable_if< @@ -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 find_object(v8::Isolate* isolate, T const& obj) + { + using namespace detail; + detail::object_registry& class_info = classes::find(isolate, type_id()); + v8::Local wrapped_object = class_info.find_v8_object(Traits::key(const_cast(&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) { diff --git a/v8pp/class.ipp b/v8pp/class.ipp index 560b97d4..77b7cb02 100644 --- a/v8pp/class.ipp +++ b/v8pp/class.ipp @@ -44,6 +44,7 @@ V8PP_IMPL object_registry::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_); diff --git a/v8pp/convert.hpp b/v8pp/convert.hpp index b50ee933..e8e912e5 100644 --- a/v8pp/convert.hpp +++ b/v8pp/convert.hpp @@ -527,6 +527,7 @@ struct convert::value>::type> { using from_type = T&; using to_type = v8::Local; + using class_type = typename std::remove_cv::type; static bool is_valid(v8::Isolate* isolate, v8::Local value) { @@ -539,7 +540,8 @@ struct convert::value>::type> { throw invalid_argument(isolate, value, "Object"); } - if (T* object = convert::from_v8(isolate, value)) + T* object = class_::unwrap_object(isolate, value); + if (object) { return *object; } @@ -548,7 +550,7 @@ struct convert::value>::type> static to_type to_v8(v8::Isolate* isolate, T const& value) { - v8::Local result = convert::to_v8(isolate, &value); + v8::Local result = class_::find_object(isolate, value); if (!result.IsEmpty()) return result; throw std::runtime_error("failed to wrap C++ object"); } @@ -586,6 +588,7 @@ struct convert { using from_type = T&; using to_type = v8::Local; + using class_type = typename std::remove_cv::type; static bool is_valid(v8::Isolate* isolate, v8::Local value) { @@ -598,7 +601,8 @@ struct convert { throw invalid_argument(isolate, value, "Object"); } - if (std::shared_ptr object = convert>::from_v8(isolate, value)) + std::shared_ptr object = class_::unwrap_object(isolate, value); + if (object) { // assert(object.use_count() > 1); return *object; @@ -608,7 +612,7 @@ struct convert static to_type to_v8(v8::Isolate* isolate, T const& value) { - v8::Local result = convert>::to_v8(isolate, &value); + v8::Local result = class_::find_object(isolate, value); if (!result.IsEmpty()) return result; throw std::runtime_error("failed to wrap C++ object"); } diff --git a/v8pp/ptr_traits.hpp b/v8pp/ptr_traits.hpp index 60811ff0..3afaf8c6 100644 --- a/v8pp/ptr_traits.hpp +++ b/v8pp/ptr_traits.hpp @@ -47,9 +47,15 @@ struct raw_ptr_traits } template - static void destroy(object_pointer_type const& object) + static object_pointer_type clone(T const& src) { - delete object; + return new T(src); + } + + template + static void destroy(object_pointer_type const& ptr) + { + delete ptr; } template @@ -91,6 +97,12 @@ struct shared_ptr_traits return std::make_shared(std::forward(args)...); } + template>> + static object_pointer_type clone(T const& src) + { + return std::make_shared(src); + } + template static void destroy(object_pointer_type const&) {