7
\$\begingroup\$

I was de-serializing objects that had an extra newline that needed to be removed before getting the next object.

#ifndef THORSANVIL_UTIL_H
#define THORSANVIL_UTIL_H

// Removed headers for   getDist() and getRandomContainerIterator()
// We already reviewed those two. 
#include <iostream>

namespace ThorsAnvil
{
    namespace Util
    {

// Removed getDist() and getRandomContainerIterator()
// We already reviewed those two.

class IgnoreUntilNewLine
{
    public:
        friend std::istream& operator>>(std::istream& stream, IgnoreUntilNewLine& /*data*/)
        {
            stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            return stream;
        }
};

class SkipEmptyLineToEnd
{
    public:
        friend std::istream& operator>>(std::istream& stream, SkipEmptyLineToEnd& /*data*/)
        {
            std::string     emptyLine;
            std::getline(stream, emptyLine);
            if (!emptyLine.empty()) {
                stream.setstate(std::ios::badbit);
            }
            return stream;
        }
};

    }
}

#endif

Simplified Example for usage:

namespace TU = ThorsAnvil::Util;
struct Example1
{
     int    x;
     int    y;
     std::string  line; // Always one line with no "\n"
     void swap(Example1& other) noexcept {/*Stuff*/}

     friend std::ostream& operator<<(std::ostream& stream, Example1 const& data)
     {
         // Note putting a new line here
         // to make the Serialized object more readable.
         // The actual object was a bit more complicated and
         // I really wanted to make it readable.
         return stream << data.x << " " << data.y << "\n"
                       << data.line << "\n";
     }
     friend std::istream& operator>>(std::istream& stream, Example1& data)
     {
         Example1  tmp;
         if (stream >> tmp.x >> tmp.y)
         {
             stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
             if (std::getline(stream, tmp.line)) {
                 data.swap(tmp);
             }
         }
         return stream;
     }
};

I thought the above read was a bit cumbersome and hard to read. Thus the two types above. This means I can re-write the read operation as:

     friend std::istream& operator>>(std::istream& stream, Example1& data)
     {
         TU::IgnoreUntilNewLine ignoreNewLine;
         Example1               tmp;

         if ((stream >> tmp.x >> tmp.y >> ignoreNewLine) && std::getline(stream, tmp.line)) {
             data.swap(tmp);
         }
         return stream;
     }
\$\endgroup\$
4
\$\begingroup\$

Classes IgnoreUntilNewLine and SkipEmptyLineToEnd can probably be simplified. Classic indicators for that are that it only has one function and no state. However, the key is the following operator>> overload

basic_istream& operator>>( basic_istream& (*func)(basic_istream&) );

In other words, you read into a function. This is used by e.g. std::endl or std::flush as well. Using that, you only need a single function for each of the above two classes. The body of those functions is the same as the operator>> for the two classes.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I like this idea. I will try an experiment with this. \$\endgroup\$ – Martin York Feb 8 at 23:09
4
\$\begingroup\$

IgnoreUntilNewLine is too long in my opinion. I'd simplify it to ignore_line.

It probably makes sense to provide the manipulator object in the library instead of letting the user declare one:

struct ignore_line_t {
    constexpr explicit ignore_until_newline_t() = default;
    // ...
};
inline constexpr ignore_line_t ignore_line{};

so the user can use it directly in a >> chain:

stream >> tmp.x >> tmp.y >> TU::ignore_line

Incidentally, we can go further and provide a similar helper for getline to fit in the chain:

class getline {
    std::string& dest;
public:
    explicit getline(std::string& s)
        : dest{s}
    {
    }
    friend std::istream& operator>>(std::istream& is, getline man)
    {
        return std::getline(is, man.dest);
    }
};

so your example can be written simply as

stream >> tmp.x >> tmp.y >> TU::ignore_line >> TU::getline(tmp.line)
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.