From 2d1f89a00a4cc27f8f5c8c917b28c72248497908 Mon Sep 17 00:00:00 2001 From: Pavel Medvedev Date: Sun, 27 Aug 2017 18:41:12 +0200 Subject: [PATCH] Fix issue #62 Cast pointers to class member (variable, function, property getter/setter) to the real class type in addition to 4974a8ba65aeb12c6529ab3097c9dbf4e5ff351e Added constructors and copy-constructors for `property_` template in order to use them in `class_::set()` for casting to the real class type. Added tests for binding classes with multiple inheritance. --- test/test_class.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++++ v8pp/class.hpp | 16 ++++----- v8pp/property.hpp | 33 +++++++++++++++---- 3 files changed, 113 insertions(+), 15 deletions(-) diff --git a/test/test_class.cpp b/test/test_class.cpp index 103612fc..f7b0daac 100644 --- a/test/test_class.cpp +++ b/test/test_class.cpp @@ -207,8 +207,87 @@ void test_class_() 1 + 2 * use_shared_ptr); // y1 + (y2 + y3 when use_shared_ptr) } +template +void test_multiple_inheritance() +{ + struct A + { + int x; + A() : x(1) {} + int f() { return x; } + void set_f(int v) { x = v; } + + int z() const { return x; } + }; + + struct B + { + int x; + B() : x(2) {} + int g() { return x; } + void set_g(int v) { x = v; } + + int z() const { return x; } + }; + + struct C : A, B + { + int x; + C() : x(3) {} + int h() { return x; } + void set_h(int v) { x = v; } + + int z() const { return x; } + }; + + v8pp::context context; + v8::Isolate* isolate = context.isolate(); + v8::HandleScope scope(isolate); + + v8pp::class_ C_class(isolate); + C_class + .ctor<>() + .set("xA", &A::x) + .set("xB", &B::x) + .set("xC", &C::x) + + .set("zA", &A::z) + .set("zB", &B::z) + .set("zC", &C::z) + + .set("f", &A::f) + .set("g", &B::g) + .set("h", &C::h) + + .set("rF", v8pp::property(&C::f)) + .set("rG", v8pp::property(&C::g)) + .set("rH", v8pp::property(&C::h)) + + .set("F", v8pp::property(&C::f, &C::set_f)) + .set("G", v8pp::property(&C::g, &C::set_g)) + .set("H", v8pp::property(&C::h, &C::set_h)) + ; + + + context.set("C", C_class); + check_eq("get attributes", run_script(context, "c = new C(); c.xA + c.xB + c.xC"), 1 + 2 + 3); + check_eq("set attributes", run_script(context, + "c = new C(); c.xA = 10; c.xB = 20; c.xC = 30; c.xA + c.xB + c.xC"), 10 + 20 + 30); + + check_eq("functions", run_script(context, "c = new C(); c.f() + c.g() + c.h()"), 1 + 2 + 3); + check_eq("z functions", run_script(context, "c = new C(); c.zA() + c.zB() + c.zC()"), 1 + 2 + 3); + + check_eq("rproperties", run_script(context, + "c = new C(); c.rF + c.rG + c.rH"), 1 + 2 + 3); + check_eq("rwproperties", run_script(context, + "c = new C(); c.F = 100; c.G = 200; c.H = 300; c.F + c.G + c.H"), 100 + 200 + 300); +} + void test_class() { test_class_(); test_class_(); + + test_multiple_inheritance(); + test_multiple_inheritance(); } diff --git a/v8pp/class.hpp b/v8pp/class.hpp index 32f40f43..c829e7d9 100644 --- a/v8pp/class.hpp +++ b/v8pp/class.hpp @@ -621,11 +621,11 @@ class class_ { using mem_func_type = typename detail::function_traits::template pointer_type; - + mem_func_type mf(mem_func); class_info_.class_function_template()->PrototypeTemplate()->Set( isolate(), name, v8::FunctionTemplate::New(isolate(), &detail::forward_function, - detail::set_external_data(isolate(), std::forward(mem_func)))); + detail::set_external_data(isolate(), std::forward(mf)))); return *this; } @@ -653,6 +653,7 @@ class class_ using attribute_type = typename detail::function_traits::template pointer_type; + attribute_type attr(attribute); v8::AccessorGetterCallback getter = &member_get; v8::AccessorSetterCallback setter = &member_set; if (readonly) @@ -663,7 +664,7 @@ class class_ class_info_.class_function_template()->PrototypeTemplate() ->SetAccessor(v8pp::to_v8(isolate(), name), getter, setter, detail::set_external_data(isolate(), - std::forward(attribute)), v8::DEFAULT, + std::forward(attr)), v8::DEFAULT, v8::PropertyAttribute(v8::DontDelete | (setter? 0 : v8::ReadOnly))); return *this; } @@ -674,25 +675,24 @@ class class_ && std::is_member_function_pointer::value, class_&>::type set(char const *name, property_&& property) { - using prop_type = property_; - v8::HandleScope scope(isolate()); using property_type = property_< typename detail::function_traits::template pointer_type, typename detail::function_traits::template pointer_type >; + property_type prop(property); v8::AccessorGetterCallback getter = property_type::template get; v8::AccessorSetterCallback setter = property_type::template set; - if (prop_type::is_readonly) + if (prop.is_readonly) { setter = nullptr; } class_info_.class_function_template()->PrototypeTemplate() - ->SetAccessor(v8pp::to_v8(isolate(), name),getter, setter, + ->SetAccessor(v8pp::to_v8(isolate(), name), getter, setter, detail::set_external_data(isolate(), - std::forward(property)), v8::DEFAULT, + std::forward(prop)), v8::DEFAULT, v8::PropertyAttribute(v8::DontDelete | (setter ? 0 : v8::ReadOnly))); return *this; } diff --git a/v8pp/property.hpp b/v8pp/property.hpp index f0559ef7..36a49787 100644 --- a/v8pp/property.hpp +++ b/v8pp/property.hpp @@ -345,6 +345,19 @@ struct property_ Set setter; enum { is_readonly = false }; + + property_(Get getter, Set setter) + : getter(getter) + , setter(setter) + { + } + + template + property_(property_ const& other) + : getter(other.getter) + , setter(other.setter) + { + } }; /// Read-only property class specialization for get only method @@ -362,25 +375,31 @@ struct property_ Get getter; enum { is_readonly = true }; + + explicit property_(Get getter) + : getter(getter) + { + } + + template + explicit property_(property_ const& other) + : getter(other.getter) + { + } }; /// Create read/write property from get and set member functions template property_ property(Get get, Set set) { - property_ prop; - prop.getter = get; - prop.setter = set; - return prop; + return property_(get, set); } /// Create read-only property from a get function template property_ property(Get get) { - property_ prop; - prop.getter = get; - return prop; + return property_(get); } } // namespace v8pp