einpoklum
einpoklum

Reputation: 131920

Temporarily override output stream behavior

Suppose I have a vector of chars (or just an iterator pair) which I want to print to an ostream. Now, I don't just want it printed - I want it to have specific kinds of spacing, I want a different representation of char values than just blurting them out to the terminal (say, both the char if it's printable and the escaped 2-digit or hex code.

Now I don't want to write my own custom code beyond the bare minimum - i.e. just a bit of glue and

void foo(ostream& os, char c) {
    os << c << ' ' << as_hex(c);
}

(which is like a custom overload for operator<< without the return.)

I do not want to have to loop over the vecotr, and for each character to manually call the abve I want it to behave and look as though I'm just pipe my vector to my outoput stream, after having temporarily changed the latter's attributes. But occasionally I want the stream to go back to behaving normally, even for the same vector.

Can this be done reasonably/idiomatically?

Upvotes: 2

Views: 268

Answers (3)

Ami Tavory
Ami Tavory

Reputation: 76336

For the overloading to be temporary, I thought it would be nice to do some stuff like in iomanip. This will allow to do stuff like

int main() 
{ 
    std::vector<char> v{'a', 'b'}; 
    /* v should be printed using foo_vector_print, perhaps
    *    next invocation should use a different format. */
    std::cout << foo_vector_print() << v << std::endl; 
}   

The idea is to have something of the form:

 stream << manip << vector;

where

  • stream << manip will return an object of a class storing a reference to the stream.
  • ... << vector will print using a specific format, then return a reference to the original stream.

The following program shows how to do this:

#include <iostream>
#include <vector>


namespace detail
{

// A dummy class in detail namespace, simply records the original ostream object.
struct foo_vector_printer_imp
{
    std::ostream *m_os;
};

};


// This particular implementation simply prints out the vectors size.
std::ostream &operator<<(detail::foo_vector_printer_imp imp, const std::vector<char> &v)
{
    *imp.m_os << v.size();

    return *imp.m_os;
}


struct foo_vector_print{};


/* The result of piping a foo_vector_print into an ostream, 
*    is a foo_vector_printer_imp object recording the ostream */
detail::foo_vector_printer_imp operator<<(std::ostream &os, const foo_vector_print &)
{
    detail::foo_vector_printer_imp imp;
    imp.m_os = &os;
    return imp;
}


int main() 
{ 
    std::vector<char> v{'a', 'b'}; 
    std::cout << foo_vector_print() << v << std::endl; 
}   

Upvotes: 1

Revolver_Ocelot
Revolver_Ocelot

Reputation: 8785

I assume that you know about operator overloading and your main problem is to how tell operator which formatting option to use, i.e. how to store arbitrary information in stream.

Anything derived from std::ios_base (all standard stream classes) has .iword() method which returns reference to long from some internal storage and .pword(), which returns reference to void*. Unique index for these calls can be aquired through call to std::ios_base::xalloc()

Example of using stream storage to change behaviour of output operation:

#include <iostream>
#include <vector>

const int vprint_index = std::ios_base::xalloc();

std::ostream& operator<<(std::ostream& out, std::vector<char> vec)
{
    if (out.iword(vprint_index) == 0) {
        for(char c: vec)
            out << c;
        return out;
    }
    out.iword(vprint_index) = 0;
    out << '{';
    bool first = true;
    for(char c: vec) {
        if(!first)
            out << ", ";
        first = false;
        out << c;
    }
    return out << '}';
}

int main()
{
    std::vector<char> vector {'f', 'o', 'o'};
    std::cout << vector << '\n';
    std::cout.iword(vprint_index) = 1;
    std::cout << vector << '\n';
    std::cout << vector << '\n';
}

foo
{f, o, o}
foo

Upvotes: 2

Sam Varshavchik
Sam Varshavchik

Reputation: 118425

Well, you do have to write your custom code somewhere. The contents of your vector aren't going to write to a std::ostream all by themselves.

You're really asking, from the looks of it, how to be able to use the << operator to operate on a std::vector<char>. The following example should get you started:

#include <iostream>
#include <vector>

std::ostream &operator<<(std::ostream &o, const std::vector<char> &v)
{
    // Fill in the blanks here.
    return o;
}

// Example:

int main()
{
    std::vector<char> v;

    std::cout << v;

    return 0;
}

Upvotes: 0

Related Questions