Domanda

I would like to know if it is possible to inherit from std::ostream, and to override flush() in such a way that some information (say, the line number) is added to the beginning of each line. I would then like to attach it to a std::ofstream (or cout) through rdbuf() so that I get something like this:

ofstream fout("file.txt");
myostream os;
os.rdbuf(fout.rdbuf());

os << "this is the first line.\n";
os << "this is the second line.\n";

would put this into file.txt

1 this is the first line.
2 this is the second line.
È stato utile?

Soluzione

flush() wouldn't be the function to override in this context, though you're on the right track. You should redefine overflow() on the underlying std::streambuf interface. For example:

class linebuf : public std::streambuf
{
public:
    linebuf() : m_sbuf() { m_sbuf.open("file.txt", std::ios_base::out); }

    int_type overflow(int_type c) override
    {
        char_type ch = traits_type::to_char_type(c);
        if (c != traits_type::eof() && new_line)
        {
            std::ostream os(&m_sbuf);
            os << line_number++ << " ";
        }

        new_line = (ch == '\n');
        return m_sbuf.sputc(ch);
    }

    int sync() override { return m_sbuf.pubsync() ? 0 : -1; }
private:
    std::filebuf m_sbuf;
    bool new_line = true;
    int line_number = 1;
};

Now you can do:

linebuf buf;
std::ostream os(&buf);

os << "this is the first line.\n";  // "1 this is the first line."
os << "this is the second line.\n"; // "2 this is the second line."

Live example

Altri suggerimenti

James Kanze's classic article on Filtering Streambufs has a very similar example which puts a timestamp at the beginning of every line. You could adapt that code.

Or, you could use the Boost tools that grew out of the ideas in that article.

#include <boost/iostreams/filtering_stream.hpp>
#include <boost/array.hpp>
#include <cstring>
#include <limits>

// line_num_filter is a model of the Boost concept OutputFilter which
// inserts a sequential line number at the beginning of every line.
class line_num_filter
    : public boost::iostreams::output_filter
{
public:
    line_num_filter();

    template<typename Sink>
    bool put(Sink& snk, char c);

    template<typename Device>
    void close(Device&);

private:
    bool m_start_of_line;
    unsigned int m_line_num;
    boost::array<char, std::numeric_limits<unsigned int>::digits10 + 4> m_buf;
    const char* m_buf_pos;
    const char* m_buf_end;
};

line_num_filter::line_num_filter() :
    m_start_of_line(true),
    m_line_num(1),
    m_buf_pos(m_buf.data()),
    m_buf_end(m_buf_pos)
{}

// put() must return true if c was written to dest, or false if not.
// After returning false, put() with the same c might be tried again later.
template<typename Sink>
bool line_num_filter::put(Sink& dest, char c)
{
    // If at the start of a line, print the line number into a buffer.
    if (m_start_of_line) {
        m_buf_pos = m_buf.data();
        m_buf_end = m_buf_pos +
            std::snprintf(m_buf.data(), m_buf.size(), "%u ", m_line_num);
        m_start_of_line = false;
    }

    // If there are buffer characters to be written, write them.
    // This can be interrupted and resumed if the sink is not accepting
    // input, which is why the buffer and pointers need to be members.
    while (m_buf_pos != m_buf_end) {
        if (!boost::iostreams::put(dest, *m_buf_pos))
            return false;
        ++m_buf_pos;
    }

    // Copy the actual character of data.
    if (!boost::iostreams::put(dest, c))
        return false;

    // If the character copied was a newline, get ready for the next line.
    if (c == '\n') {
        ++m_line_num;
        m_start_of_line = true;
    }
    return true;
}

// Reset the filter object.
template<typename Device>
void line_num_filter::close(Device&)
{
    m_start_of_line = true;
    m_line_num = 1;
    m_buf_pos = m_buf_end = m_buf.data();
}


int main() {
    using namespace boost::iostreams;
    filtering_ostream myout;
    myout.push(line_num_filter());
    myout.push(std::cout);

    myout << "this is the first line.\n";
    myout << "this is the second line.\n";
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top