aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChad Austin <chad@imvu.com>2014-05-07 18:30:30 -0700
committerBruce Mitchener <bruce.mitchener@gmail.com>2014-05-21 22:58:23 +0700
commit1f7d9ea73de5fccbedf642b68ff600a5b0285061 (patch)
tree01c309e4398f055c191c7cd60db436e2e09dbd3f
parent1bad0e2ae1038b9ae6ba362c73ca9d3fc8f17e11 (diff)
Support deriving from abstract classes with constructors
-rw-r--r--src/embind/embind.js17
-rw-r--r--system/include/emscripten/bind.h35
-rw-r--r--tests/embind/embind.test.js20
-rw-r--r--tests/embind/embind_test.cpp33
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>