如何在编写函数中使用boost :: asio :: defer()?

时间:2018-05-04 13:53:18

标签: c++ boost boost-asio boost-beast

在Boost 1.66上,Asio拥有deprecated /** * Code goes in functions.php or a custom plugin. */ add_filter( 'woocommerce_states', 'custom_woocommerce_states' ); function custom_woocommerce_states( $states ) { $states['XX'] = array( 'XX1' => 'State 1', 'XX2' => 'State 2' ); return $states; } 挂钩功能,促进asio_handler_is_continuation功能的使用。当asio_handler_is_continuation == true时,It seems that defer函数的行为与defer完全相同。但是,使用post的方式与使用defer的方式不同,我不确定如何正确使用asio_handler_is_continuation

编辑:我认为以下示例过于冗长,无法清楚地表达我的意思。这是一个较短的例子:

defer

现在async_read_until(stream, read_buffer, "\r\n", [](boost::system::error_code ec, std::size_t bytes_transferred) { if(!ec) async_write(stream, write_buffer, some_handler); }) 完成后,将使用等效于async_read_until的某些方法调用传递的lambda处理程序。但是lambda处理程序中的boost::asio::post是从最后一个异步任务继续的,所以我想使用async_write调用lambda处理程序来进行优化的冒险。

有没有办法在上面的示例中使用defer(而不是defer)来调用lambda处理程序?

原始帖子:我正在尝试编写一个与beast document类似的简单初始化函数post,但调用async_echo的部分将会被称为延续。为实现此目的,先前的中间操作boost::asio::async_write必须将处理程序boost::asio::async_read_until作为延续。

这是我在野兽文档的async_echo示例中引用的部分:

*this

在1.66天之前,我可以按如下方式挂钩该功能:

template<class AsyncStream, class Handler>
void echo_op<AsyncStream, Handler>::
operator()(boost::beast::error_code ec, std::size_t bytes_transferred)
{
    // Store a reference to our state. The address of the state won't
    // change, and this solves the problem where dereferencing the
    // data member is undefined after a move.
    auto& p = *p_;

    // Now perform the next step in the state machine
    switch(ec ? 2 : p.step)
    {
        // initial entry
        case 0:
            // read up to the first newline
            p.step = 1;
            return boost::asio::async_read_until(p.stream, p.buffer, "\r", std::move(*this));

        case 1:
            // write everything back
            p.step = 2;
            // async_read_until could have read past the newline,
            // use buffers_prefix to make sure we only send one line
            return boost::asio::async_write(p.stream,
                boost::beast::buffers_prefix(bytes_transferred, p.buffer.data()), std::move(*this));

        case 2:
            p.buffer.consume(bytes_transferred);
            break;
    }

    // Invoke the final handler. The implementation of `handler_ptr`
    // will deallocate the storage for the state before the handler
    // is invoked. This is necessary to provide the
    // destroy-before-invocation guarantee on handler memory
    // customizations.
    //
    // If we wanted to pass any arguments to the handler which come
    // from the `state`, they would have to be moved to the stack
    // first or else undefined behavior results.
    //
    p_.invoke(ec);
    return;
}

template <Function, Handler> friend bool asio_handler_is_continuation(echo_op<Function, Handler>* handler) { using boost::asio::asio_handler_is_continuation; return handler.p_->step == 1 || asio_handler_is_continuation(std::addressof(handler.p_->handler())); } 的声明中。

从Boost 1.66开始,上面的代码不太可能产生任何影响(没有echo_op宏)。所以我应该使用BOOST_ASIO_NO_DEPRECATION

但是因为defera guarantee&#34;处理程序的调用将以与使用boost :: asio :: io_context :: post()等效的方式执行。&#34 ;,boost::asio::async_read_until不会使用*this调用,也就是说,作为延续。

是否有任何变通方法使defer使用boost::asio::async_read_until调用处理程序?是否有使用defer函数的好例子?

2 个答案:

答案 0 :(得分:4)

这也困扰了我。

除了此注释之外,

Executor::deferExecutor::post都执行相同的操作:

  

注意:虽然对延迟的要求与post相同,但post的使用传达了一个偏好,即调用者不会阻止f1进程的第一步,而延迟传达了调用者阻止的首选项f1的第一步。延迟的一个用途是传达调用者的意图,即f1是当前调用上下文的延续。执行者可以使用该信息来优化或以其他方式调整调用f1的方式。 - 注意

https://www.boost.org/doc/libs/1_67_0/doc/html/boost_asio/reference/Executor1.html

因此,链接延续的责任似乎已成为Executor模型的实现细节。

据我所知,这意味着您需要做的只是调用自由函数defer(executor, handler)而执行者将做正确的事情&#39;

更新

找到一些文档,展示如何通过最终执行程序链接处理程序:

文档来源:https://github.com/chriskohlhoff/asio-tr2/blob/master/doc/executors.qbk

示例:https://github.com/chriskohlhoff/executors/blob/v0.2-branch/src/examples/executor/async_op_2.cpp

请参阅async_op_2.cpp中的第38行

答案 1 :(得分:1)

在玩了一下之后,事实证明asio_handler_is_continuation没有被弃用;并且目前无法用defer替换它。

要将post次调用重定向到defer,我提供了以下自定义执行者:

template<typename UnderlyingExecutor, typename std::enable_if<boost::asio::is_executor<UnderlyingExecutor>::value, int>::type = 0>
class continuation_executor
{
    private:
        UnderlyingExecutor _ex;

    public:

        continuation_executor(UnderlyingExecutor ex)
            :_ex(ex){}

        template<class Function, class Allocator>
        void post(Function f, Allocator a)
        {
            std::cout<<"Redirected to defer()"<<std::endl;
            _ex.defer(BOOST_ASIO_MOVE_CAST(Function)(f),a);
        }

        template<class Function, class Allocator>
        void defer(Function f, Allocator a)
        {
            std::cout<<"defer() called"<<std::endl;
            _ex.defer(BOOST_ASIO_MOVE_CAST(Function)(f),a);
        }

        template<class Function, class Allocator>
        void dispatch(Function f, Allocator a)
        {
            std::cout<<"dispatch() called"<<std::endl;
            _ex.dispatch(BOOST_ASIO_MOVE_CAST(Function)(f),a);
        }

        auto context() -> decltype(_ex.context())
        {
            return _ex.context(); 
        }

        void on_work_started()
        {
            _ex.on_work_started();
        }
        void on_work_finished()
        {
            _ex.on_work_finished();
        }
};

它实际上是一个微不足道的执行者,完全依赖于底层执行者,continuation_executor::post重定向到基础执行者的defer

但是当我将处理程序传递给async_read_some之类的bind_executor(conti_exec, handler)时,我得到以下输出:

dispatch() called

因此传递的处理程序不会通过post()进行调度;它是通过其他方式安排的。具体而言,像asio::async_read_some这样的内置异步函数通过scheduler::post_immediate_completion调度内部操作对象,然后io_context::run执行操作。

完成异步操作后,将调用操作对象的complete方法来执行用户提供的处理程序。至少在当前实现方面,complete方法使用关联的执行程序的dispatch方法来运行处理程序。上面没有钩子的地方。所以这完全是荒凉的;尝试使用defer代替asio_handler_is_continuation是不合时宜的。

我在问题上所说的,&#34;从Boost 1.66开始,上面的代码不太可能产生任何影响(没有BOOST_ASIO_NO_DEPRECATION宏)。&#34;,是完全错误的。 asio_handler_is_continuation仍然有效,且为not deprecated as of 1.67

这是asio_handler_is_continuation仍然有效的证据:

  // Start an asynchronous send. The data being sent must be valid for the
  // lifetime of the asynchronous operation.
  template <typename ConstBufferSequence, typename Handler>
  void async_send(base_implementation_type& impl,
      const ConstBufferSequence& buffers,
      socket_base::message_flags flags, Handler& handler)
  {
    bool is_continuation =
      boost_asio_handler_cont_helpers::is_continuation(handler);

    // Allocate and construct an operation to wrap the handler.
    typedef reactive_socket_send_op<ConstBufferSequence, Handler> op;
    typename op::ptr p = { boost::asio::detail::addressof(handler),
      op::ptr::allocate(handler), 0 };
    p.p = new (p.v) op(impl.socket_, impl.state_, buffers, flags, handler);

    BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",
          &impl, impl.socket_, "async_send"));

    start_op(impl, reactor::write_op, p.p, is_continuation, true,
        ((impl.state_ & socket_ops::stream_oriented)
          && buffer_sequence_adapter<boost::asio::const_buffer,
            ConstBufferSequence>::all_empty(buffers)));
    p.v = p.p = 0;
  }

请注意,它使用boost_asio_handler_cont_helpers来确定处理程序是否为continuation。 boost_asio_handler_cont_helpers在内部调用asio_handler_is_continuation

内部async_send使用

async_write_some。我没有检查asio库提供的每个内置异步任务,但我很确定其他异步任务以相同的方式执行它的处理程序。

因此,如果您希望内置异步任务以继续执行处理程序,则必须依赖asio_handler_is_continuationdefer不会完全替换它! defer只能在您直接从代码中安排处理程序时使用。