ikh
ikh

Reputation: 10417

Boost.Asio - Multiple buffers with custom buffer

I'm writing a kind of tcp server. I'm using Boost.Asio, with shared_const_buffer.

Now I have to write multiple buffers. So I write like this:

std::vector<shared_const_buffer> bufs;
bufs.push_back(buf1);
bufs.push_back(buf2);
...
boost::asio::async_write(*pSock, bufs, [] { ... });

However, I can see an error.

d:\boost_1_56_0\boost\asio\detail\consuming_buffers.hpp(175): error C2679: 이항 '=' : 오른쪽 피연산자로 'shared_const_buffer' 형식을 사용하는 연산자가 없거나 허용되는 변환이 없습니다.

Well, my Visual Studio is not English version >o< Here's my translation

d:\boost_1_56_0\boost\asio\detail\consuming_buffers.hpp(175): error C2679: binary operator '=' : there's no operator or permissible conversion which use shared_const_buffer type as right-hand-side.

I'm not sure that my translation is correct, but I think you can get a point. Anyway, I saw boost\asio\detail\consuming_buffers.hpp(175) to figure out the problem.

if (!at_end_)
{
  first_ = *buffers_.begin(); // <-- here's error point
  ++begin_remainder_;
}

So I tried adding casting operator into shared_const_buffer, and it works magically.

Here's my mcve

#include <iostream>
#include <vector>
#include <memory>

#include <boost/asio.hpp>

using boost::asio::ip::tcp;

class shared_const_buffer
{
private:
    std::shared_ptr<std::vector<char> > m_pData;
    boost::asio::const_buffer m_buffer;

public:
    explicit shared_const_buffer(const std::vector<char> &data)
        : m_pData { std::make_shared<std::vector<char> >(data) }
        , m_buffer { boost::asio::buffer(*m_pData) }
    {

    }

    typedef boost::asio::const_buffer value_type;
    typedef const boost::asio::const_buffer *const_iterator;
    const boost::asio::const_buffer *begin() const { return &m_buffer; }
    const boost::asio::const_buffer *end() const { return &m_buffer + 1; }

    // it'll work if you uncomment this line.
    //operator const boost::asio::const_buffer &() const { return *begin(); }
};

void run_accept(boost::asio::io_service &iosrv, tcp::acceptor &acpt);

int main()
{
    boost::asio::io_service iosrv;

    tcp::acceptor acpt(iosrv, tcp::endpoint(tcp::v4(), 12345));
    run_accept(iosrv, acpt);

    iosrv.run();
}

void run_accept(boost::asio::io_service &iosrv, tcp::acceptor &acpt)
{
    auto pSock = std::make_shared<tcp::socket>(iosrv);
    acpt.async_accept(*pSock,
        [&, pSock](const boost::system::error_code &ec) {
            run_accept(iosrv, acpt);

            std::vector<shared_const_buffer> bufs;
            bufs.push_back(shared_const_buffer({ 'a', 'b', 'c', 'd' }));
            bufs.push_back(shared_const_buffer({ 'e', 'f', 'g', 'h' }));

            boost::asio::async_write(*pSock, bufs,
                [pSock](const boost::system::error_code &ec, size_t size) {
                    pSock->close();
                });
        });
}

However, I can't know why it doesn't work. According to documentation,

Buffers and Scatter-Gather I/O

To read or write using multiple buffers (i.e. scatter-gather I/O), multiple buffer objects may be assigned into a container that supports the MutableBufferSequence (for read) or ConstBufferSequence (for write) concepts:

char d1[128];
std::vector<char> d2(128);
boost::array<char, 128> d3;

boost::array<mutable_buffer, 3> bufs1 = {
  boost::asio::buffer(d1),
  boost::asio::buffer(d2),
  boost::asio::buffer(d3) };
bytes_transferred = sock.receive(bufs1);

std::vector<const_buffer> bufs2;
bufs2.push_back(boost::asio::buffer(d1));
bufs2.push_back(boost::asio::buffer(d2));
bufs2.push_back(boost::asio::buffer(d3));
bytes_transferred = sock.send(bufs2);

Also documentation says it's enough with these 5 line to satisfy "ConstBufferSequence".

// Implement the ConstBufferSequence requirements.
typedef boost::asio::const_buffer value_type;
typedef const boost::asio::const_buffer* const_iterator;
const boost::asio::const_buffer* begin() const { return &buffer_; }
const boost::asio::const_buffer* end() const { return &buffer_ + 1; }

What did I miss? I'm using Visual Studio 2013 Update 3 and boost 1.56.0.

Upvotes: 1

Views: 3782

Answers (2)

Tanner Sansbury
Tanner Sansbury

Reputation: 51871

The subtle detail is that std::vector<shared_const_buffer> is being passed to the write() operation, and thus it must support the ConstBufferSequence concept. While shared_const_buffer meets the type requirements for ConstBufferSequence, std::vector<shared_const_buffer> fails to meet the type requirements for ConstBufferSequence. One of the requirements for ConstBufferSequence is that for type of X, X::value_type is a type that meets the type requirements of ConvertibleToConstBuffer. Basically, an instance of const_buffer must be constructible from X::value_type and assignable from X::value_type:

X::value_type a;
boost::asio::const_buffer u(a);
u = a;

In the case of std::vector<shared_const_buffer>, the value_type is shared_const_buffer, and shared_const_buffer can neither be used to construct const_buffer nor assigned to const_buffer. Hence, the resulting compilation error.

Upvotes: 6

sehe
sehe

Reputation: 392833

async_write takes a ConstBufferSequence. According to the docs, the first requirement that this concept needs to satisfy is:

enter image description here

In your sample, std::vector<shared_const_buffer>::value_type evaluates to shared_const_buffer, which means it needs to model ConvertibleToConstBuffer. By adding the conversion, you satisfy exactly that.

Upvotes: 4

Related Questions