diff options
-rw-r--r-- | src/embind/embind.js | 17 | ||||
-rw-r--r-- | system/include/emscripten/bind.h | 35 | ||||
-rw-r--r-- | tests/embind/embind.test.js | 20 | ||||
-rw-r--r-- | tests/embind/embind_test.cpp | 33 |
4 files changed, 93 insertions, 12 deletions
diff --git a/src/embind/embind.js b/src/embind/embind.js index 43b67fbf..536d53fc 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -1743,6 +1743,8 @@ function __embind_create_inheriting_constructor(constructorName, wrapperType, pr wrapperType = requireRegisteredType(wrapperType, 'wrapper'); properties = requireHandle(properties); + var arraySlice = [].slice; + var registeredClass = wrapperType.registeredClass; var wrapperPrototype = registeredClass.instancePrototype; var baseClass = registeredClass.baseClass; @@ -1755,12 +1757,19 @@ function __embind_create_inheriting_constructor(constructorName, wrapperType, pr } }.bind(this)); - var inner = baseConstructor.__$implement(this); - this.$$ = inner.$$; - this.initialize.apply(this, Array.prototype.slice.call(arguments)); + this.__parent = wrapperPrototype; + this.initialize.apply(this, arraySlice.call(arguments)); }); + + // It's a little nasty that we're modifying the wrapper prototype here. + wrapperPrototype.initialize = function initialize() { + var inner = baseConstructor.__$implement.apply( + undefined, + [this].concat(arraySlice.call(arguments))); + this.$$ = inner.$$; + }; + ctor.prototype = Object.create(wrapperPrototype); - ctor.prototype.initialize = function initialize() {}; for (var p in properties) { ctor.prototype[p] = properties[p]; } diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index c8fa94c9..57d8f476 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -889,8 +889,10 @@ namespace emscripten { public: typedef T class_type; - explicit wrapper(val&& wrapped) - : wrapped(std::forward<val>(wrapped)) + template<typename... Args> + explicit wrapper(val&& wrapped, Args&&... args) + : T(std::forward<Args>(args)...) + , wrapped(std::forward<val>(wrapped)) {} template<typename ReturnType, typename... Args> @@ -911,8 +913,11 @@ namespace emscripten { val wrapped; }; -#define EMSCRIPTEN_WRAPPER(T) \ - T(::emscripten::val&& v): wrapper(std::forward<::emscripten::val>(v)) {} +#define EMSCRIPTEN_WRAPPER(T) \ + template<typename... Args> \ + T(::emscripten::val&& v, Args&&... args) \ + : wrapper(std::forward<::emscripten::val>(v), std::forward<Args>(args)...) \ + {} namespace internal { struct NoBaseClass { @@ -1030,6 +1035,10 @@ namespace emscripten { }; } + template<typename... ConstructorArgs> + struct constructor { + }; + template<typename ClassType, typename BaseSpecifier = internal::NoBaseClass> class class_ { public: @@ -1136,8 +1145,12 @@ namespace emscripten { return *this; } - template<typename WrapperType, typename PointerType = WrapperType*> - const class_& allow_subclass(const char* wrapperClassName, const char* pointerName = "<UnknownPointerName>") const { + template<typename WrapperType, typename PointerType = WrapperType*, typename... ConstructorArgs> + const class_& allow_subclass( + const char* wrapperClassName, + const char* pointerName = "<UnknownPointerName>", + ::emscripten::constructor<ConstructorArgs...> = ::emscripten::constructor<ConstructorArgs...>() + ) const { using namespace internal; auto cls = class_<WrapperType, base<ClassType>>(wrapperClassName) @@ -1150,7 +1163,7 @@ namespace emscripten { // _embind_register_wrapper_constructor class_function( "__$implement", - &wrapped_new<PointerType, WrapperType, val>, + &wrapped_new<PointerType, WrapperType, val, ConstructorArgs...>, allow_raw_pointer<ret_val>()) .class_function( "extend", @@ -1158,6 +1171,14 @@ namespace emscripten { ; } + template<typename WrapperType, typename... ConstructorArgs> + const class_& allow_subclass( + const char* wrapperClassName, + ::emscripten::constructor<ConstructorArgs...> constructor + ) const { + return allow_subclass<WrapperType, WrapperType*>(wrapperClassName, "<UnknownPointerName>", constructor); + } + template<typename ReturnType, typename... Args, typename... Policies> EMSCRIPTEN_ALWAYS_INLINE const class_& function(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...), Policies...) const { using namespace internal; diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js index 8307677d..6f0c6860 100644 --- a/tests/embind/embind.test.js +++ b/tests/embind/embind.test.js @@ -1570,6 +1570,7 @@ module({ test("properties set in constructor are externally visible", function() { var HasProperty = cm.AbstractClass.extend("HasProperty", { initialize: function(x) { + this.__parent.initialize.call(this); this.property = x; }, abstractMethod: function() { @@ -1595,6 +1596,7 @@ module({ test("properties set in constructor are visible in overridden methods", function() { var HasProperty = cm.AbstractClass.extend("HasProperty", { initialize: function(x) { + this.__parent.initialize.call(this); this.x = x; }, abstractMethod: function() { @@ -1747,6 +1749,24 @@ module({ }); assert.equal('Pure virtual function abstractMethod must be implemented in JavaScript', error.message); }); + + test("can extend from C++ class with constructor arguments", function() { + var parent = cm.AbstractClassWithConstructor; + var C = parent.extend("C", { + initialize: function(x) { + this.__parent.initialize.call(this, x); + }, + abstractMethod: function() { + return this.concreteMethod(); + } + }); + + var impl = new C("hi"); + var rv = cm.callAbstractMethod2(impl); + impl.delete(); + + assert.equal("hi", rv); + }); }); BaseFixture.extend("registration order", function() { diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index 1bfd0ef7..d652d528 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -1127,7 +1127,6 @@ class ConcreteClass : public AbstractClass { return "from concrete"; } - void differentArguments(int i, double d, unsigned char f, double q, std::string s) { } @@ -1157,6 +1156,31 @@ void callDifferentArguments(AbstractClass& ac, int i, double d, unsigned char f, return ac.differentArguments(i, d, f, q, s); } +struct AbstractClassWithConstructor { + explicit AbstractClassWithConstructor(std::string s) + : s(s) + {} + + virtual std::string abstractMethod() = 0; + std::string concreteMethod() { + return s; + } + + std::string s; +}; + +struct AbstractClassWithConstructorWrapper : public wrapper<AbstractClassWithConstructor> { + EMSCRIPTEN_WRAPPER(AbstractClassWithConstructorWrapper); + + virtual std::string abstractMethod() override { + return call<std::string>("abstractMethod"); + } +}; + +std::string callAbstractMethod2(AbstractClassWithConstructor& ac) { + return ac.abstractMethod(); +} + EMSCRIPTEN_BINDINGS(interface_tests) { class_<AbstractClass>("AbstractClass") .smart_ptr<std::shared_ptr<AbstractClass>>("shared_ptr<AbstractClass>") @@ -1177,6 +1201,13 @@ EMSCRIPTEN_BINDINGS(interface_tests) { function("callOptionalMethod", &callOptionalMethod); function("callReturnsSharedPtrMethod", &callReturnsSharedPtrMethod); function("callDifferentArguments", &callDifferentArguments); + + class_<AbstractClassWithConstructor>("AbstractClassWithConstructor") + .allow_subclass<AbstractClassWithConstructorWrapper>("AbstractClassWithConstructorWrapper", constructor<std::string>()) + .function("abstractMethod", &AbstractClassWithConstructor::abstractMethod, pure_virtual()) + .function("concreteMethod", &AbstractClassWithConstructor::concreteMethod) + ; + function("callAbstractMethod2", &callAbstractMethod2); } template<typename T, size_t sizeOfArray> |