示例#1
0
  /*
   * 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;
  }
示例#2
0
文件: signal.cpp 项目: cgestes/libqi
  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);
    }
  }
示例#3
0
  //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;
    }
  }