Search code examples
c++c++17boost-asioshared-ptr

How to use enable_shared_from_this in template functions?


I am trying to use std::enabled_shared_from_this function in a template function but I keep getting message bad_weak_ptr error in calling shared_from_this() in a class.

template<T>
class A: public std::enable_shared_from_this <A<T>>
{

    /* some parts in the class */
    void Run()
    {
        resolver_.async_resolve(host_, port_, boost::bind_front_handler(&A:on_resolve, this->shared_from_this());
    }
    /* ... */
};

I have tried std::enable_shared_from_this>::shared_from_this() and this->shared_from_this() and I did not call shared_from_this() function in a class constructor. If there are any ways to solve this error, please let me know.


Solution

  • Your query is wrong, you're passing too many arguments. Instead, pass a single query parameter:

        boost::asio::ip::tcp::resolver::query q{host_, port_};
        resolver_.async_resolve(q,
    

    Next up, you can use either bind method you prefer:

        boost::bind(&A::on_resolve, this->shared_from_this(),
            boost::asio::placeholders::error(),
            boost::asio::placeholders::results())
    

    Or not using bind at all:

        resolver_.async_resolve(q,
            [self=this->shared_from_this()](boost::system::error_code ec, boost::asio::ip::tcp::resolver::results_type eps) {
                self->on_resolve(ec, eps);
            }
        );
    

    Or using the convenience helper from Beast:

        boost::beast::bind_front_handler(&A::on_resolve, this->shared_from_this())
    

    Live Demo

    Live On Coliru

    #include <memory> // for std::shared_from_this etc
    #include <iostream>
    #include <boost/asio.hpp>
    #include <boost/bind.hpp>                    // for boost::bind
    #include <boost/beast/core/bind_handler.hpp> // for bind_front_handler
    
    template<typename T>
    class A: public std::enable_shared_from_this<A<T>>
    {
      public:
        A(boost::asio::io_context& io, std::string host, std::string port)
            : resolver_(io), host_(host), port_(port) {}
    
        void Run()
        {
            boost::asio::ip::tcp::resolver::query q{host_, port_};
            resolver_.async_resolve(q,
                boost::beast::bind_front_handler(&A::on_resolve, this->shared_from_this())
            );
            resolver_.async_resolve(q,
                boost::bind(&A::on_resolve, this->shared_from_this(),
                    boost::asio::placeholders::error(),
                    boost::asio::placeholders::results())
            );
            resolver_.async_resolve(q,
                [self=this->shared_from_this()](boost::system::error_code ec, boost::asio::ip::tcp::resolver::results_type eps) {
                    self->on_resolve(ec, eps);
                }
            );
        }
    
      private:
        void on_resolve(boost::system::error_code ec, boost::asio::ip::tcp::resolver::results_type eps) {
            std::cout << "Resolved: (" << ec.message() << "):";
            for (auto& ep : eps) {
                std::cout << " " << ep.endpoint();
            }
            std::cout << "\n";
        }
    
        boost::asio::ip::tcp::resolver resolver_;
        std::string host_, port_;
    };
    
    int main() {
        boost::asio::io_context io;
        auto instance = std::make_shared<A<int> >(io, "localhost", "1234");
        instance->Run();
    
        io.run();
    }
    

    Prints (not on Coliru due to networking restrictions):

    Resolved: (Success): 127.0.0.1:1234
    Resolved: (Success): 127.0.0.1:1234
    Resolved: (Success): 127.0.0.1:1234