Question

I am trying to get working a program using Boost::asio. Here is a method that I use to do an async_write():

template<typename T>
void Write(shared_ptr<std::vector<T>> data){
    std::cout << "Write Method [std::vector]" << std::endl;

    ioService.post(boost::bind(&TCPClient::DoWrite<T>, this, data));
}

Then, DoWrite() method is called to actually send the data in the vector:

template<typename T>
void DoWrite(shared_ptr<std::vector<T>> data){
std::cout << "DoWrite Method [std::vector]" << std::endl;

boost::asio::async_write(   socket_,
                            boost::asio::buffer(*data),
                            boost::bind(&TCPClient::handle_write,
                            this, boost::asio::placeholders::error,
                            boost::asio::placeholders::bytes_transferred)
                        );
std::cout << "async_write executed" << std::endl;

}

But on runtime I am getting this error:

Program: C:\Windows\SYSTEM32\MSVCP120D.dll File: C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\vector Line: 72

Expression: vector iterator not dereferencable

After investigating I know the problem is that the std::vector<T> passed to the method doesn't stay alive enough and when the write happens (because it is async) the vector doesn't exists so I get this error.

I know the problem is that because if I remove the template and my function is now something like:

void DoWrite(std::vector<char> data){
    std::cout << "DoWrite Method [std::vector]" << std::endl;

    backupVector = data;

    boost::asio::async_write(   socket_,
                                boost::asio::buffer(backupVector),
                                boost::bind(&TCPClient::handle_write,
                                this, boost::asio::placeholders::error,
                                boost::asio::placeholders::bytes_transferred)
                            );
    std::cout << "async_write executed" << std::endl;
}

Where backupVector is std::vector<char> inside the class I am not getting the error because I have a reference that exists, but I can't create at runtime a std::vector<T> to store the incoming vector in the class (Am I right? I am new to C++). So I read about shared_ptr:

std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer. Several shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of the following happens:

  • The last remaining shared_ptr owning the object is destroyed.

  • The last remaining shared_ptr owning the object is assigned another pointer via operator= or reset().

So If I am passing the pointer to async_write why it is being "destroyed" if an object has a reference to it? Is any other way to do this?

This is how I use Write() method:

std::vector<char> data;
data.push_back('B');
data.push_back(bValue);

client.Write<char>(make_shared<std::vector<char>>(data));

Sleep(100000);
Was it helpful?

Solution

I'm far from being able to tell what you should do here (missing information and only used asio a couple of times), but guessing from this example, you should be able to pass your buffer using a custom buffer class similar to:

// based on http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/example/cpp11/buffers/reference_counted.cpp
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

template<class T>
class shared_const_buffer
{
public:
  // Construct from a `shared_ptr`
  explicit shared_const_buffer(std::shared_ptr<std::vector<T>> const& p_data)
    : data_(p_data),
      buffer_(boost::asio::buffer(*data_))
  {}

  // Implement the ConstBufferSequence requirements.
  using value_type = boost::asio::const_buffer;
  using const_iterator = boost::asio::const_buffer const*;

  boost::asio::const_buffer const* begin() const { return &buffer_; }
  boost::asio::const_buffer const* end() const { return &buffer_ + 1; }

private:
  std::shared_ptr<std::vector<T>> data_;
  boost::asio::const_buffer buffer_;
};

As mentioned in my comments to the question, I do not know if passing around shared buffers for sending (writing) is a good idea.


How this works:

In the write function, you pass in a buffer:

boost::asio::async_write(   socket_,
                            boost::asio::buffer(backupVector), /*...*/ );

In this example, the buffer is created via the boost::asio::buffer function. But this function doesn't acquire (and doesn't share) ownership of the argument, it solely stores a reference / pointer. The passed buffer object is copied into the socket (in the job queue), where it lives until the job is done.

By using shared_const_buffer instead of the buffer object created by boost::asio::buffer, we extend the lifetime of the vector to at least the lifetime of the created shared_const_buffer object. Therefore, the vector lives at least until the job is done.

See the documentation of async_write and the documentation of the buffer function / buffer invalidation.

OTHER TIPS

The reference to the shared_ptr lives only as long as it takes to execute Clint.write(...).

If you declare it outside this parameter it will be alive longer and your program will probably work.

You just have to make sure it stays around long enough to be written asynchronously by boost::asio called functions.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top