banner
cells

cells

为美好的世界献上 bug

C++ 偽閉包

偽閉包#

閉包允許函數捕獲並攜帶其作用域中的變量,在閉包定義的作用域已經結束,這些變量仍然可以透過閉包訪問,閉包也可以推遲操作,減少代碼冗餘。

與智能指針配合延長對象的生命週期。

class A {};

int foo() {
    std::shared_ptr<A> a_ptr = std::make_shared<A>();
    auto callback = [a_ptr]() {
        // ...
    };
    async(callback); // 非同步調用 callback
}
// 離開作用域後,a_ptr 智能指針引用計數減 1
// 如果回調還在執行,就還需要 a_ptr

案例代碼:

// server.h
#include <boost/asio.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <memory>
#include <unordered_map>
#include <iostream>

namespace asio = boost::asio;

class Server;

class Session :
    public std::enable_shared_from_this<Session> {

public:
    Session(asio::io_context &ioc, Server *server);
    ~Session();
    asio::ip::tcp::socket &getSocket();
    const std::string &getUuid() const;
    void start();

private:
    void handleRead(
        const boost::system::error_code &err,
        std::size_t bytes_transferred,
        std::shared_ptr<Session> session
    );

    void handleWrite(
        const boost::system::error_code &err,
        std::shared_ptr<Session> session
    );

    enum { MAX_MESSAGE_LEN = 1024 };
    char data_[MAX_MESSAGE_LEN];
    asio::ip::tcp::socket socket_;
    std::string uuid_;
    Server *server_;
};

class Server {
    using SessionSPtr = std::shared_ptr<Session>;
public:
    Server(asio::io_context &ioc, unsigned short port);
    void removeSession(const std::string &uuid);

private:
    void startAccept();

    void handleAccept(
        SessionSPtr session,
        const boost::system::error_code &err
    );
    asio::io_context &ioc_;
    unsigned short port_;
    asio::ip::tcp::acceptor acceptor_;
    std::unordered_map<std::string, SessionSPtr> sessions_;
};
// server.cpp
Session::Session(asio::io_context &ioc, Server *server) :
    socket_(ioc),
    server_(server) {
    boost::uuids::uuid uuid = boost::uuids::random_generator()();
    uuid_ = boost::uuids::to_string(uuid);
}

Session::~Session() {
    std::cerr << "~Session()" << std::endl;
}

asio::ip::tcp::socket &Session::getSocket() {
    return socket_;
}

const std::string &Session::getUuid() const {
    return uuid_;
}

void Session::start() {
    memset(data_, 0, MAX_MESSAGE_LEN);
    socket_.async_read_some(
        asio::buffer(data_, MAX_MESSAGE_LEN),
        std::bind(
            &Session::handleRead,
            this,
            std::placeholders::_1,
            std::placeholders::_2,
            shared_from_this() // 只是為了增加引用計數,防止在讀回調時 this 對象已銷毀
        )
    );
}

void Session::handleRead(
    const boost::system::error_code &err,
    std::size_t bytes_transferred,
    std::shared_ptr<Session> self
) {
    std::cout << "[handleRead] session use_count: " << self.use_count() << std::endl;

    if (err.value() != 0) {
        // 客戶端關閉,伺服器會收到一個讀事件
        std::cerr << "[error] [handleRead] client closed" << std::endl;
        std::cerr << "[error] [handleRead] error msg: " << err.message() << std::endl;
        std::cout << "[error] [handleRead] session use_count: " << self.use_count() << std::endl;
        // 這裡並沒有真正移除 session
        server_->removeSession(uuid_);
        std::cerr << "[error] [handleWrite] session use_count: " << self.use_count() << std::endl;
        return;
    }

    std::cout << "receive data: " << data_ << std::endl;

    socket_.async_read_some(
        asio::buffer(data_, MAX_MESSAGE_LEN),
        std::bind(
            &Session::handleRead,
            this,
            std::placeholders::_1,
            std::placeholders::_2,
            shared_from_this()
        )
    );

    socket_.async_write_some(
        asio::buffer(data_, bytes_transferred),
        std::bind(
            &Session::handleWrite,
            this,
            std::placeholders::_1,
            shared_from_this()
        )
    );

    std::cout << "[handleRead] session use_count: " << self.use_count() << std::endl;
}

void Session::handleWrite(
    const boost::system::error_code &err,
    std::shared_ptr<Session> self
) {
    std::cout << "[handleWrite] session use_count: " << self.use_count() << std::endl;

    if (err.value() != 0) {
        std::cerr << "[error] [handleWrite] error msg: " << err.message() << std::endl;
        std::cout << "[error] [handleWrite] session use_count: " << self.use_count() << std::endl;
        // 這裡並沒有銷毀 session
        server_->removeSession(uuid_);
        std::cerr << "[error] [handleWrite] session use_count: " << self.use_count() << std::endl;
        return;
    }

    socket_.async_read_some(
        asio::buffer(data_, MAX_MESSAGE_LEN),
        std::bind(
            &Session::handleRead,
            this,
            std::placeholders::_1,
            std::placeholders::_2,
            shared_from_this()
        )
    );

    std::cout << "[handleWrite] session use_count: " << self.use_count() << std::endl;
}

Server::Server(
    asio::io_context &ioc,
    unsigned short port
) :
    ioc_(ioc),
    port_(port),
    acceptor_(ioc_, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port_)) {
    std::cout << "server start listen " << port_ << std::endl;
    startAccept();
}

void Server::removeSession(const std::string &uuid) {
    sessions_.erase(uuid);
}

void Server::startAccept() {
    SessionSPtr session = std::make_shared<Session>(ioc_, this);
    acceptor_.async_accept(
        session->getSocket(),
        std::bind(
            &Server::handleAccept,
            this,
            session,
            std::placeholders::_1
        )
    );
}

void Server::handleAccept(
    SessionSPtr session,
    const boost::system::error_code &err
) {
    if (err.value() != 0) {
        std::cerr << err.message() << std::endl;
        return;
    }
    std::cout << "client ip: "
        << session->getSocket().remote_endpoint().address().to_string()
        << std::endl;

    sessions_[session->getUuid()] = session;
    session->start();

    startAccept();
}

int main(int argc, char *argv[]) {
    try {
        asio::io_context ioc;
        Server server(ioc, 11451);
        ioc.run();
    } catch (std::exception &exp) {
        std::cerr << "[exception]: " << exp.what() << std::endl;
    }
    return 0;
}

延遲操作減少代碼冗餘。

class Defer {
public:
    Defer(const std::function<void()> &&func) : 
        func_(func) {
        
    }
    ~Defer() {
        func_();
    }
    
private:
    std::function<void()> func_;
};

int main() {
    int *a = new int(1);
    if (!a) {
        return;
    }
    Defer defer([&]() {
        delete a;
    });
    // ...
}
// defer 離開作用域後,調用函數來回收資源

案例代碼:

bool MysqlManager::removeUser(
    const std::string &name
) {
    std::unique_ptr<SqlConnection> connection \
        = mysql_connection_pool_->getConnection();
    if (!connection) {
        ERROR("connection is null");
        return false;
    }
    
    // 推遲 connection 的回收
    Defer defer([&]() {
        mysql_connection_pool_->recycleConnection(
            std::move(connection)
        );
    });

    try {
        std::unique_ptr<sql::PreparedStatement> stmt(
            connection->m_connection->prepareStatement(
                "DELETE FROM user WHERE name = ?"
            )
        );
        stmt->setString(1, name);
        int res = stmt->executeUpdate();
        INFO("remove user %d", res);
        return true;
    } catch (sql::SQLException &exp) {
        ERROR("SQL Exception: %s", exp.what());
        ERROR("SQL error code: %d", exp.getErrorCode());
        ERROR("SQL State: %d", exp.getSQLState().c_str());
        return -1;
    }
}
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。