1
votes

I need to use the data read from an asio::socket (with ASIO_STANDALONE) as an istream

Up to now I have this code

asio::error_code ec;
auto bytes_transferred = asio::read_until(socket, buffer, '\n', ec);

if(!ec)
{
    buffer.commit(bytes_transferred);
    std::istream line(&buffer);
    // use line
    buffer.consume(bytes_transferred);
}

the problem I'm facing is that the '\n' appears at the end of the istream, since it is not discarded by the read_until.

The declarations are

tcp::socket socket;
asio::streambuf buffer;

So I need to ignore the '\n' char when constructing the stream. I tried (as I saw in this question efficient copy of data from boost::asio::streambuf to std::string ) with

if(!ec)
{
    // Ignore the last char ('\n')
    std::string line = std::string(asio::buffers_begin(buffer),
                                   asio::buffers_begin(buffer) + bytes_transferred - 1);
    std::stringstream ss(line);
    // use line
    buffer.consume(bytes_transferred);
}

But this produces the following compile error

In instantiation of ‘struct asio::detail::buffers_iterator_types<asio::basic_streambuf<>, char>’:
lib/asio/asio/buffers_iterator.hpp:77:46:   required from ‘class asio::buffers_iterator<asio::basic_streambuf<>, char>’
file.h:58:78:   required from here
lib/asio/asio/buffers_iterator.hpp:60:5: error: no type named ‘value_type’ in ‘class asio::basic_streambuf<>’
     {
     ^
lib/asio/asio/buffers_iterator.hpp: In instantiation of ‘class asio::buffers_iterator<asio::basic_streambuf<>, char>’:
file.h:58:78:   required from here
lib/asio/asio/buffers_iterator.hpp:455:43: error: no type named ‘const_iterator’ in ‘class asio::basic_streambuf<>’
   typename BufferSequence::const_iterator begin_;

and a few others always complaining about missing types. I'm fairly new to C++ and this message is kind of cryptic to me, moreover I'm not sure this is the best approach, as I will have 3 copy of the same data (in the buffer, in the string and in the stream)

What could be a possible solution to my problem?

2

2 Answers

3
votes

There are a number of approaches here, but it's difficult to give good advice without knowing something about how you intend to use the istream.

One thing about this code is that you know with absolute certainty that there will be a newline at the end of the istream. There is no code path that can be taken that will not result in that (other than an I/O error, which you will catch early).

As a result it's straightforward to write code that expects the \n to be there and ignore it.

Obviously you want to avoid buffer copies if possible.

However, if you want to copy the entire streambuf into a string, you might try this:

auto buf = buffer.data(); /* where buffer is your asio::streambuf */
auto s = std::string(boost::asio::buffer_cast<const char*>(buf), 
                     boost::asio::buffer_size(buf) - 1); /* remove the newline */

Of course if you're looking to parse strings, numbers etc out of the istream then the fact that there is a newline on the end won't matter - it's just whitespace which is ignored by default.

This mini program demonstrates this:

#include <boost/asio.hpp>
#include <sstream>
#include <iomanip>
#include <iostream>


int main()
{
    std::ostringstream oss;

    oss << 10 << std::quoted("Hello\" World") << '\n';
    auto payload = oss.str();

    boost::asio::streambuf sb;
    std::ostream otmp(&sb);
    otmp.write(payload.data(), payload.size());

    // streambuf now contains string with newline

    std::string s;
    int i;
    std::istream itmp(&sb);
    itmp >> i >> std::quoted(s);

    std::cout << i << std::endl;   // expect 10
    std::cout << s << std::endl;   // expect Hello" World
}
2
votes

The buffer iterators assume that you're not using a streambuf. If you can receive to a buffer (sequence) that would totally work.

However, you can perhaps just use

  1. streambuf iterators:

    std::string line;
    // Ignore the last char ('\n')
    std::copy_n(std::istreambuf_iterator<char>(&buffer), bytes_transferred - 1, back_inserter(line));
    

    Live On Coliru

  2. iostream whitespace aware functionality (like std::getline and std::skipws, which is enabled by default).