diff options
author | Chad Austin <chad@imvu.com> | 2013-04-09 18:01:15 -0700 |
---|---|---|
committer | Jukka Jylänki <jujjyl@gmail.com> | 2013-04-18 20:08:05 +0300 |
commit | 17639c372a5f41177177c21c56731b103ad738ad (patch) | |
tree | 5f0e512d5824bf3581410af529461bc50b21ba76 | |
parent | 927c8a5e44da6c5cc539fa7d4e0a2168dafa3d36 (diff) |
allow optional implementation of non-abstract virtual methods
-rwxr-xr-x | src/embind/emval.js | 5 | ||||
-rwxr-xr-x | system/include/emscripten/bind.h | 15 | ||||
-rw-r--r-- | system/include/emscripten/val.h | 7 | ||||
-rwxr-xr-x | tests/embind/embind.test.js | 22 | ||||
-rw-r--r-- | tests/embind/embind_test.cpp | 14 |
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)
|