Передача лямбда-выражения в std :: function в C ++ - PullRequest
0 голосов
/ 02 февраля 2020

В настоящее время я борюсь с лямбда-выражениями. Я создал ConcurrentDictionary, который оборачивает std :: map мьютексом. Теперь я хотел бы экспортировать все элементы, которые соответствуют указанному условию.

template<typename MutexTypeT, typename Key_Type_T, typename Mapped_Type_T>
class CConcurrentDictionary
{
    public:
        ///Constructor
        CConcurrentDictionary();

        class CSingleElement
        {
        public:

          ///the key
          Key_Type_T key = { };

          ///THe Mapped Type
          Mapped_Type_T mapped_type = { };

        };

     template<typename ...Args_T>
        std::vector<CSingleElement> exportSelectedData(
            const uint32_t u32MutexTimeout,
            std::function<bool(const CSingleElement, Args_T &&...)> compareFn,
            Args_T&&...CompareArgs...) const;

}

template<typename MutexTypeT, typename Key_Type_T, typename Mapped_Type_T>
   template<typename ...Args_T>
   auto CConcurrentDictionary<MutexTypeT, Key_Type_T, Mapped_Type_T>::exportSelectedData(
          const uint32_t u32MutexTimeout,
          std::function<bool(const CSingleElement, Args_T &&...)> compareFn,
          Args_T&&...CompareArgs...) const ->
      std::vector<CSingleElement>
      {
        //lock mutex...
        std::vector<CSingleElement> vecRes;
        for (const auto &single_element : dictionary)
        {
          if(compareFn(single_element, CompareArgs...))
          {
            //this element mathes the preconditions
            vecRes.push_back(single_element);
          }
        }

        return vecRes;
      }

Однако, когда я пытаюсь использовать этот класс как этот

 class CTraceCmdDuration
  {

  public:


    ///Cmd Datatype
    using CmdType_T = uint32_t;

    class CCmdSummary
    {
    public:
      /**
       * SIngle Long Running Cmd Summary
       * @param CmdIdArg Cmd ID
       * @param u32MaxCmdDurationArg Max CmdDuration
       */
      CCmdSummary(const CmdType_T CmdIdArg, const uint32_t u32MaxCmdDurationArg);
      ///Id of this cmd
      const CmdType_T CmdId;

      ///Duration of this cmd
      const uint32_t u32MaxCmdDuration;
    };

/**
     * Exports all Cmds to a vector with took longer than u32MaxCmdDuration
     * @param u32MaxCmdDuration Maximal Cmd Processing duration time. Cmds that took longer than this will be exported
     * @param u32MutexTimeout Mutex Timeout
     * @return List with all long running cmds
     */
    std::vector<CCmdSummary> ExportLongRunningCmds(const uint32_t u32MaxCmdDuration, const uint32_t u32MutexTimeout) const;

}

  auto CTraceCmdDuration::ExportLongRunningCmds(const uint32_t u32MaxCmdDuration, const uint32_t u32MutexTimeout) const ->
      std::vector<CCmdSummary>
  {
    auto lambda = [u32MaxCmdDuration](const CombinedDictElement& singleElement)
        {
          const bool bRes = (u32MaxCmdDuration < singleElement.mapped_type.u32MaxProcessingDuration);
          return bRes;
        };
    auto vecRes = dict.exportSelectedData(u32MutexTimeout, lambda, u32MaxCmdDuration);
    return vecRes;
  }

Это, к сожалению, не вызывает соответствующего вызова функциональные ошибки

error: no matching function for call to 'NConcurrent_Container::CConcurrentDictionary<NMutex::CEmbos_Mutex, long unsigned int, NDebug::CTraceCmdDuration::CProcessingInfo>::exportSelectedData(const uint32_t&, NDebug::CTraceCmdDuration::ExportLongRunningCmds(uint32_t, uint32_t) const::__lambda0&, const uint32_t&) const'
     auto vecRes = dict.exportSelectedData(u32MutexTimeout, lambda, u32MaxCmdDuration);

Что не так с моим лямбда-выражением? Идея состояла в том, чтобы передать максимально допустимую продолжительность времени в качестве перехвата и каждый отдельный сохраненный элемент в std :: map в качестве аргумента.

Или у вас есть идея получше? Не могли бы вы помочь мне здесь?

Редактировать : Спасибо за ваш ответ, это хорошо работает, если я передам функцию stati c, но как мне передать лямбду в качестве аргумента шаблона?

static bool CompareDuaration(const CSingleElement&singleElement, const uint32_t u32MaxDuration);

  auto CTraceCmdDuration::ExportLongRunningCmds(const uint32_t u32MaxCmdDuration, const uint32_t u32MutexTimeout) const ->
      std::vector<CombinedDictElement>
  {
    auto vecRes = dict.exportSelectedData(u32MutexTimeout, lambda, u32MaxCmdDuration);
    return vecRes;
  }

Это работает, но

auto CTraceCmdDuration::ExportLongRunningCmds(const uint32_t u32MaxCmdDuration, const uint32_t u32MutexTimeout) const ->
      std::vector<CombinedDictElement>
  {

    auto lambda = [u32MaxCmdDuration](const CombinedDictElement& singleElement)
        {
          const bool bRes = (u32MaxCmdDuration < singleElement.mapped_type.u32MaxProcessingDuration);
          return bRes;
        };

    auto vecRes = dict.exportSelectedData(u32MutexTimeout, lambda, u32MaxCmdDuration);
    return vecRes;
  }

выдает ошибку компиляции

error: no match for call to '(CTraceCmdDuration::ExportLongRunningCmds(uint32_t, uint32_t) const::__lambda0) (CConcurrentDictionary<NMutex::CEmbos_Mutex, long unsigned int, CTraceCmdDuration::CProcessingInfo>::CSingleElement&, const long unsigned int&)'
       if(compareFn(singleElement, compareArgs...))

Это похоже на то, что передача лямбды в шаблоны не работает так хорошо. Что мне не хватает?

1 Ответ

2 голосов
/ 02 февраля 2020

Проблема

Проблема, с которой вы столкнулись, может быть сведена к следующему:

#include <functional>

template <typename ...Args>
void foo(std::function<bool(Args...)>) { }

int main()
{
    foo([](int, int) { return true; });
}

, которая не скомпилируется. Причина этого заключается в том, что вывод типа для аргументов шаблона для std::function завершается неудачей.

Вы ожидаете, что std::function некоторого вида будет передано в качестве аргумента. Поскольку объект std::function (независимо от точной реализации) не передается, компилятор пытается создать std::function и определить типы параметров шаблона, вызвав конструктор std::function. Здесь начинается проблема. Соответствующий конструктор в этом случае будет:

template <typename F>
function(F f);

Вы заметите, что сам конструктор также является шаблоном. Компилятор может успешно вывести F как лямбду, но, поскольку F является параметром шаблона конструктора, параметр шаблона самого класса std::function не может быть выведен.

Кроме того, для цитирования cppreference для этого конструктора:

[...] Этот конструктор не участвует в разрешении перегрузки, если только для параметра типа аргумента Args ... и типа R. [. ..]

Это означает, что существование этого конструктора основано на том, можно ли F вызывать с аргументами шаблона класса Args..., но поскольку они не определены явно и не могут быть выведены этот конструктор все равно не будет доступен.

Решение

Поскольку вы используете только std::function внутри exportSelectedData, просто сделайте его параметром шаблона полностью (исключая std::function) part):

template<typename Func, typename ...Args_T>
std::vector<CSingleElement> exportSelectedData(uint32_t u32MutexTimeout, Func compareFn, Args_T const&...) const;

Вам также следует изменить Args_T&& на Args_T const&, поскольку вы не просто перенаправляете эти аргументы, но повторно используете их внутри al oop.

E dit

Относительно вашего дополнительного вопроса в редакции: подумайте о том, что вы делаете.

Сначала вы объявляете лямбду:

auto lambda = [u32MaxCmdDuration](const CombinedDictElement& singleElement) { /* ... */ };

Теперь подумайте о подпись этой лямбды. Вы возвращаете логическое значение, поэтому тип возвращаемого значения bool (пока все хорошо). Вы берете u32MaxCmdDuration в предложении захвата и принимаете один аргумент singleElement. Давайте удалим все квалификаторы extra и посмотрим на подпись:

bool(CombinedDictElement) // take a CombinedDictElement and return a bool

Далее мы рассмотрим вызов exportSelectedData:

exportSelectedData(u32MutexTimeout, lambda, u32MaxCmdDuration);

Вы передаете u32MutexTimeout и lambda, что совершенно нормально, лямбда захвачена compareFn. Третий аргумент - u32MaxCmdDuration, который фиксируется ...compareArgs в вашем шаблоне. Теперь давайте посмотрим, где вы на самом деле вызываете лямбду внутри exportSelectedData:

if (compareFn(singleElement, compareArgs...)) // ...

Какую подпись вы ожидаете compareFn здесь? Если развернуть пакет ...compareArgs (опять же, для простоты удаляем extra квалификаторы), подпись будет выглядеть так:

bool(CombinedDictElement, unsigned int) // take a CombinedDictElement and an unsigned int and return a bool

Снова лямбда-сигнатура:

bool(CombinedDictElement)

Вы замечаете проблему? Лямбда захватывает u32MaxCmdDuration как захват состояния, тогда как exportSelectedData ожидает его в качестве параметра (поскольку вы передали его в exportSelectedData в качестве дополнительного аргумента). Очевидно, подписи отличаются друг от друга, поэтому мы должны изменить одну из них, чтобы она соответствовала другой. Это довольно просто в вашем случае, либо

  • измените лямбду на u32MaxCmdDuration в качестве параметра:

    auto lambda = [](const CombinedDictElement& singleElement, unsigned int u32MaxCmdDuration)
    

    или

  • удалить u32MaxCmdDuration в качестве дополнительного аргумента для вашего exportSelectedData вызова:

     exportSelectedData(u32MutexTimeout, lambda);
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...