diff --git a/site/source/docs/porting/connecting_cpp_and_javascript/embind.rst b/site/source/docs/porting/connecting_cpp_and_javascript/embind.rst index 01951087c29cb..d0814f5f7a045 100644 --- a/site/source/docs/porting/connecting_cpp_and_javascript/embind.rst +++ b/site/source/docs/porting/connecting_cpp_and_javascript/embind.rst @@ -770,24 +770,53 @@ are available. Overloaded functions ==================== -Constructors and functions can be overloaded on the number of arguments, -but *embind* does not support overloading based on type. When specifying -an overload, use the :cpp:func:`select_overload` helper function to select -the appropriate signature. +Constructors and functions can be overloaded on both the number and +javascript types of arguments, but *embind* does not support overloading +based on the C++ type. When specifying an overload, use the +:cpp:func:`select_overload` helper function to select the appropriate signature. .. code:: cpp - struct HasOverloadedMethods { + class A {}; + class B : public A {}; + class C {}; + + class HasOverloadedMethods { + public: + HasOverloadedMethods(std::string i); // javascript type of parameter: string + HasOverloadedMethods(int i); // javascript type of parameter: number + void foo(); - void foo(int i); - void foo(float f) const; + void foo(std::string i); // javascript type of parameter: string + void foo(short i); // javascript type of parameter: number + void foo(A i); // javascript type of parameter: A + void foo(std::shared_ptr i); // javascript type of parameter: C + void foo(int i, int j); // javascript types of parameters: number, number + void foo(double i); // javascript type of parameter: number + void foo(float f) const; // javascript type of parameter: number + void foo(B i); // javascript type of parameter: B (derived from A) + + static void staticFoo(std::string i); // javascript type of parameter: string + static void staticFoo(int i); // javascript type of parameter: number }; EMSCRIPTEN_BINDING(overloads) { class_("HasOverloadedMethods") + .constructor() + .constructor() + // .smart_ptr_constructor("HasOverloadedMethods", &std::make_shared) + // .smart_ptr_constructor("HasOverloadedMethods", &std::make_shared) .function("foo", select_overload(&HasOverloadedMethods::foo)) - .function("foo_int", select_overload(&HasOverloadedMethods::foo)) + .function("foo", select_overload(&HasOverloadedMethods::foo)) + .function("foo", select_overload(&HasOverloadedMethods::foo)) + .function("foo", select_overload(&HasOverloadedMethods::foo)) + .function("foo", select_overload)>(&HasOverloadedMethods::foo)) + .function("foo", select_overload(&HasOverloadedMethods::foo)) + .function("foo_double", select_overload(&HasOverloadedMethods::foo)) .function("foo_float", select_overload(&HasOverloadedMethods::foo)) + .function("foo_B", select_overload(&HasOverloadedMethods::foo)) + .class_function("staticFoo", select_overload(&HasOverloadedMethods::staticFoo)) + .class_function("staticFoo", select_overload(&HasOverloadedMethods::staticFoo)) ; } diff --git a/src/lib/libembind.js b/src/lib/libembind.js index 3542d45f8c4e9..b7df859d2b141 100644 --- a/src/lib/libembind.js +++ b/src/lib/libembind.js @@ -55,6 +55,70 @@ var LibraryEmbind = { throw new UnboundTypeError(`${message}: ` + unboundTypes.map(getTypeName).join([', '])); }, + $getSignature__deps: ['$registeredTypes'], + $getSignature: (args, keys) => { + var signature = ''; + keys.some(key => { + if (key.length !== args.length) { + return false; // continue + } + var isKeyMatched = key.every((field, i) => { + return ( + field === 'emscripten::val' + || (typeof args[i] === 'bigint' && field === 'number') + || ( + typeof args[i] === 'object' + && undefined !== registeredTypes[field] + && args[i] instanceof registeredTypes[field].registeredClass.constructor + ) + || typeof args[i] === field + ); + }); + if (isKeyMatched) { + signature = key.join(', '); + return true; // break + } + return false; // continue + }); + return signature; + }, + + $cppTypeToJsType__deps: ['$registeredTypes'], + $cppTypeToJsType: (typeId) => { + var type = registeredTypes[typeId]; + if (type.name === 'std::string' || type.name === 'std::wstring') return 'string'; + else if (type.name === 'bool') return 'boolean'; + else if (['char', 'signed char', 'unsigned char', 'short', 'unsigned short', 'int', 'unsigned int', 'long', 'unsigned long', 'float', 'double', 'int64_t', 'uint64_t'].includes(type.name)) return 'number'; + return typeId; + }, + + // Creates a function overload signature resolution table to the given method 'methodName' in the given prototype, + // if the overload signature table doesn't yet exist. + $ensureOverloadSignatureTable__deps: ['$throwBindingError', '$getSignature', '$ensureOverloadTable', '$registeredTypes'], + $ensureOverloadSignatureTable: (proto, methodName, humanName, numArguments) => { + ensureOverloadTable(proto, methodName, humanName); + if (undefined !== proto[methodName].overloadTable && undefined !== proto[methodName].overloadTable[numArguments] && undefined === proto[methodName].overloadTable[numArguments].signatures) { + var prevFunc = proto[methodName].overloadTable[numArguments]; + // Inject an overload resolver function that routes to the appropriate overload based on signatures. + proto[methodName].overloadTable[numArguments] = function(...args) { + var keys = proto[methodName].overloadTable[args.length].signaturesArray; + var signature = getSignature(args, keys); + // TODO This check can be removed in -O3 level "unsafe" optimizations. + if (!proto[methodName].overloadTable[args.length].signatures.hasOwnProperty(signature)) { + var signatures = proto[methodName].overloadTable[args.length].signaturesArray.map(sig => '(' + sig.map(s => typeof s === 'string' ? s : registeredTypes[s].name) + ')'); + var params = args.map(arg => (typeof arg === 'object' && arg.constructor && arg.constructor.name) ? arg.constructor.name : typeof arg); + throwBindingError(`Function '${humanName}' called with an invalid signature (${params}) - expects one of (${signatures})!`); + } + return proto[methodName].overloadTable[args.length].signatures[signature].apply(this, args); + }; + // Move the previous function into the overload signature table. + proto[methodName].overloadTable[numArguments].signatures = {}; + proto[methodName].overloadTable[numArguments].signatures[prevFunc.signature] = prevFunc; + // prevFunc.signature is raw signature, so it doesn't need to be added to the array. + proto[methodName].overloadTable[numArguments].signaturesArray = []; + } + }, + // Creates a function overload resolution table to the given method 'methodName' in the given prototype, // if the overload table doesn't yet exist. $ensureOverloadTable__deps: ['$throwBindingError'], @@ -81,44 +145,79 @@ var LibraryEmbind = { name: The name of the symbol that's being exposed. value: The object itself to expose (function, class, ...) numArguments: For functions, specifies the number of arguments the function takes in. For other types, unused and undefined. + rawSignature: For functions, specifies raw signature of arguments the function takes in. For other types, unused and undefined. To implement support for multiple overloads of a function, an 'overload selector' function is used. That selector function chooses the appropriate overload to call from an function overload table. This selector function is only used if multiple overloads are actually registered, since it carries a slight performance penalty. */ - $exposePublicSymbol__deps: ['$ensureOverloadTable', '$throwBindingError'], - $exposePublicSymbol__docs: '/** @param {number=} numArguments */', - $exposePublicSymbol: (name, value, numArguments) => { + $exposePublicSymbol__deps: ['$ensureOverloadTable', '$throwBindingError', '$ensureOverloadSignatureTable'], + $exposePublicSymbol__docs: `/** + @param {number=} numArguments, + @param {string=} rawSignature, + */`, + $exposePublicSymbol: (name, value, numArguments, rawSignature) => { if (Module.hasOwnProperty(name)) { - if (undefined === numArguments || (undefined !== Module[name].overloadTable && undefined !== Module[name].overloadTable[numArguments])) { + if ( + undefined === numArguments + || Module[name].signature === rawSignature + || (undefined !== Module[name].overloadTable && undefined !== Module[name].overloadTable[numArguments] && Module[name].overloadTable[numArguments].signature === rawSignature) + || (undefined !== Module[name].overloadTable && undefined !== Module[name].overloadTable[numArguments] && undefined !== Module[name].overloadTable[numArguments].signatures && undefined !== Module[name].overloadTable[numArguments].signatures[rawSignature]) + ) { throwBindingError(`Cannot register public name '${name}' twice`); } - // We are exposing a function with the same name as an existing function. Create an overload table and a function selector - // that routes between the two. - ensureOverloadTable(Module, name, name); - if (Module[name].overloadTable.hasOwnProperty(numArguments)) { - throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`); + if ( + (undefined === Module[name].overloadTable && Module[name].argCount === numArguments) + || (undefined !== Module[name].overloadTable && undefined !== Module[name].overloadTable[numArguments]) + ) { + ensureOverloadSignatureTable(Module, name, name, numArguments); + Module[name].overloadTable[numArguments].signatures[rawSignature] = value; + } else { + // We are exposing a function with the same name as an existing function. Create an overload table and a function selector + // that routes between the two. + ensureOverloadTable(Module, name, name); + // Add the new function into the overload table. + Module[name].overloadTable[numArguments] = value; + Module[name].overloadTable[numArguments].signature = rawSignature; } - // Add the new function into the overload table. - Module[name].overloadTable[numArguments] = value; } else { Module[name] = value; - Module[name].argCount = numArguments; + if (undefined !== rawSignature) { + Module[name].argCount = numArguments; + Module[name].signature = rawSignature; + } } }, - $replacePublicSymbol__deps: ['$throwInternalError'], - $replacePublicSymbol__docs: '/** @param {number=} numArguments */', - $replacePublicSymbol: (name, value, numArguments) => { + $replacePublicSymbol__deps: ['$throwInternalError', '$throwBindingError'], + $replacePublicSymbol__docs: `/** + @param {number=} numArguments, + @param {Array=} signatureArray, + @param {string=} rawSignature + */`, + $replacePublicSymbol: (name, value, numArguments, signatureArray, rawSignature) => { if (!Module.hasOwnProperty(name)) { throwInternalError('Replacing nonexistent public symbol'); } + var signatureString = undefined !== signatureArray ? signatureArray.join(', ') : undefined; // If there's an overload table for this symbol, replace the symbol in the overload table instead. if (undefined !== Module[name].overloadTable && undefined !== numArguments) { - Module[name].overloadTable[numArguments] = value; + if (undefined !== Module[name].overloadTable[numArguments] && undefined !== Module[name].overloadTable[numArguments].signatures && signatureString) { + if (undefined !== Module[name].overloadTable[numArguments].signatures[rawSignature]) { + delete Module[name].overloadTable[numArguments].signatures[rawSignature]; + } + if (undefined !== Module[name].overloadTable[numArguments].signatures[signatureString]) { + throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`); + } + Module[name].overloadTable[numArguments].signatures[signatureString] = value; + Module[name].overloadTable[numArguments].signaturesArray.push(signatureArray); + } else { + Module[name].overloadTable[numArguments] = value; + } } else { Module[name] = value; Module[name].argCount = numArguments; + Module[name].signature = signatureString; } }, @@ -860,21 +959,25 @@ var LibraryEmbind = { _embind_register_function__deps: [ '$craftInvokerFunction', '$exposePublicSymbol', '$heap32VectorToArray', '$AsciiToString', '$replacePublicSymbol', '$embind__requireFunction', - '$throwUnboundTypeError', '$whenDependentTypesAreResolved', '$getFunctionName'], + '$throwUnboundTypeError', '$whenDependentTypesAreResolved', '$getFunctionName', + '$cppTypeToJsType'], _embind_register_function: (name, argCount, rawArgTypesAddr, signature, rawInvoker, fn, isAsync, isNonnullReturn) => { var argTypes = heap32VectorToArray(argCount, rawArgTypesAddr); name = AsciiToString(name); name = getFunctionName(name); rawInvoker = embind__requireFunction(signature, rawInvoker, isAsync); + var rawSignatureArray = argTypes.slice(1); + var rawSignatureString = rawSignatureArray.join(', '); exposePublicSymbol(name, function() { throwUnboundTypeError(`Cannot call ${name} due to unbound types`, argTypes); - }, argCount - 1); + }, argCount - 1, rawSignatureString); whenDependentTypesAreResolved([], argTypes, (argTypes) => { var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */); - replacePublicSymbol(name, craftInvokerFunction(name, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn, isAsync), argCount - 1); + var signatureArray = rawSignatureArray.map(a => cppTypeToJsType(a)); + replacePublicSymbol(name, craftInvokerFunction(name, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn, isAsync), argCount - 1, signatureArray, rawSignatureString); return []; }); }, @@ -1654,7 +1757,7 @@ Originally allocated`); // `.stack` will add "at ..." after this sentence '$makeLegalFunctionName', '$AsciiToString', '$RegisteredClass', '$RegisteredPointer', '$replacePublicSymbol', '$embind__requireFunction', '$throwUnboundTypeError', - '$whenDependentTypesAreResolved'], + '$whenDependentTypesAreResolved', '$getSignature', '$registeredTypes'], _embind_register_class: (rawType, rawPointerType, rawConstPointerType, @@ -1702,9 +1805,27 @@ Originally allocated`); // `.stack` will add "at ..." after this sentence if (undefined === registeredClass.constructor_body) { throw new BindingError(`${name} has no accessible constructor`); } - var body = registeredClass.constructor_body[args.length]; + + var body = undefined; + if (undefined !== registeredClass.constructor_body[args.length]) { + if (undefined !== registeredClass.constructor_body[args.length].func) { + body = registeredClass.constructor_body[args.length].func; + } else { + var keys = registeredClass.constructor_body[args.length].signaturesArray; + var signature = getSignature(args, keys); + + body = registeredClass.constructor_body[args.length].signatures[signature]; + } + } + if (undefined === body) { - throw new BindingError(`Tried to invoke ctor of ${name} with invalid number of parameters (${args.length}) - expected (${Object.keys(registeredClass.constructor_body).toString()}) parameters instead!`); + if (undefined === registeredClass.constructor_body[args.length]) { + throw new BindingError(`Tried to invoke ctor of ${name} with invalid number of parameters (${args.length}) - expected (${Object.keys(registeredClass.constructor_body).toString()}) parameters instead!`); + } else { + var signatures = registeredClass.constructor_body[args.length].signaturesArray.map(sig => '(' + sig.map(s => typeof s === 'string' ? s : registeredTypes[s].name) + ')'); + var params = args.map(arg => (typeof arg === 'object' && arg.constructor && arg.constructor.name) ? arg.constructor.name : typeof arg); + throw new BindingError(`Tried to invoke ctor of ${name} with invalid signature (${params}) - expected [${signatures}] parameters instead!`); + } } return body.apply(this, args); }); @@ -1777,7 +1898,7 @@ Originally allocated`); // `.stack` will add "at ..." after this sentence _embind_register_class_constructor__deps: [ '$heap32VectorToArray', '$embind__requireFunction', '$whenDependentTypesAreResolved', - '$craftInvokerFunction'], + '$craftInvokerFunction', '$cppTypeToJsType'], _embind_register_class_constructor: ( rawClassType, argCount, @@ -1801,17 +1922,43 @@ Originally allocated`); // `.stack` will add "at ..." after this sentence if (undefined === classType.registeredClass.constructor_body) { classType.registeredClass.constructor_body = []; } - if (undefined !== classType.registeredClass.constructor_body[argCount - 1]) { - throw new BindingError(`Cannot register multiple constructors with identical number of parameters (${argCount-1}) for class '${classType.name}'! Overload resolution is currently only performed using the parameter count, not actual type info!`); + + var rawSignatureArray = rawArgTypes.slice(1); + var rawSignatureString = rawSignatureArray.join(', '); + if (undefined !== classType.registeredClass.constructor_body[argCount - 1] && undefined !== classType.registeredClass.constructor_body[argCount - 1].signatures[rawSignatureString]) { + throw new BindingError(`Cannot register multiple constructors with identical javascript types of parameters for class '${classType.name}'!`); } - classType.registeredClass.constructor_body[argCount - 1] = () => { + + function unboundTypesHandler() { throwUnboundTypeError(`Cannot construct ${classType.name} due to unbound types`, rawArgTypes); - }; + } + + if (undefined === classType.registeredClass.constructor_body[argCount - 1]) { + classType.registeredClass.constructor_body[argCount - 1] = { + func: unboundTypesHandler, + signatures: {}, + signaturesArray: [] + } + } else { + delete classType.registeredClass.constructor_body[argCount - 1].func; + } + + classType.registeredClass.constructor_body[argCount - 1].signatures[rawSignatureString] = unboundTypesHandler; whenDependentTypesAreResolved([], rawArgTypes, (argTypes) => { // Insert empty slot for context type (argTypes[1]). argTypes.splice(1, 0, null); - classType.registeredClass.constructor_body[argCount - 1] = craftInvokerFunction(humanName, argTypes, null, invoker, rawConstructor); + + delete classType.registeredClass.constructor_body[argCount - 1].signatures[rawSignatureString]; + var func = craftInvokerFunction(humanName, argTypes, null, invoker, rawConstructor); + var signatureArray = rawSignatureArray.map(a => cppTypeToJsType(a)); + var signatureString = signatureArray.join(', '); + + if (undefined !== classType.registeredClass.constructor_body[argCount - 1].func) { + classType.registeredClass.constructor_body[argCount - 1].func = func; + } + classType.registeredClass.constructor_body[argCount - 1].signatures[signatureString] = func; + classType.registeredClass.constructor_body[argCount - 1].signaturesArray.push(signatureArray); return []; }); return []; @@ -1866,7 +2013,8 @@ Originally allocated`); // `.stack` will add "at ..." after this sentence _embind_register_class_function__deps: [ '$craftInvokerFunction', '$heap32VectorToArray', '$AsciiToString', '$embind__requireFunction', '$throwUnboundTypeError', - '$whenDependentTypesAreResolved', '$getFunctionName'], + '$whenDependentTypesAreResolved', '$getFunctionName', + '$cppTypeToJsType', '$ensureOverloadSignatureTable'], _embind_register_class_function: (rawClassType, methodName, argCount, @@ -1900,21 +2048,34 @@ Originally allocated`); // `.stack` will add "at ..." after this sentence var proto = classType.registeredClass.instancePrototype; var method = proto[methodName]; - if (undefined === method || (undefined === method.overloadTable && method.className !== classType.name && method.argCount === argCount - 2)) { + + var rawSignatureArray = rawArgTypes.slice(2); + var rawSignatureString = rawSignatureArray.join(', '); + if (undefined === method || (undefined === method.overloadTable && method.className !== classType.name && method.signature === rawSignatureString)) { // This is the first overload to be registered, OR we are replacing a // function in the base class with a function in the derived class. unboundTypesHandler.argCount = argCount - 2; + unboundTypesHandler.signature = rawSignatureString; unboundTypesHandler.className = classType.name; proto[methodName] = unboundTypesHandler; - } else { + } else if ( + (undefined === proto[methodName].overloadTable && proto[methodName].argCount !== argCount - 2) + || (undefined !== proto[methodName].overloadTable && undefined === proto[methodName].overloadTable[argCount - 2])) + { // There was an existing function with the same name registered. Set up // a function overload routing table. ensureOverloadTable(proto, methodName, humanName); + unboundTypesHandler.signature = rawSignatureString; proto[methodName].overloadTable[argCount - 2] = unboundTypesHandler; + } else { + ensureOverloadSignatureTable(proto, methodName, humanName, argCount - 2); + proto[methodName].overloadTable[argCount - 2].signatures[rawSignatureString] = unboundTypesHandler; } whenDependentTypesAreResolved([], rawArgTypes, (argTypes) => { var memberFunction = craftInvokerFunction(humanName, argTypes, classType, rawInvoker, context, isAsync); + var signatureArray = rawSignatureArray.map(a => cppTypeToJsType(a)); + var signatureString = signatureArray.join(', '); // Replace the initial unbound-handler-stub function with the // appropriate member function, now that all types are resolved. If @@ -1923,9 +2084,15 @@ Originally allocated`); // `.stack` will add "at ..." after this sentence if (undefined === proto[methodName].overloadTable) { // Set argCount in case an overload is registered later memberFunction.argCount = argCount - 2; + memberFunction.signature = signatureString; proto[methodName] = memberFunction; - } else { + } else if (undefined === proto[methodName].overloadTable[argCount - 2].signatures) { + memberFunction.signature = signatureString; proto[methodName].overloadTable[argCount - 2] = memberFunction; + } else { + delete proto[methodName].overloadTable[argCount - 2].signatures[rawSignatureString]; + proto[methodName].overloadTable[argCount - 2].signatures[signatureString] = memberFunction; + proto[methodName].overloadTable[argCount - 2].signaturesArray.push(signatureArray); } return []; @@ -2004,7 +2171,8 @@ Originally allocated`); // `.stack` will add "at ..." after this sentence _embind_register_class_class_function__deps: [ '$craftInvokerFunction', '$ensureOverloadTable', '$heap32VectorToArray', '$AsciiToString', '$embind__requireFunction', '$throwUnboundTypeError', - '$whenDependentTypesAreResolved', '$getFunctionName'], + '$whenDependentTypesAreResolved', '$getFunctionName', + '$cppTypeToJsType', '$ensureOverloadSignatureTable'], _embind_register_class_class_function: (rawClassType, methodName, argCount, @@ -2031,15 +2199,24 @@ Originally allocated`); // `.stack` will add "at ..." after this sentence } var proto = classType.registeredClass.constructor; + var rawSignatureArray = rawArgTypes.slice(1); + var rawSignatureString = rawSignatureArray.join(', '); if (undefined === proto[methodName]) { // This is the first function to be registered with this name. unboundTypesHandler.argCount = argCount-1; proto[methodName] = unboundTypesHandler; - } else { + } else if ( + (undefined === proto[methodName].overloadTable && proto[methodName].argCount !== argCount - 1) + || (undefined !== proto[methodName].overloadTable && undefined === proto[methodName].overloadTable[argCount - 1]) + ) { // There was an existing function with the same name registered. Set up // a function overload routing table. ensureOverloadTable(proto, methodName, humanName); + unboundTypesHandler.signature = rawSignatureString; proto[methodName].overloadTable[argCount-1] = unboundTypesHandler; + } else { + ensureOverloadSignatureTable(proto, methodName, humanName, argCount - 1); + proto[methodName].overloadTable[argCount-1].signatures[rawSignatureString] = unboundTypesHandler; } whenDependentTypesAreResolved([], rawArgTypes, (argTypes) => { @@ -2048,11 +2225,19 @@ Originally allocated`); // `.stack` will add "at ..." after this sentence // go into an overload table. var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */); var func = craftInvokerFunction(humanName, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn, isAsync); + var signatureArray = rawSignatureArray.map(a => cppTypeToJsType(a)); + var signatureString = signatureArray.join(', '); if (undefined === proto[methodName].overloadTable) { func.argCount = argCount-1; + func.signature = signatureString; proto[methodName] = func; - } else { + } else if (undefined === proto[methodName].overloadTable[argCount-1].signatures) { + func.signature = signatureString; proto[methodName].overloadTable[argCount-1] = func; + } else { + delete proto[methodName].overloadTable[argCount-1].signatures[rawSignatureString]; + proto[methodName].overloadTable[argCount-1].signatures[signatureString] = func; + proto[methodName].overloadTable[argCount-1].signaturesArray.push(signatureArray); } if (classType.registeredClass.__derivedClasses) { diff --git a/test/embind/embind.test.js b/test/embind/embind.test.js index 41ca7398b1687..33ec43d43fd8a 100644 --- a/test/embind/embind.test.js +++ b/test/embind/embind.test.js @@ -880,18 +880,47 @@ module({ assert.equal(b.WhichCtorCalled(), 2); var c = new cm.MultipleCtors(30, 30, 30); assert.equal(c.WhichCtorCalled(), 3); + var d = new cm.MultipleCtors("dummy"); + assert.equal(d.WhichCtorCalled(), 4); + var base = new cm.Base(); + var e = new cm.MultipleCtors(base); + assert.equal(e.WhichCtorCalled(), 5); + var derived = new cm.Derived(); + var f = new cm.MultipleCtors(derived); + assert.equal(f.WhichCtorCalled(), 5); a.delete(); b.delete(); c.delete(); + d.delete(); + e.delete(); + f.delete(); + base.delete(); + derived.delete(); }); test("access multiple smart ptr ctors", function() { var a = new cm.MultipleSmartCtors(10); assert.equal(a.WhichCtorCalled(), 1); - var b = new cm.MultipleCtors(20, 20); + var b = new cm.MultipleSmartCtors(20, 20); assert.equal(b.WhichCtorCalled(), 2); + var c = new cm.MultipleSmartCtors("dummy"); + assert.equal(c.WhichCtorCalled(), 3); + var base = new cm.Base(); + var d = new cm.MultipleSmartCtors(base); + assert.equal(d.WhichCtorCalled(), 4); + var derived = new cm.Derived(); + var e = new cm.MultipleSmartCtors(derived); + assert.equal(e.WhichCtorCalled(), 4); + var f = new cm.MultipleSmartCtors(a); + assert.equal(f.WhichCtorCalled(), 5); a.delete(); b.delete(); + c.delete(); + d.delete(); + e.delete(); + f.delete(); + base.delete(); + derived.delete(); }); test("wrong number of constructor arguments throws", function() { @@ -899,11 +928,18 @@ module({ assert.throws(cm.BindingError, function() { new cm.MultipleCtors(1,2,3,4); }); }); + test("wrong argument throws", function() { + assert.throws(cm.BindingError, function() { new cm.MultipleCtors(true); }); + assert.throws(cm.BindingError, function() { new cm.MultipleCtors(new cm.SecondBase()); }); + }); + test("overloading of free functions", function() { var a = cm.overloaded_function(10); assert.equal(a, 1); var b = cm.overloaded_function(20, 20); assert.equal(b, 2); + var c = cm.overloaded_function("dummy"); + assert.equal(c, 3); }); test("wrong number of arguments to an overloaded free function", function() { @@ -911,12 +947,19 @@ module({ assert.throws(cm.BindingError, function() { cm.overloaded_function(30, 30, 30); }); }); + test("wrong type to an overloaded free function", function() { + assert.throws(cm.BindingError, function() { cm.overloaded_function(true); }); + }); + test("overloading of class member functions", function() { var foo = new cm.MultipleOverloads(); assert.equal(foo.Func(10), 1); assert.equal(foo.WhichFuncCalled(), 1); assert.equal(foo.Func(20, 20), 2); assert.equal(foo.WhichFuncCalled(), 2); + assert.equal(foo.Func("dummy"), 3); + assert.equal(foo.WhichFuncCalled(), 3); + foo.delete(); }); @@ -932,6 +975,16 @@ module({ assert.throws(cm.BindingError, function() { cm.MultipleOverloads.StaticFunc(30, 30, 30); }); }); + test("wrong type to an overloaded class member function", function() { + var foo = new cm.MultipleOverloads(); + assert.throws(cm.BindingError, function() { foo.Func(true); }); + foo.delete(); + }); + + test("wrong type to an overloaded class static function", function() { + assert.throws(cm.BindingError, function() { cm.MultipleOverloads.StaticFunc(true); }); + }); + test("overloading of derived class member functions", function() { var foo = new cm.MultipleOverloadsDerived(); @@ -943,6 +996,9 @@ module({ assert.equal(foo.Func(20, 20), 2); assert.equal(foo.WhichFuncCalled(), 2); + assert.equal(foo.Func("dummy"), 3); + assert.equal(foo.WhichFuncCalled(), 3); + assert.equal(foo.Func(30, 30, 30), 3); assert.equal(foo.WhichFuncCalled(), 3); assert.equal(foo.Func(40, 40, 40, 40), 4); @@ -955,6 +1011,8 @@ module({ assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 1); assert.equal(cm.MultipleOverloads.StaticFunc(20, 20), 2); assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 2); + assert.equal(cm.MultipleOverloads.StaticFunc("dummy"), 3); + assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 3); }); test("overloading of derived class static functions", function() { diff --git a/test/embind/embind_test.cpp b/test/embind/embind_test.cpp index 1c8ca6e937419..d7fa241c08b1c 100644 --- a/test/embind/embind_test.cpp +++ b/test/embind/embind_test.cpp @@ -2563,6 +2563,11 @@ int overloaded_function(int i, int j) { return 2; } +int overloaded_function(std::string text) { + assert(text == "dummy"); + return 3; +} + class MultipleCtors { public: int value = 0; @@ -2582,6 +2587,14 @@ class MultipleCtors { assert(j == 30); assert(k == 30); } + MultipleCtors(std::string text) { + value = 4; + assert(text == "dummy"); + } + MultipleCtors(Base b) { + value = 5; + assert(b.name == "Base"); + } int WhichCtorCalled() const { return value; @@ -2591,6 +2604,7 @@ class MultipleCtors { class MultipleSmartCtors { public: int value = 0; + int constValue = 3; MultipleSmartCtors(int i) { value = 1; @@ -2601,6 +2615,19 @@ class MultipleSmartCtors { assert(i == 20); assert(j == 20); } + MultipleSmartCtors(std::string text) { + value = 3; + assert(text == "dummy"); + } + MultipleSmartCtors(Base b) { + value = 4; + assert(b.name == "Base"); + } + + MultipleSmartCtors(std::shared_ptr b) { + value = 5; + assert(b->constValue == 3); + } int WhichCtorCalled() const { return value; @@ -2626,6 +2653,12 @@ class MultipleOverloads { return 2; } + int Func(std::string text) { + assert(text == "dummy"); + value = 3; + return 3; + } + int WhichFuncCalled() const { return value; } @@ -2642,6 +2675,12 @@ class MultipleOverloads { return 2; } + static int StaticFunc(std::string text) { + assert(text == "dummy"); + staticValue = 3; + return 3; + } + static int WhichStaticFuncCalled() { return staticValue; } @@ -2740,11 +2779,14 @@ DummyForOverloads getDummy(DummyForOverloads d) { EMSCRIPTEN_BINDINGS(overloads) { function("overloaded_function", select_overload(&overloaded_function)); function("overloaded_function", select_overload(&overloaded_function)); + function("overloaded_function", select_overload(&overloaded_function)); class_("MultipleCtors") .constructor() .constructor() .constructor() + .constructor() + .constructor() .function("WhichCtorCalled", &MultipleCtors::WhichCtorCalled) ; @@ -2752,6 +2794,9 @@ EMSCRIPTEN_BINDINGS(overloads) { .smart_ptr>("shared_ptr") .constructor(&std::make_shared) .constructor(&std::make_shared) + .constructor(&std::make_shared) + .constructor(&std::make_shared) + .constructor(&std::make_shared>) .function("WhichCtorCalled", &MultipleSmartCtors::WhichCtorCalled) ; @@ -2759,9 +2804,11 @@ EMSCRIPTEN_BINDINGS(overloads) { .constructor<>() .function("Func", select_overload(&MultipleOverloads::Func)) .function("Func", select_overload(&MultipleOverloads::Func)) + .function("Func", select_overload(&MultipleOverloads::Func)) .function("WhichFuncCalled", &MultipleOverloads::WhichFuncCalled) .class_function("StaticFunc", select_overload(&MultipleOverloads::StaticFunc)) .class_function("StaticFunc", select_overload(&MultipleOverloads::StaticFunc)) + .class_function("StaticFunc", select_overload(&MultipleOverloads::StaticFunc)) .class_function("WhichStaticFuncCalled", &MultipleOverloads::WhichStaticFuncCalled) ;