0

I'm working on boost::asio::streambuf and found out that I can send/get a struct using it, but when I send a struct I just can't get it as I have sent it. The documentation says that one should use commit() and consume(), but where should I use them here?

struct person
{
    int m_id;
    std::string m_message;
};

std::istream& operator>>(std::istream& in, struct person& p)
{
    return in >> p.m_id >> p.m_message;
}

std::ostream& operator<<(std::ostream& out, struct person& p)
{
    return out << p.m_id << " " << p.m_message;
}

int main()
{
    boost::asio::streambuf buf;
    std::ostream out(&buf);

    person p;

    p.m_id = 1;
    p.m_message = "Hello World!";

    out << p;

    std::istream in(&buf);

    person p1;

    in >> p1;

    cout << "ID: " << p1.m_id << endl;
    cout << "Message: " << p1.m_message << endl;

return 0;
}

The problem is with strings so when I type only "hello" (without world), it works fine, but if I add "world!" as shown above it just doesn't see the added "world!", why?

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
Abdurasul1996
  • 51
  • 1
  • 8

2 Answers2

2

There are a number of issues.

  1. Firstly, make the arguments const& when possible:

    std::ostream &operator<<(std::ostream &out, person const &p) {
    
  2. Secondly, make sure the streams flush to the buffer. I think it's good practice to limit the lifetime of the ostream or istream instances

  3. Thirdly, choose a format that will be robust. Your sample already had bigger problems, when you had m_id = 1 and m_message = "123" (can you see it?).

    In text formats you need either fixed-length fields or a delimiting protocol. Let's fix it:

    std::ostream &operator<<(std::ostream &out, person const &p) {
        return out << p.m_id << ";" << p.m_message.length() << ";" << p.m_message;
    }
    

    Now when reading it back you will see how much more precise you need to be:

    std::istream &operator>>(std::istream &in, person &p) {
        char separator;
        size_t length;
    
        bool ok = in >> p.m_id 
               && in >> separator && separator == ';' 
               && in >> length
               && in >> separator && separator == ';' 
               ;
    
        if (ok) {
            p.m_message.resize(length);
            in.read(&p.m_message[0], length);
    
            p.m_message.resize(in.gcount());
        }
    
        // ensure the expected number of bytes were read
        ok = ok && (p.m_message.length() == length);
    
        if (!ok)
            in.setstate(std::ios::failbit);
    
        return in;
    }
    

    Yikes. Really? Yes really. At a minimum!

  4. Do error handling

Full Demo

Live On Coliru

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

struct person {
    int m_id;
    std::string m_message;
};

std::ostream &operator<<(std::ostream &out, person const &p) {
    return out << p.m_id << ";" << p.m_message.length() << ";" << p.m_message;
}

std::istream &operator>>(std::istream &in, person &p) {
    char separator;
    size_t length;

    bool ok = in >> p.m_id 
           && in >> separator && separator == ';' 
           && in >> length
           && in >> separator && separator == ';' 
           ;

    if (ok) {
        p.m_message.resize(length);
        in.read(&p.m_message[0], length);

        p.m_message.resize(in.gcount());
    }

    // ensure the expected number of bytes were read
    ok = ok && (p.m_message.length() == length);

    if (!ok)
        in.setstate(std::ios::failbit);

    return in;
}

int main() {
    boost::asio::streambuf buf;

    std::ostream(&buf) << person{ 1, "Hello World!" }; 

    person received;
    if (std::istream(&buf) >> received) {
        std::cout << "ID:      " << received.m_id << std::endl;
        std::cout << "Message: " << received.m_message << std::endl;
    } else {
        std::cout << "Couldn't receive person\n";
    }
}

Prints

ID:      1
Message: Hello World!

BONUS

C++14 added std::quoted:

#include <iomanip>
std::ostream &operator<<(std::ostream &out, person const &p) { return out << p.m_id << std::quoted(p.m_message); }
std::istream &operator>>(std::istream &in,  person       &p) { return in  >> p.m_id >> std::quoted(p.m_message); }

Which, in this case, also does the job: Live On Coliru

sehe
  • 374,641
  • 47
  • 450
  • 633
  • As a bonus added [a C++14 version based on `std::quoted`](http://coliru.stacked-crooked.com/a/94d9d8d499166b2b) (much underrated feature) – sehe Nov 21 '17 at 00:30
  • That's a more exhaustive answer indeed, +1 – Drax Nov 21 '17 at 09:41
  • when the struct of person include `char`、`short`、`int`、`std::string`,the `operator>>` will return fail? – Liu Guangxuan Mar 27 '20 at 06:16
  • @LiuGuangxuan I'm not sure what you mean. Does this help? http://coliru.stacked-crooked.com/a/87feed568c394db4 – sehe Mar 27 '20 at 11:25
  • @sehe yes,That's really what I mean.I want to know why the type such of `char`、`short` has fixed size,also need separator? – Liu Guangxuan Mar 29 '20 at 04:23
0

From http://en.cppreference.com/w/cpp/string/basic_string/operator_ltltgtgt (emphasis and ellipsis mine):

2) ... reads characters from is and appends them to str ... until one of the following conditions becomes true:

  • ...
  • ...
  • std::isspace(c,is.getloc()) is true for the next character c in is (this whitespace character remains in the input stream). ...

Basicaly what this means is that if you extract a string from an istream using operator >> it stops at white spaces.

If you want to get everything from the stream into your string there are plenty of questions asking that (Like this one or this one).

Drax
  • 12,682
  • 7
  • 45
  • 85