aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/embind/emval.js5
-rwxr-xr-xsystem/include/emscripten/bind.h15
-rw-r--r--system/include/emscripten/val.h7
-rwxr-xr-xtests/embind/embind.test.js22
-rw-r--r--tests/embind/embind_test.cpp14
5 files changed, 62 insertions, 1 deletions
diff --git a/src/embind/emval.js b/src/embind/emval.js
index 45030b99..037ce47d 100755
--- a/src/embind/emval.js
+++ b/src/embind/emval.js
@@ -194,3 +194,8 @@ function __emval_call_void_method(handle, name, argCount, argTypes) {
var obj = _emval_handle_array[handle].value;
obj[name].apply(obj, args);
}
+
+function __emval_has_function(handle, name) {
+ name = Pointer_stringify(name);
+ return _emval_handle_array[handle].value[name] instanceof Function;
+}
diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h
index d70aec91..07955d75 100755
--- a/system/include/emscripten/bind.h
+++ b/system/include/emscripten/bind.h
@@ -713,7 +713,7 @@ namespace emscripten {
template<typename T>
class wrapper : public T {
public:
- wrapper(const val& wrapped)
+ explicit wrapper(const val& wrapped)
: wrapped(wrapped)
{}
@@ -722,7 +722,20 @@ namespace emscripten {
return Caller<ReturnType, Args...>::call(wrapped, name, args...);
}
+ template<typename ReturnType, typename... Args, typename Default>
+ ReturnType optional_call(const char* name, Default def, Args... args) const {
+ if (has_function(name)) {
+ return Caller<ReturnType>::call(wrapped, name);
+ } else {
+ return def();
+ }
+ }
+
private:
+ bool has_function(const char* name) const {
+ return wrapped.has_function(name);
+ }
+
// this class only exists because you can't partially specialize function templates
template<typename ReturnType, typename... Args>
struct Caller {
diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h
index 91f775a7..c27a0fdb 100644
--- a/system/include/emscripten/val.h
+++ b/system/include/emscripten/val.h
@@ -48,6 +48,9 @@ namespace emscripten {
unsigned argCount,
internal::TYPEID argTypes[]
/*, ...*/);
+ bool _emval_has_function(
+ EM_VAL value,
+ const char* methodName);
}
}
@@ -222,6 +225,10 @@ namespace emscripten {
toWireType(args)...);
}
+ bool has_function(const char* name) const {
+ return _emval_has_function(handle, name);
+ }
+
template<typename T>
T as() const {
using namespace internal;
diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js
index 8155fd11..4a8665f4 100755
--- a/tests/embind/embind.test.js
+++ b/tests/embind/embind.test.js
@@ -1464,6 +1464,28 @@ module({
assert.equal(expected, cm.callAbstractMethod(impl));
impl.delete();
});
+
+ test("can implement optional methods in JavaScript", function() {
+ var expected = "my JS string";
+ function MyImplementation() {
+ this.rv = expected;
+ }
+ MyImplementation.prototype.optionalMethod = function() {
+ return this.rv;
+ };
+
+ var impl = cm.AbstractClass.implement(new MyImplementation);
+ assert.equal(expected, impl.optionalMethod(expected));
+ assert.equal(expected, cm.callOptionalMethod(impl, expected));
+ impl.delete();
+ });
+
+ test("if not implemented then optional method runs default", function() {
+ var impl = cm.AbstractClass.implement({});
+ assert.equal("optionalfoo", impl.optionalMethod("foo"));
+ assert.equal("optionalfoo", cm.callOptionalMethod(impl, "foo"));
+ impl.delete();
+ });
});
BaseFixture.extend("registration order", function() {
diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp
index f3e2cbee..39de3852 100644
--- a/tests/embind/embind_test.cpp
+++ b/tests/embind/embind_test.cpp
@@ -1074,6 +1074,9 @@ class AbstractClass {
public:
virtual ~AbstractClass() {}
virtual std::string abstractMethod() const = 0;
+ virtual std::string optionalMethod(std::string s) const {
+ return "optional" + s;
+ }
};
class AbstractClassWrapper : public wrapper<AbstractClass> {
@@ -1083,6 +1086,11 @@ public:
std::string abstractMethod() const {
return call<std::string>("abstractMethod");
}
+ std::string optionalMethod(std::string s) const {
+ return optional_call<std::string>("optionalMethod", [&] {
+ return AbstractClass::optionalMethod(s);
+ }, s);
+ }
};
class ConcreteClass : public AbstractClass {
@@ -1099,6 +1107,10 @@ std::string callAbstractMethod(AbstractClass& ac) {
return ac.abstractMethod();
}
+std::string callOptionalMethod(AbstractClass& ac, std::string s) {
+ return ac.optionalMethod(s);
+}
+
class HasExternalConstructor {
public:
HasExternalConstructor(const std::string& str)
@@ -1859,10 +1871,12 @@ EMSCRIPTEN_BINDINGS(tests) {
.smart_ptr<std::shared_ptr<AbstractClass>>()
.allow_subclass<AbstractClassWrapper>()
.function("abstractMethod", &AbstractClass::abstractMethod)
+ .function("optionalMethod", &AbstractClass::optionalMethod)
;
function("getAbstractClass", &getAbstractClass);
function("callAbstractMethod", &callAbstractMethod);
+ function("callOptionalMethod", &callOptionalMethod);
class_<HasExternalConstructor>("HasExternalConstructor")
.constructor(&createHasExternalConstructor)