10

I use boost::asio::buffer to send a message using

void Send(const std::string& messageData)
{
    socket.async_write(boost::asio::buffer(messageData), ...);
}

And encounter "string iterator not dereferencable" runtime error somewhere within io_service' thread. When I create objects' variable to store message data for the buffer:

void Send(const std::string& messageData)
{
    this->tempStorage = messageData;
    socket.async_write(boost::asio::buffer(this->tempStorage), ...);
}

the error is never occured. std::string (to which messageData is referenced to) is freed almost right after Send() calling - does boost::asio::buffer stores just a reference to object? If so, how can I force it to store the data by value?

Chnossos
  • 9,971
  • 4
  • 28
  • 40
Slaus
  • 2,086
  • 4
  • 26
  • 41

2 Answers2

16

Answer from boost-users mailing lists:

If it did, it would be completely useless as you do not have access to any of the buffers in your completion handler.

The way to use buffer() is to pass in references to storage that you guarantee the lifetime of in some other way.

You can either have it stored in an external object, or store it in `this' as you did, or by binding it into the completion handler function object itself.

void onComplete(shared_ptr<std::string> s, error_code const&, size_t)
{
    // do stuff
}

void send(std::string const& messageData)
{
    shared_ptr<std::string> s = make_shared<std::string>(messageData);
    async_send(socket, boost::asio::buffer(*s),
    boost::bind(&T::onSend, this, s, _1, _2));
}

This ensures that the lifetime of the buffer data is at least as long as the completion handler exists.

Slaus
  • 2,086
  • 4
  • 26
  • 41
  • 1
    `async_send` call сan be a bit shortened in C++11, please, correct me if I'm wrong: `async_send(socket, boost::asio::buffer(*s), [s](error_code const&, size_t) {});`. And now no need to create class-member function `onComplete`. – Tsar Ioann Aug 06 '15 at 13:39
  • @slav you don't need a pointer to string here, string manages allocation of data on the heap itself. – Francisco Aguilera Feb 29 '16 at 05:38
  • @FranciscoAguilera what exact **pointer** do you mean? If it is **shared_ptr**, then without it std::string will be destroyed (and all it's dynamic data allocated at heap will be freed) at the end of `void send()`. – Slaus Feb 29 '16 at 18:29
  • Is there a way to bind it to the completion handler with `unique_ptr`? There should be, right? That would have a positive impact on performance, compared to `shared_ptr` which is rather slow. – Kenji Nov 21 '16 at 21:52
  • @Kenji: Unfortunately no. You could move-capture the unique_ptr into the lambda (starting with C++14), but that would make the lambda itself non-copyable, which violates the boost::asio requirement that a handler be copy constructible. – sh- Jun 25 '17 at 16:56
1

From boost::asio's documentation:

A buffer object does not have any ownership of the memory it refers to. It is the responsibility of the application to ensure the memory region remains valid until it is no longer required for an I/O operation. When the memory is no longer available, the buffer is said to have been invalidated.

For the boost::asio::buffer overloads that accept an argument of type std::vector, the buffer objects returned are invalidated by any vector operation that also invalidates all references, pointers and iterators referring to the elements in the sequence (C++ Std, 23.2.4)

For the boost::asio::buffer overloads that accept an argument of type std::basic_string, the buffer objects returned are invalidated according to the rules defined for invalidation of references, pointers and iterators referring to elements of the sequence (C++ Std, 21.3).

Joel Bodenmann
  • 2,152
  • 2
  • 17
  • 44
billow
  • 125
  • 1
  • 1
  • 9