chriskohlhoff / asio

Asio C++ Library
http://think-async.com/Asio
4.73k stars 1.2k forks source link

Propagating a completion handler indefinitely for later calling - Docs unclear or operator error? #1315

Closed systocrat closed 1 year ago

systocrat commented 1 year ago

Hello,

I'd like to write some code that looks like this:

class meaningful_data;

template <typename BlobProducer>
class blob_processor {
public:
    template <typename RunToken>
    auto async_start(RunToken && token) {
        auto coro = [](shared_ptr<blob_processor> self) {
            self->producer->template async_run(detached);

            std::string message;

            while (!self->cancelled_) {
            // this actually should use the same mechanism of storing a callback underneath
                message = co_await self->producer->template async_next_message(use_awaitable);

                self->fresh_data_ = true;
                self->data_ = process_message(message);

                if (cb_ != nullptr) {
                    self->cb_(success, self->data_);
                }
            }
        };

        co_spawn(execution_context, coro(shared_from_this()), token);
    }

    template <typename DataToken>
    auto next_meaningful_data(DataToken && token) {
        auto init = [](auto && handler, shared_ptr<blob_processor> self) {
            self->cb_ = handler;

            if (self->fresh_data_) {
                post(execution_context, [cb=std::move(self->cb), data=std::move(data_)]() {
                    cb(success, data);
                });
            }
        };

        return async_initiate<DataToken, void(const error_code&, meaningful_data&)>(init, token, shared_from_this());
    }

    // cancellation code omitted

    private:
    shared_ptr<BlobProducer> producer_;
    callback_type cb_ = nullptr;
    bool fresh_data_ = false;
    meaningful_data data_ = {};
}

In which I have a data processor that waits for a message from an underlying implementation with its own async functions.

Since I don't have any idea when the next message is coming, and asking for a message doesn't inherently initiate any asynchronous operations, I'd like to store a callback for later. I'd like to be able to pass in any kind of completion token without treating them any differently from one another.

The issue I'm running into is with propagating the handlers that my initiation function in async_initiate is called with. Since I don't know the type of the completion handler at compile time, I can't reasonably store it in a class member.

How should I approach this problem?

andrei-datcu commented 1 year ago

Easiest (and probably best): use an asio::experimental::channel Alternatively, use any_completion_handler

systocrat commented 1 year ago

Channel seems to work great for me, and good shout about any_completion_handler- That'll be useful elsewhere. Cheers!