1

I am using boost::asio::async_read_until to read the \n-ended line from the TCP socket. Let me please recall that async_read_until signature is the following:

http://www.boost.org/doc/libs/1_63_0/doc/html/boost_asio/reference.html#boost_asio.reference.async_read_until

void-or-deduced async_read_until(
    AsyncReadStream & s,
    boost::asio::basic_streambuf< Allocator > & b,
    char delim,
    ReadHandler handler);

Here

boost::asio::streambuf b;

is auto resized object to store received data.

As far as I understand, it internally consists of buffer sequence, a list of boost::asio buffers. However, there is no easy way to obtain ForwardIterator to iterate over this internal buffer that consists of multiple contiguous regions.

I find the following usage pattern:

std::istream stream(&b);
std::istream_iterator<char> end;
std::istream_iterator<char> begin(stream);

However, here end and begin are InputIterators.

At the same time, boost::spirit::phrase_parse(begin, end, grammar, space, obj) parser that could be used to parse obtained from socked line requires begin and end be ForwardIterators:

http://www.boost.org/doc/libs/1_63_0/libs/spirit/doc/html/spirit/support/multi_pass.html

This is required for backtracking. However, the data are actually already stored in the memory buffer in boost::asio::streambuf b object, nothing prevents iterator from being dereferenced more than once.

0x2207
  • 878
  • 1
  • 6
  • 20
  • I could write my own iterator (iterating over BufferSequenced elements one-by-one) and use b.data() to iterate over it. But, I think there should be standard way... – 0x2207 Jan 26 '17 at 19:34
  • @Nim you can simply multi-pass adapt a streambuf_iterator (in exactly the same way boost::spirit::istream_iterator adapts an istream_iterator). But even that is unnecessary. Boost Asio's buffer iterators meet the requirements, see my answer. – sehe Jan 26 '17 at 23:01

1 Answers1

3

Boost has buffers_begin() and buffers_end() which you can use on streambuf's data():

Live On Coliru

#include <boost/asio.hpp>
#include <boost/spirit/include/qi.hpp>

namespace a = boost::asio;
namespace qi = boost::spirit::qi;

#include <iostream>
int main() 
{
    a::streambuf input;
    std::ostream(&input) << "123, 4, 5; 78, 8, 9;\n888, 8, 8;";

    auto f = a::buffers_begin(input.data()), l = a::buffers_end(input.data());
    //std::copy(f, l, std::ostream_iterator<char>(std::cout));

    std::vector<std::vector<int> > parsed;
    bool ok = qi::phrase_parse(f, l, *(qi::int_ % ',' >> ';'), qi::space, parsed);

    if (ok) {
        std::cout << "parsed: \n";
        for (auto& row : parsed) {
            for (auto& v : row) { std::cout << v << " "; }
            std::cout << ";\n";
        }
    }
    else
        std::cout << "parse failed\n";

    if (f!=l)
        std::cout << "remaining unparsed input: '" << std::string(f,l) << "'\n";
}

Prints (as expected):

parsed: 
123 4 5 ;
78 8 9 ;
888 8 8 ;

Note don't forget to consume() the parsed portion of the input!

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Oh cool - I didn't realise the `buffers_begin()` et-al returned a suitable iterator for this! I'll remove my comment.. – Nim Jan 27 '17 at 09:32
  • Thank you. The answer makes sense. I was sure that buffers_begin/end() iterated over buffers of the sequence. – 0x2207 Jan 27 '17 at 16:37