4

I've recently started using Boost.Asio in a project and would like to know whether anyone knows a clean solution to transfer ownership of a newly created socket to tcp::acceptor::async_accept, which would in turn transfer this ownership to the accept handler function.

This isn't an incoherent desire, mind you, since the handler is to be called exactly once.

I have noticed I can't std::bind() an std::unique_ptr<> as parameter, since std::bind() requires its parameters to be CopyConstructible, and rightfully so. Not only that, but Boost's AcceptHandler concept is also required to be CopyConstructible.

So my options would be:

  • Go the deprecated std::auto_ptr<> way of moving objects with the copy constructor, potentially causing obscure bugs on new releases of Boost.Asio.
  • Use std::shared_ptr<> and have no way to take the shared ownership off the pointer once it's not needed anymore, i.e. when it reaches the actual handler function (this is how the job is done on the examples at http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/examples.html as far as I've read).

or

  • You have a better idea for me.

I'm pretty much at a loss here. Can anyone enlighten me?

spraff
  • 32,570
  • 22
  • 121
  • 229
Gui Prá
  • 5,559
  • 4
  • 34
  • 59
  • This question is also somewhat related http://stackoverflow.com/questions/2835900/how-to-use-boostbind-with-non-copyable-params-for-example-boostpromise – Sam Miller Jun 26 '10 at 15:59
  • You might find more people familiar with boost-asio on the boost user's mailing list: http://lists.boost.org/mailman/listinfo.cgi/boost-users – Howard Hinnant Mar 04 '11 at 15:49

1 Answers1

1

I tried to find a way to do this using the c++0x standard library, but could not. Eventually I settled on writing my own rvalue_reference_wrapper and rvalue_ref() convenience class. As per usual with std::bind, you need to wrap your non copyable object in something that is copyable (reference_wrapper is the best example). You could also have just passed a pointer, but that means changing your interface.

This worked on my machine:

#include <iostream>
#include <functional>
#include <memory>

template< class T >
struct rvalue_reference_wrapper
{
    rvalue_reference_wrapper( T&& t )
        : t_(std::move(t))
    {}

    operator T&&() const volatile
    {
        return std::move(t_);
    }

private:
    T&& t_; 
};

template< class T >
rvalue_reference_wrapper<T> rvalue_ref( T&& t )
{
    return rvalue_reference_wrapper<T>(std::move(t));
}

void go( std::unique_ptr<int> i )
{
    std::cout << *i << std::endl;
}

int main()
{
    std::unique_ptr<int> i(new int(1));

    auto b = std::bind( go, rvalue_ref(std::move(i)) );
    //auto b = std::bind( go, std::ref(std::move(i)) ); // Wont work

    b();
}

I have not made the code bulletproof, but welcome some discussion about the need for an rvalue_reference_wrapper, or how to simulate one using std::reference_wrapper.

Also, for your specific case, you will likely need to write a difference version of rvalue_reference_wrapper, which holds the object by value not by rvalue reference, since your original unique_ptr is likely going to leave scope (and be destroyed) since you are using async asio calls.

mmocny
  • 8,775
  • 7
  • 40
  • 50
  • Your solution is interesting, but I think it's unsafe. If `b` is never called, the pointer is never freed (since `i` doesn't hold ownership anymore because you've `std::move`'d it). Am I wrong? – Gui Prá Mar 04 '11 at 15:26
  • the std::move() operation itself doesnt do anything, it only casts to an rvalue reference. – mmocny Mar 04 '11 at 21:13
  • Not until the rvalue is actually accepted and used to modify the source object will anything change. In most cases that means the move constructor or move assignment operator being called, which will "destroy" the source object in a safe manner. If you don't ever use the rvalue reference, the lvalue acts as it would normally. This may lead to a dangling reference (a problem which exists with lvalue references and std::ref() as well) – mmocny Mar 04 '11 at 21:15
  • By this leading to a dangling reference, i meant this proposed solution, if b lived longer than i, for example (by being returned from the function or copied somewhere). Now that I think about it, for your use case, you would be looking at a dangling reference since the call to "b" is not actually made until after local scope is left. Again, this is not a problem with rvalue_reference_wrapper directly, but merely your use case, it would happen with std::ref() in a similar scenario. – mmocny Mar 04 '11 at 21:22