sewenew / redis-plus-plus

Redis client written in C++
Apache License 2.0
1.64k stars 351 forks source link

WIP:Add support c++20 coroutine #349

Closed fantasy-peak closed 2 years ago

fantasy-peak commented 2 years ago
#include <sw/redis++/async_redis++.h>

#include <iostream>
#include <utility>

#include <cppcoro/sync_wait.hpp>
#include <cppcoro/task.hpp>

int main() {
    sw::redis::ConnectionOptions opts;
    opts.host = "127.0.0.1";
    opts.port = 6379;
    sw::redis::ConnectionPoolOptions pool_opts;
    pool_opts.size = 3;
    sw::redis::AsyncRedis async_redis_cli(opts, pool_opts);
    boost::executors::basic_thread_pool pool(3);

    cppcoro::sync_wait([&]() -> cppcoro::task<> {
        try {
            std::unordered_map<std::string, std::string> m = {{"a", "b"}, {"c", "d"}};
            boost::future<void> hmset_res = async_redis_cli.hmset("hash", m.begin(), m.end());
            co_await sw::redis::FutureAwaiter{std::move(hmset_res), &pool};

            boost::future<bool> set_ret = async_redis_cli.set("name", "xiaoli");
            auto flag = co_await sw::redis::FutureAwaiter{std::move(set_ret), &pool};
            if (flag)
                std::cout << "set key success!!!" << std::endl;

            boost::future<std::optional<std::string>> get_data = async_redis_cli.get("name");
            auto data = co_await sw::redis::FutureAwaiter{std::move(get_data), &pool};
            if (data)
                std::cout << "name:" << data.value() << std::endl;

            boost::future<std::vector<std::string>> hgetall_res = async_redis_cli.hgetall<std::vector<std::string>>("hash");
            auto hgetall_data = co_await sw::redis::FutureAwaiter{std::move(hgetall_res)};
            for (auto& d : hgetall_data)
                std::cout << d << std::endl;
        } catch (const std::exception& e) {
            std::cout << "error:" << e.what() << std::endl;
        }
    }());
    return 0;
}

[15:36:39] root :: c837fca6f43a  ➜  /tmp » g++ test_boost_future.cpp -std=c++20 -lpthread -lcppcoro -lredis++ -fcoroutines -lboost_thread -lhiredis -luv -o test_boost_future_coro
[15:37:35] root :: c837fca6f43a  ➜  /tmp » ./test_boost_future_coro
set key success!!!
name:xiaoli
c
d
a
b

#include <sw/redis++/async_redis++.h>

#include <iostream>
#include <utility>

#include <cppcoro/sync_wait.hpp>
#include <cppcoro/task.hpp>

int main() {
    sw::redis::ConnectionOptions opts;
    opts.host = "127.0.0.1";
    opts.port = 6379;
    sw::redis::ConnectionPoolOptions pool_opts;
    pool_opts.size = 3;
    sw::redis::AsyncRedis async_redis_cli(opts, pool_opts);

    class MyExecutor : public sw::redis::Executor {
    public:
        virtual void enqueue(std::function<void()> cb) override {
            std::thread([cb = std::move(cb)]() mutable { cb(); }).detach();
        }
    };
    MyExecutor my_executor;

    cppcoro::sync_wait([&]() -> cppcoro::task<> {
        try {
            std::unordered_map<std::string, std::string> m = {{"a", "b"}, {"c", "d"}};
            std::future<void> hmset_res = async_redis_cli.hmset("hash", m.begin(), m.end());
            co_await sw::redis::FutureAwaiter{std::move(hmset_res), &my_executor};

            std::future<bool> set_ret = async_redis_cli.set("name", "xiaoli");
            auto flag = co_await sw::redis::FutureAwaiter{std::move(set_ret), &my_executor};
            if (flag)
                std::cout << "set key success!!!" << std::endl;

            std::future<std::optional<std::string>> get_data = async_redis_cli.get("key");
            auto data = co_await sw::redis::FutureAwaiter{std::move(get_data), &my_executor};
            if (data)
                std::cout << "data:" << data.value() << std::endl;

            std::future<std::vector<std::string>> hgetall_res = async_redis_cli.hgetall<std::vector<std::string>>("hash");
            auto hgetall_data = co_await sw::redis::FutureAwaiter{std::move(hgetall_res), &my_executor};
            for (auto& d : hgetall_data)
                std::cout << d << std::endl;
        } catch (const std::exception& e) {
            std::cout << "error:" << e.what() << std::endl;
        }
    }());
    return 0;
}

[15:42:25] root :: c837fca6f43a  ➜  /tmp/ » g++ test_std_future.cpp -std=c++20 -lpthread -lcppcoro -lredis++ -fcoroutines -lboost_thread -lhiredis -luv -o test_std_future_coro
[15:42:42] root :: c837fca6f43a  ➜  /tmp/ » ./test_std_future_coro                     
set key success!!!
data:hello world
c
d
a
b
sewenew commented 2 years ago

@fantasy-peak After some research on coroutine, I reviewed this PR. It looks like this is a generic future awaiter, which can be used to co_await any future type. Correct me if I'm wrong, since I'm still not quite familiar with coroutine.

If it is a generic future awaiter, I'm not sure if it's a good idea to add it to the library. Because it should be a generic facility for coroutine, not a specific one for redis-plus-plus.

Also, I'm not sure, if we can achieve something like the following:

CoRedis co_redis("tcp:://127.0.0.1");

optional<string> val = co_await co_redis.get("key");

If you have any idea on it, feel free to let me know :)

Sorry for the late reply, too busy these days.

Regards

fantasy-peak commented 2 years ago

@fantasy-peak After some research on coroutine, I reviewed this PR. It looks like this is a generic future awaiter, which can be used to co_await any future type. Correct me if I'm wrong, since I'm still not quite familiar with coroutine.

If it is a generic future awaiter, I'm not sure if it's a good idea to add it to the library. Because it should be a generic facility for coroutine, not a specific one for redis-plus-plus.

Also, I'm not sure, if we can achieve something like the following:

CoRedis co_redis("tcp:://127.0.0.1");

optional<string> val = co_await co_redis.get("key");

If you have any idea on it, feel free to let me know :)

Sorry for the late reply, too busy these days.

Regards

I'm very sorry for the late reply,Yes, this is a general-purpose awaiter for future, If there is CoRedis, I think it is excellent, But there will be a lot of work, We must wrap the asynchronous interface now,I also don't think it's a good idea to add it to the library, But at present , it is a temporary solution(redis-plus-plus use coroutine)

sewenew commented 2 years ago

@fantasy-peak I've added built-in support for coroutine (check this) for details.

Please try it, and any feedback or contribution is welcomed!

Regards