C ++ Лямбда - ошибка: нет подходящей функции для вызова - PullRequest
0 голосов
/ 05 сентября 2018

Я пытаюсь передать лямбду как параметр функции, но как только я пытаюсь получить доступ к переменной внутри лямбды, которая была объявлена ​​снаружи, сборка завершается неудачно: error: no matching function for call to 'AWS::subscribe(char [128], mainTask(void*)::<lambda(AWS_IoT_Client*, char*, uint16_t, IoT_Publish_Message_Params*, void*)>)'

Я думал, что [&] позаботится о захвате переменных. Я также пытался [=], а также [someVar], [&someVar].

Я использую C ++ 11.

char someVar[128];

aws->subscribe(
  topic,
  [&] (AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, IoT_Publish_Message_Params *params, void *pData) {
    char *text = (char *)params->payload;
    sprintf(someVar, "%s", text);
  }
);

Из библиотеки AWS:

void AWS::subscribe(const char *topic,
                    pApplicationHandler_t iot_subscribe_callback_handler) {
  m_error =
      ::aws_iot_mqtt_subscribe(&m_client, topic, (uint16_t)std::strlen(topic),
                              QOS1, iot_subscribe_callback_handler, NULL);

}

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018
void AWS::subscribe(const char *topic,
                    pApplicationHandler_t iot_subscribe_callback_handler,
                    void* ptr) {
  m_error =
      ::aws_iot_mqtt_subscribe(&m_client, topic, (uint16_t)std::strlen(topic),
                              QOS1, iot_subscribe_callback_handler, ptr);

}

затем маленькая утилита типа:

namespace utils {
  template<class F>
  struct c_style_callback_t {
    F f;
    template<class...Args>
    static void(*get_callback())(Args..., void*) {
      return [](Args...args, void* fptr)->void {
        (*static_cast<F*>(fptr))(std::forward<Args>(args)...);
      };
    }
    void* get_pvoid() {
      return std::addressof(f);
    }
  };
  template<class F>
  c_style_callback_t< std::decay_t<F> >
  c_style_callback( F&& f ) { return {std::forward<F>(f)}; }
}

Теперь мы можем сделать:

auto task = utils::c_style_callback(
  [&] (AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, IoT_Publish_Message_Params *params) {
    char *text = (char *)params->payload;
    sprintf(someVar, "%s", text);
  }
);
aws->subscribe(
  topic,
  task.get_callback<AWS_IoT_Client*, char*, uint16_t, IoT_Publish_Message_Params*>(),
  task.get_pvoid()
);

Живой пример .


Как я уже понял, вы можете изменить класс AWS, я бы сделал это:

template<class Handler>
void AWS::subscribe(const char *topic,
                    Handler iot_subscribe_callback_handler) {
  auto task = utils::c_style_callback(iot_subscribe_callback_handler);

  m_error =
    ::aws_iot_mqtt_subscribe(
      &m_client,
      topic,
      (uint16_t)std::strlen(topic),
       QOS1,
      task.get_callback<AWS_IoT_Client*, char*, uint16_t, IoT_Publish_Message_Params*>(),
      task.get_pvoid()
    );
}

где subscribe теперь принимает лямбду с подписью void(AWS_IoT_Client*, char*, uint16_t, IoT_Publish_Message_Params*).

0 голосов
/ 05 сентября 2018

Проблема в том, что функция AWS::subscribe ожидает указатель на функцию, а не лямбду. Лямбды без захвата могут быть преобразованы в указатели функций, но лямбды с захватами (то есть состоянием) не могут.

«Обычное» решение этой проблемы можно увидеть уже в подписи: есть параметр void*, в который следует упаковать все данные, относящиеся к обратному вызову. Предположительно, это последний аргумент aws_iot_mqtt_subscribe, который вы в настоящее время устанавливаете на NULL (предпочтительнее использовать nullptr btw).

Это ужаснее, чем использование лямбда-выражений, но в основном это единственный вариант для C-совместимых библиотечных интерфейсов:

// Your callback (could also be a capture-less lambda):
void callbackFunc(/* etc. */, void *pData) 
{
    std::string* someVarPtr = static_cast<std::string*>(pData);
    char *text = (char *)params->payload;
    sprintf(*someVarPtr, "%s", text);
}

// To subscribe:
std::string someVar;
void* callbackData = &someVar; // Or a struct containing e.g. pointers to all your data.
aws_iot_mqtt_subscribe(/* etc. */, callbackFunc, callbackData);
...