Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add a custom prefix in a << operator for a custom object

Tags:

c++

Is there a way to add a custom prefix in the operator<< for an object that I implement?

Ex:

class A {
   public:
    std::string id;
    int count;
};

std::ostream &operator<<(std::ostream &os, const A &a)
{
    os << os.prefix() << "Id: " << a.id << "\n";
    os << os.prefix() << "Count: " << a.count << "\n";
    return os;
}

If I do something like this:

A a;
a.id = "foo";
a.count = 1;
std::cout << a << std::endl;

The output will be:

Id: foo
Count: 1

I want to do something like:

std::cout << set_prefix(" -") << a << std::endl;
std::cout << set_prefix("==>") << a << std::endl;

To get an output like this:

 -Id: foo
 -Count: 1
==>Id: foo
==>Count: 1

A suggestion is to use std::setfill and os.fill, but std::setfill takes a single char as an argument and I need a custom string instead.

Solution

Looking at operator<<(std::basic_ostream) documentation, I found this:

Before insertion, first, all characters are widened using os.widen(), then padding is determined as follows: if the number of characters to insert is less than os.width(), then enough copies of os.fill() are added to the character sequence to make its length equal os.width(). If (out.flags()&std::ios_base::adjustfield) == std::ios_base::left, the fill characters are added at the end of the output sequence, otherwise they are added before the output sequence. After insertion, width(0) is called to cancel the effects of std::setw, if any.

So the solution that works for me was save the original width of stream at the beggining and than recovering them when necessary.

std::ostream &operator<<(std::ostream &os, const A &a)
{
    auto w = os.width();
    os << std::setw(w) << "" << "Id: " << a.id << "\n";
    os << std::setw(w) << "" << "Count: " << a.count;
    return os;
}

Then:

std::cout << a << std::endl;
std::cout << std::setw(4) << a << std::endl;
std::cout << std::setfill('>') << std::setw(2) << a << std::endl;

Gave the following output:

Id: foo
Count: 1
    Id: foo
    Count: 1
>>Id: foo
>>Count: 1
like image 873
Filipe Utzig Avatar asked Mar 05 '23 08:03

Filipe Utzig


1 Answers

Maybe a bit of overkill, but you can use something like this:

#include <iostream>
#include <sstream>

struct line_buffered_stream {
    std::ostream& out;
    std::stringstream ss;
    std::string prefix;
    line_buffered_stream(std::ostream& out,std::string prefix) : 
        out(out),prefix(prefix) {}        
    template <typename T> 
    auto operator<<(const T& t) -> decltype(this->ss << t,*this) { 
        ss << t; 
        return *this;
    }        
    ~line_buffered_stream(){
        std::string line;
        while (std::getline(ss,line)){
            out << prefix << line << "\n";
        }
    }
};

int main() {
     line_buffered_stream(std::cout,"==>") << "a\nb\n";
     line_buffered_stream(std::cout,"-->") << "a\nb\n";        
}

output:

==>a
==>b
-->a
-->b

Live Demo

Note that the implementation above is not meant to be used as anything else than a temporary whose lifetime is restricted to a single line of code. If you dont like that you'd have to add some mechanism to flush the stream to std::cout not to wait till the destructor is called.

like image 196
463035818_is_not_a_number Avatar answered Apr 28 '23 01:04

463035818_is_not_a_number