haxpanel
haxpanel

Reputation: 4678

C++ Lambda - error: no matching function for call to

I am trying to pass a lambda as parameter to a function but once I attempt to access a variable inside the lambda which was declared outside, the build fails: 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*)>)'

I was thinking that the [&] would take care of capturing variables. I also tried [=] as well as [someVar], [&someVar].

I'm using 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);
  }
);

From the AWS library:

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);

}

Upvotes: 3

Views: 3284

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275966

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);

}

then a little utility type:

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)}; }
}

now we can do:

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()
);

Live example.


As I have learned you can modify AWS class, I'd do this:

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()
    );
}

where subscribe now takes a lambda with signature void(AWS_IoT_Client*, char*, uint16_t, IoT_Publish_Message_Params*).

Upvotes: 2

Max Langhof
Max Langhof

Reputation: 23711

The issue is that the AWS::subscribe function expects a function pointer, not a lambda. Capture-less lambdas can be converted to function pointers, but lambdas with captures (i.e. state) cannot.

You can see the "conventional" solution to this already in the signature: There is a void* parameter that you should pack all your callback-specific data into. Presumably this is the last argument of aws_iot_mqtt_subscribe that you currently set to NULL (prefer using nullptr btw).

This is uglier than using lambdas, but it's basically the only option for C-compatible library interfaces:

// 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);

Upvotes: 5

Related Questions