04-Timer.4-Using a member function as a completion handler

在本教程中,我们将学习如何将类成员函数用作完成处理程序。该程序的执行效果应与教程Timer.3中的示例程序完全相同。

#include <functional>
#include <iostream>

#include <asio.hpp>
#include <spdlog/spdlog.h>

// 不同于早期教程程序中那样将自由函数print定义为完成处理程序,我们现在定义了一个名为printer的类。
class Printer {
public:
    // 该类的构造函数将接收一个对io_context对象的引用,并在初始化m_timer时使用它。
    // 用于关闭程序的计数器现在也是该类的成员。
    Printer(asio::io_context& _io, int _n = 1)
        : m_timer(_io, std::chrono::seconds(1))
        , m_count(0)
    {
        // std::bind函数不仅适用于自由函数,也同样适用于类成员函数。
        // 由于所有非静态类成员函数都有一个隐式的this参数,我们需要将this绑定到函数上。如教程Timer.3所示,
        // std::bind将我们的完成处理程序(现在是一个成员函数)转换为一个函数对象,
        // 可以像具有void(const std::error_code&)签名的函数一样被调用。
        //
        // 您会注意到这里没有指定asio::placeholders::error占位符,
        // 因为print成员函数不接受错误对象作为参数。
        m_timer.async_wait(std::bind(&Printer::print, this, _n));
    }

    ~Printer()
    {
        spdlog::info("Final count = {}", m_count);
    }

    // print成员函数与教程Timer.3中的print函数非常相似,
    // 不同之处在于它现在操作的是类的数据成员,而不是将计时器和计数器作为参数传入。
    void print(int _n)
    {
        if (m_count >= _n) {
            return;
        }

        m_count++;
        spdlog::info("Count = {}", m_count);
        if (m_count < _n) {
            m_timer.expires_at(m_timer.expiry() + asio::chrono::seconds(1));
            m_timer.async_wait(std::bind(&Printer::print, this, _n));
        }
    }

private:
    asio::steady_timer m_timer;
    int m_count {};
};

// 主函数比之前简单多了,因为它现在在正常运行io_context之前声明了一个本地打印机对象。
int main()
{
    spdlog::info("Starting...");

    asio::io_context io;
    Printer pointer(io, 10);
    io.run();

    return 0;
}