将 hiredis 与 Qt 应用集成

hiredis 与 Qt 应用框架结合使用。

Qt 是一个流行的跨平台 C++ 框架,您可以使用它来构建命令行和 GUI 应用。本指南解释了如何使用 hiredis 从 Qt 应用连接到 Redis 服务器。

安装 Qt

如果您尚未这样做,您应该首先下载并安装适用于您的开发平台的 Qt 开发环境。下面的示例简要解释了如何使用 Qt Creator 来管理您的项目,但请参阅 Qt Creator 文档以获取大量的示例和教程。

创建一个简单的应用

我们将使用一个简单的控制台应用来演示如何从 Qt 连接到 Redis。使用 **文件 > 新建项目** 命令在 Qt Creator 中创建应用项目。生成的源代码是一个单独的 C++ 文件,名为 main.cpp,它使用 QCoreApplication 对象来处理主事件循环。虽然它可以编译和运行,但在现阶段它并没有做任何有用的事情。

添加 hiredis 文件

如果您尚未这样做,请构建 hiredis(有关更多信息,请参阅 构建和安装)。

您还应该使 libhiredis 库可用于该项目。例如,如果您已将 cmake 的默认选项用作项目构建工具,并且您已将 hiredis.dylib.so 文件安装在 /usr/local/lib 中,您应该将以下行添加到 CMakeLists.txt 文件中

add_library(hiredis SHARED IMPORTED)
set_property(TARGET hiredis PROPERTY
             IMPORTED_LOCATION "/usr/local/lib/libhiredis.dylib")

您还应该修改 target_link_libraries 指令以包含 hiredis

target_link_libraries(ConsoleTest Qt${QT_VERSION_MAJOR}::Core hiredis)

添加代码以访问 Redis

您可以使用 Qt Creator 中项目文件夹上的 **添加新项** 上下文菜单添加类。以下各节给出了您应该添加到此类以连接到 Redis 的代码示例。该代码分为头文件和实现文件。

头文件

下面显示了一个名为 RedisExample 的类的头文件。代码后面会有一个解释。

// redisexample.h

#ifndef REDISEXAMPLE_H
#define REDISEXAMPLE_H

#include <QObject>

#include <hiredis/hiredis.h>
#include <hiredis/async.h>
#include <hiredis/adapters/qt.h>


class RedisExample : public QObject
{
    Q_OBJECT

public:
    // Constructor
    RedisExample(const char *keyForRedis, const char *valueForRedis, QObject *parent = 0)
        :QObject(parent), m_key(keyForRedis), m_value(valueForRedis) {}

public slots:
    // Slot method to hold the code that connects to Redis and issues
    // commands.
    void run();

signals:
    // Signal to indicate that our code has finished executing.
    void finished();

public:
    // Method to close the Redis connection and signal that we've
    // finished.
    void finish();

private:
    const char *m_key;          // Key for Redis string.
    const char *m_value;        // Value for Redis string.
    redisAsyncContext *m_ctx;   // Redis connection context.
    RedisQtAdapter m_adapter;   // Adapter to let `hiredis` work with Qt.
};

#endif // REDISEXAMPLE_H

QObject 是一个关键的 Qt 类,它实现了用于对象之间通信的 对象模型。当您在 Qt Creator 中创建类时,您可以指定您希望它成为 QObject 的子类(这将添加适当的头文件,并在类声明中包含 Q_OBJECT 宏)。

QObject 通信模型使用一些实例方法作为信号来报告事件,另一些实例方法作为来充当处理事件的回调(有关介绍,请参阅 信号和槽)。Qt 元对象编译器 识别类声明中的非标准 C++ 访问说明符 signals:slots:,并在编译期间为它们添加额外的代码以启用通信机制。

在我们的类中,有一个 run() 槽将实现访问 Redis 的代码。该代码最终会发出一个 finished() 信号,表明应用应该退出。

我们的简单示例代码只设置和获取一个 Redis 字符串 键。该类包含键和值的私有属性(遵循 Qt m_xxx 类成员命名约定)。这些由构造函数设置,并调用 QObject 构造函数。其他属性表示 Redis 的连接上下文(对于 Qt 应用,通常应该是异步)和一个 hiredis 用于与 Qt 集成的适配器对象。

实现文件

下面显示了实现头文件中声明的方法的文件。代码后面会有一个完整的解释。

// redisexample.cpp

#include <iostream>

#include "redisexample.h"


void RedisExample::finish() {
    // Disconnect gracefully.
    redisAsyncDisconnect(m_ctx);

    // Emit the `finished()` signal to indicate that the
    // execution is complete.
    emit finished();
}


// Callback used by our `GET` command in the `run()` method.
void getCallback(redisAsyncContext *, void * r, void * privdata) {

    // Cast data pointers to their appropriate types.
    redisReply *reply = static_cast<redisReply *>(r);
    RedisExample *ex = static_cast<RedisExample *>(privdata);

    if (reply == nullptr || ex == nullptr) {
        return;
    }

    std::cout << "Value: " << reply->str << std::endl;

    // Close the Redis connection and quit the app.
    ex->finish();
}


void RedisExample::run() {
    // Open the connection to Redis.
    m_ctx = redisAsyncConnect("localhost", 6379);

    if (m_ctx->err) {
        std::cout << "Error: " << m_ctx->errstr << std::endl;
        finish();
    }

    // Configure the connection to work with Qt.
    m_adapter.setContext(m_ctx);

    // Issue some simple commands. For the `GET` command, pass a
    // callback function and a pointer to this object instance
    // so that we can access the object's members from the callback.
    redisAsyncCommand(m_ctx, NULL, NULL, "SET %s %s", m_key, m_value);
    redisAsyncCommand(m_ctx, getCallback, this, "GET %s", m_key);
}

访问 Redis 的代码位于 run() 方法中(回想一下,这实现了 Qt 槽,将在响应信号时调用)。该代码连接到 Redis,并将连接上下文指针存储在类实例的 m_ctx 属性中。调用 m_adapter.setContext() 会初始化上下文的 Qt 支持。请注意,我们需要 Qt 的异步连接。有关更多信息,请参阅 异步连接

然后,该代码发出两个 Redis 命令,以 SET 使用该类的构造函数提供的字符串键和值。我们对该命令返回的响应不感兴趣,但我们对随后的 GET 命令的响应感兴趣。由于命令是异步的,我们需要设置一个回调来处理 GET 响应的到达。在 redisAsyncCommand() 调用中,我们将一个指向我们的 getCallback() 函数的指针,以及一个指向 RedisExample 实例的指针传递过去。这是一个自定义数据字段,它将简单地传递给回调函数执行(有关更多信息,请参阅 构造异步命令)。

getCallback() 函数中的代码首先将回复指针参数强制转换为 redisReply,并将自定义数据指针强制转换为 RedisExample。在这里,示例只是将回复字符串打印到控制台,但您可以以任何您喜欢的方式处理它。您可以将方法添加到您的类中,并使用在 redisAsyncCommand() 调用期间传递的自定义数据指针在回调中调用它们。在这里,我们只是使用指针来调用 finish() 方法。

finish() 方法调用 redisAsyncDisconnect() 来关闭连接,然后使用 Qt 信号机制发出 finished() 信号。您可能需要使用特定的连接上下文处理多个命令,但在完成使用后,应从回调中关闭它。

主程序

要访问 RedisExample 类,您应该在 main.cpp 中定义的 main() 函数中使用如下代码:

#include <QCoreApplication>
#include <QTimer>

#include "redisexample.h"


int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    // Instance of our object.
    RedisExample r("url", "https://redis.ac.cn/");

    // Call the `run()` slot on our `RedisExample` instance to
    // run our Redis commands. 
    QTimer::singleShot(0, &r, SLOT(run()));

    // Set up a communication connection between our `finished()`
    // signal and the application's `quit()` slot.
    QObject::connect(&r, SIGNAL(finished()), &app, SLOT(quit()));

    // Start the app's main event loop.
    return app.exec();
}

这会创建 QCoreApplication 实例,该实例管理控制台应用的主事件循环。 然后,它使用键 ("url") 和值 ("https://redis.ac.cn/") 为我们的 Redis 字符串创建 RedisExample 的实例。

下面的两行代码设置了应用程序的 QObject 通信机制。 调用 QTimer::singleShot() 会激活我们 RedisExample 实例上的 run() 槽方法。 QObject::connect() 调用会在 RedisExample 实例的 finished() 信号和我们的 QCoreApplication 实例的 quit() 槽之间创建一个通信链接。 当 RedisExample 对象发出 finished() 信号时,这将退出应用程序事件循环并退出应用程序。 这会在 GET 命令回调结束时调用 finish() 方法时发生。

运行代码

添加代码后,您可以从 Qt Creator 的构建菜单或窗口左侧的工具栏运行它。 假设与 Redis 的连接成功,它将打印消息 Value: https://redis.ac.cn/ 并退出。 您可以使用 KEYS 命令从 redis-cliRedis Insight 检查 “url” 字符串键是否已添加到 Redis 数据库中。

关键信息

您可以使用多种方法将 Redis 与 Qt 应用程序一起使用,但我们的示例演示了一些广泛适用的技术

  • 使用 QObject 通信机制来简化您的代码。
  • 使用 hiredis 异步 API。 向代码中添加 RedisQtAdapter 实例,并确保在发出 Redis 命令之前调用其 setContext() 方法对其进行初始化。
  • 将所有与 Redis 交互所需的代码和数据(包括连接上下文)放在一个类中,或确保可以通过指针和 Qt 信号从一个类中访问它。 使用 redisAsyncCommand() 发出 Redis 命令时,在自定义数据参数中传递指向您的类的实例的指针,并使用它来处理回复或从回调中发出更多命令。
为此页面评分
返回顶部 ↑