/* * return a negative value on error * -1 : no method found * -2 : arguments do not matches * -3 : ambiguous matches */ int MetaObjectPrivate::findMethod(const std::string& nameWithOptionalSignature, const GenericFunctionParameters& args, bool* canCache) const { // We can keep this outside the lock because we assume MetaMethods can't be // removed MetaMethod* firstOverload = nullptr; { boost::recursive_mutex::scoped_lock sl(_methodsMutex); if (_dirtyCache) const_cast<MetaObjectPrivate*>(this)->refreshCache(); if (nameWithOptionalSignature.find(':') != nameWithOptionalSignature.npos) { // full name and signature was given, there can be only one match if (canCache) *canCache = true; NameToIdx::const_iterator itRev = _methodsNameToIdx.find(nameWithOptionalSignature); if (itRev == _methodsNameToIdx.end()) { std::string funname = qi::signatureSplit(nameWithOptionalSignature)[1]; // check if it's no method found, or if it's arguments mismatch if (_methodNameToOverload.find(funname) != _methodNameToOverload.end()) { return -2; } return -1; } else return itRev->second; } // Only name given, try to find an unique match with given argument count OverloadMap::const_iterator overloadIt = _methodNameToOverload.find(nameWithOptionalSignature); if (overloadIt == _methodNameToOverload.end()) { // no match for the name, no chance if (canCache) *canCache = true; return -1; } MetaMethod* firstMatch = nullptr; bool ambiguous = false; size_t nargs = args.size(); for (MetaMethod* mm = overloadIt->second; mm; mm=mm->_p->next) { assert(mm->name() == nameWithOptionalSignature); const Signature& sig = mm->parametersSignature(); if (sig == "m" || sig.children().size() == nargs) { if (firstMatch) { // this is the second match, ambiguity that needs args to resolve ambiguous = true; break; } else { firstMatch = mm; // go on to check for more matches } } } if (canCache) *canCache = !ambiguous || !firstMatch; if (!firstMatch) { //TODO.... return -2; // no match for a correct overload (bad number of args) } if (!ambiguous) { return firstMatch->uid(); } firstOverload = overloadIt->second; } int retval = -2; // resolve ambiguity by using arguments for (unsigned dyn = 0; dyn < 2; ++dyn) { // DO *NOT* hold the lock while resolving signatures dynamically. This // may block (and in case of python need the GIL) Signature sResolved = args.signature(dyn==1); { boost::recursive_mutex::scoped_lock sl(_methodsMutex); std::string resolvedSig = sResolved.toString(); std::string fullSig = nameWithOptionalSignature + "::" + resolvedSig; qiLogDebug() << "Finding method for resolved signature " << fullSig; // First try an exact match, which is much faster if we're lucky. NameToIdx::const_iterator itRev = _methodsNameToIdx.find(nameWithOptionalSignature); if (itRev != _methodsNameToIdx.end()) return itRev->second; using MethodsPtr = std::vector<std::pair<const MetaMethod*, float>>; MethodsPtr mml; // embed findCompatibleMethod for (MetaMethod* mm = firstOverload; mm; mm=mm->_p->next) { // still suboptimal, we are rescanning all overloads regardless of arg count float score = sResolved.isConvertibleTo(mm->parametersSignature()); if (score) mml.push_back(std::make_pair(mm, score)); } if (mml.empty()) continue; if (mml.size() == 1) return mml.front().first->uid(); // get best match MethodsPtr::iterator it = std::max_element(mml.begin(), mml.end(), less_pair_second()); int count = 0; for (unsigned i=0; i<mml.size(); ++i) { if (mml[i].second == it->second) ++count; } assert(count); if (count > 1) { qiLogVerbose() << generateErrorString(nameWithOptionalSignature, fullSig, const_cast<MetaObjectPrivate*>(this)->findCompatibleMethod(nameWithOptionalSignature), -3, false); retval = -3; } else return it->first->uid(); } } return retval; }
void SignalSubscriber::call(const GenericFunctionParameters& args, MetaCallType callType) { // this is held alive by caller if (handler) { bool async = true; if (threadingModel != MetaCallType_Auto) async = (threadingModel == MetaCallType_Queued); else if (callType != MetaCallType_Auto) async = (callType == MetaCallType_Queued); qiLogDebug() << "subscriber call async=" << async <<" ct " << callType <<" tm " << threadingModel; if (async) { GenericFunctionParameters* copy = new GenericFunctionParameters(args.copy()); // We will check enabled when we will be scheduled in the target // thread, and we hold this SignalSubscriber alive, so no need to // explicitly track the asynccall // courtesy-check of el, but it should be kept alive longuer than us qi::EventLoop* el = getEventLoop(); if (!el) // this is an assert basicaly, no sense trying to do something clever. throw std::runtime_error("Event loop was destroyed"); el->post(FunctorCall(copy, new SignalSubscriberPtr(shared_from_this()))); } else { // verify-enabled-then-register-active op must be locked { boost::mutex::scoped_lock sl(mutex); if (!enabled) return; addActive(false); } //do not throw bool mustDisconnect = false; try { handler(args); } catch(const qi::PointerLockException&) { qiLogDebug() << "PointerLockFailure excepton, will disconnect"; mustDisconnect = true; } catch(const std::exception& e) { qiLogWarning() << "Exception caught from signal subscriber: " << e.what(); } catch (...) { qiLogWarning() << "Unknown exception caught from signal subscriber"; } removeActive(true); if (mustDisconnect) source->disconnect(linkId); } } else if (target) { AnyObject lockedTarget = target->lock(); if (!lockedTarget) { source->disconnect(linkId); } else // no need to keep anything locked, whatever happens this is not used lockedTarget.metaPost(method, args); } }
//should be done in the object thread void RemoteObject::onMessagePending(const qi::Message &msg) { qiLogDebug() << this << "(" << _service << '/' << _object << ") msg " << msg.address() << " " << msg.buffer().size(); if (msg.object() != _object) { qiLogDebug() << "Passing message " << msg.address() << " to host " ; { boost::mutex::scoped_lock lock(_socketMutex); if(_socket) ObjectHost::onMessage(msg, _socket); return; } } if (msg.type() == qi::Message::Type_Event) { SignalBase* sb = signal(msg.event()); if (sb) { try { // Signal associated with properties have incorrect signature, // Trust MetaObject. //std::string sig = sb->signature(); const MetaSignal* ms = _self.metaObject().signal(msg.event()); qi::Signature sig = ms->parametersSignature(); // Remove top-level tuple //sig = sig.substr(1, sig.length()-2); //TODO: Optimise AnyReference value = msg.value((msg.flags()&Message::TypeFlag_DynamicPayload)? "m":sig, _socket); { GenericFunctionParameters args; if (sig == "m") args = value.content().asTupleValuePtr(); else args = value.asTupleValuePtr(); qiLogDebug() << "Triggering local event listeners with args : " << args.size(); sb->trigger(args); } value.destroy(); } catch (const std::exception& e) { qiLogWarning() << "Deserialize error on event: " << e.what(); } } else { qiLogWarning() << "Event message on unknown signal " << msg.event(); qiLogDebug() << metaObject().signalMap().size(); } return; } if (msg.type() != qi::Message::Type_Reply && msg.type() != qi::Message::Type_Error && msg.type() != qi::Message::Type_Canceled) { qiLogError() << "Message " << msg.address() << " type not handled: " << msg.type(); return; } qi::Promise<AnyReference> promise; { boost::mutex::scoped_lock lock(_promisesMutex); std::map<int, qi::Promise<AnyReference> >::iterator it; it = _promises.find(msg.id()); if (it != _promises.end()) { promise = _promises[msg.id()]; _promises.erase(it); qiLogDebug() << "Handling promise id:" << msg.id(); } else { qiLogError() << "no promise found for req id:" << msg.id() << " obj: " << msg.service() << " func: " << msg.function() << " type: " << Message::typeToString(msg.type()); return; } } switch (msg.type()) { case qi::Message::Type_Canceled: { qiLogDebug() << "Message " << msg.address() << " has been cancelled."; promise.setCanceled(); return; } case qi::Message::Type_Reply: { // Get call signature MetaMethod* mm = metaObject().method(msg.function()); if (!mm) { qiLogError() << "Result for unknown function " << msg.function(); promise.setError("Result for unknown function"); return; } try { qi::AnyReference val = msg.value( (msg.flags() & Message::TypeFlag_DynamicPayload) ? "m" : mm->returnSignature(), _socket); promise.setValue(val); } catch (std::runtime_error &err) { promise.setError(err.what()); } qiLogDebug() << "Message " << msg.address() << " passed to promise"; return; } case qi::Message::Type_Error: { try { static std::string sigerr("m"); qi::AnyReference gvp = msg.value(sigerr, _socket).content(); std::string err = gvp.asString(); qiLogVerbose() << "Received error message" << msg.address() << ":" << err; promise.setError(err); } catch (std::runtime_error &e) { //houston we have an error about the error.. promise.setError(e.what()); } return; } default: //not possible return; } }