c++ - 在C++中的类中实现回调函数

标签 c++ oop callback

我想实现一种解决方法,使用非静态类作为回调函数。我正在使用 Eclipse Paho MQTT 代码。以下类型被实现并用作回调:

typedef void MQTTAsync_onSuccess(void* context, MQTTAsync_successData* response);

MQTTAsync_onSuccess* onSuccess;

onSuccess = myStaticCallback;

void myStaticCallback (void* context, MQTTAsync_successData* response)
{
   //...callback actions...
}

我想包装这个C API(不修改现有的MQTT C API)并实现属于对象/类的非静态/非集中回调函数。

typedef void MQTTAsync_onSuccess(void* context, MQTTAsync_successData* response);

class myMQTTClass
{
   private:
      void myCallback (void* context, MQTTAsync_successData* response);
      MQTTAsync_onSuccess* onSuccess;

   public:
      void foo (void)
      {
          this->onSuccess = this->myCallback;
      }
}

正如您可能猜到的,上面的代码会导致错误: 无法将 myCallback 从类型 'void (myMQTTClass::) (void*, MQTTAsync_successData*)' 转换为类型 'void (*)(void*, MQTTAsync_successData*)'

非常感谢有关如何解决此问题或任何解决方法的任何指导。我愿意提供任何可能缺失的信息。提前致谢。

编辑:实际代码有一些遗漏

   namespace rover
    {
       typedef struct
       {
        char * clientID;
        char * topic;
        char * payload;
        int         qos;        // 1
        long int    timeout;    // Such as 10000L usec
       } RoverMQTT_Configure_t;

       class RoverPahoMQTT
       {
           public:
              RoverPahoMQTT (char * host_name, int port, RoverMQTT_Configure_t MQTT_Configure);

           private:

             /**
             * @brief Host name used for connecting to the Eclipse Paho MQTT server
             */
            char * HOST_NAME;

            /**
             * @brief Port used for connecting to the Eclipse Paho MQTT server
             */
            int PORT;

            RoverMQTT_Configure_t rover_MQTT_configure;

            /* Internal attributes */
            MQTTAsync client;

            /**
             * @brief Connect options
             */
            MQTTAsync_connectOptions conn_opts;

            /**
             * @brief Disconnect options
             */
            MQTTAsync_disconnectOptions disc_opts;

             //...
             static void onPublisherConnect (void* context, MQTTAsync_successData* response);
             void onPublisherConnect_ (MQTTAsync_successData* response);

           //...

       }
    }

    int rover::RoverPahoMQTT::publish (void)
    {
        this->flushFlags ();

        this->conn_opts = MQTTAsync_connectOptions_initializer;
        this->client = new MQTTAsync;
        int rc;

        char my_addr[20];
        this->constructAddress (my_addr);
        printf ("address: %s", my_addr);
        MQTTAsync_create (  &(this->client),
                            my_addr,
                            this->rover_MQTT_configure.clientID,
                            MQTTCLIENT_PERSISTENCE_NONE,
                            NULL);

        MQTTAsync_setCallbacks(this->client, NULL, onConnectionLost, NULL, NULL);

        conn_opts.keepAliveInterval = 20;
        conn_opts.cleansession = 1;
        conn_opts.onSuccess = rover::RoverPahoMQTT::onPublisherConnect;
        conn_opts.onFailure = onConnectFailure;
        conn_opts.context = this->client;

        if ((rc = MQTTAsync_connect(this->client, &(this->conn_opts))) != MQTTASYNC_SUCCESS)
        {
            printf("Failed to start connect, return code %d\n", rc);
            return rc;
        }

        /*printf("Waiting for publication of %s\n"
             "on topic %s for client with ClientID: %s\n",
             PAYLOAD, TOPIC, CLIENTID);*/
        while (!mqtt_finished)
            usleep(this->rover_MQTT_configure.timeout);

        MQTTAsync_destroy(&client);
        return rc;
    }

    void rover::RoverPahoMQTT::onPublisherConnect_(MQTTAsync_successData* response)
    {
        MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
        MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
        int rc;

        printf("Successful connection\n");

        opts.onSuccess = onPublisherSend;
        opts.context = client;

        pubmsg.payload = &default_MQTT_configure.payload;
        pubmsg.payloadlen = strlen(default_MQTT_configure.payload);
        pubmsg.qos = default_MQTT_configure.qos;
        pubmsg.retained = 0;
        deliveredtoken = 0;

        if ((rc = MQTTAsync_sendMessage(client, default_MQTT_configure.topic, &pubmsg, &opts)) != MQTTASYNC_SUCCESS)
        {
            printf("Failed to start sendMessage, return code %d\n", rc);
            exit(EXIT_FAILURE);
        }
    }

    void rover::RoverPahoMQTT::onPublisherConnect (void* context, MQTTAsync_successData* response)
    {
        rover::RoverPahoMQTT* m = (rover::RoverPahoMQTT*) context;
        m->onPublisherConnect_(response);
        //((rover::RoverPahoMQTT*)context)->onPublisherConnect_(response);
        // ^^^HERE IS THE SEGMENTATION FAULT

    }

最佳答案

正如明确指出的here ,回调必须是

registered with the client library by passing it as an argument in MQTTAsync_responseOptions

并且 context 参数是

pointer to the context value originally passed to MQTTAsync_responseOptions, which contains any application-specific context.

我建议您的类使用一个通用接口(interface),它提供了一个与回调原型(prototype)匹配的静态方法:

class myMQTTClass
{
public:
  static void callback(void* context, MQTTAsync_successData* response)
  {
    myMQTTClass * m = (myMQTTClass*)context;
    m->myCallback(response);
  }
protected:
  virtual void myCallback(MQTTAsync_successData* response) = 0;
};

您现在可以在子类中实现不同的行为:

class myMQTTClassImpl : public myMQTTClass
{
protected:
  void myCallback(MQTTAsync_successData *response)
  {
    std::cout << "success!!!" << std::endl;
  }
};

让我们看看如何使用它:

int main()
{
  myMQTTClass * m = new myMQTTClassImpl();

  MQTTAsync_responseOptions options;
  options.onSuccess = myMQTTClass::callback;
  options.context = m;
}

编辑(指发布的实际代码):

在您的 publish 方法中,这是正确的:

conn_opts.onSuccess = rover::RoverPahoMQTT::onPublisherConnect;

这是错误的:

conn_opts.context = this->client;

应该是:

conn_opts.context = this;

关于c++ - 在C++中的类中实现回调函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47731244/

相关文章:

javascript - XmlHttpRequest 请求失败时获取回调响应

c++ - 带参数的函数 vector

c++ - header 包含和循环依赖

c# - 如何避免在服务层上引用 Entity Framework 的需要?

node.js - 从导出函数回调读取结果

javascript - 原型(prototype)对象继承

java - 存储对不同类的对象的引用

c++ - ifstream::ifstream 可以读取的最大文件大小是多少

c++ - 如何使用 C++ 在 Windows 中创建守护线程?

C++数组问题