How to overload std::cout << std::endl?

1.3k Views Asked by At

I was wondering if there was anyway I could overload std::cout << std::endl; for the endl to not only make a newline but also print a '-' where the newline is supposed to be and then print another newline.

Like if I did std::cout << std::endl << '-' << std::endl;

So I assume I must overload << but I'm not sure where to go from there for it to work with endl.

4

There are 4 best solutions below

6
NicholasM On

How about defining your own function (or function template) that is akin to std::endl:

std::ostream& enddash(std::ostream& os)
{
    // Can use std::endl in place of '\n' if flushing desired.
    os << "\n-\n";  
    return os;
}

Usage example:

int main(int argc, char* argv[]) {
    std::cout << "Hello, world." << enddash;
    std::cout << "Hello, world, once again" << enddash;
}

Output:

Hello, world.
-
Hello, world, once again
-
4
NicholasM On

Inspired by the epic question about indenting std::ostream instances, here is a codecvt class that will add the additional characters.

The class was adapted from the popular answer by @MartinYork: I copy-pasted the class, adapted it to use a distinct character, and rewrote the for loop into a form I found more natural.

Here's a working example.

#include <iostream>
#include <locale>

class augmented_newline_facet : public std::codecvt<char, char, std::mbstate_t>
{
    const char addition = '-';
public:
    explicit augmented_newline_facet(const char addition, size_t refs = 0) : std::codecvt<char,char,std::mbstate_t>(refs), addition{addition} {}

    using result = std::codecvt_base::result;
    using base = std::codecvt<char,char,std::mbstate_t>;
    using intern_type = base::intern_type;
    using extern_type = base::extern_type;
    using state_type = base::state_type;

    int& state(state_type& s) const {return *reinterpret_cast<int*>(&s);}
  protected:
    virtual result do_out(state_type& addition_needed,
                          const intern_type* rStart, const intern_type* rEnd, const intern_type*&   rNewStart,
                          extern_type* wStart, extern_type* wEnd, extern_type*& wNewStart) const override
    {
        result  res = std::codecvt_base::noconv;

        while ((rStart < rEnd) && (wStart < wEnd))
        {
            // The last character seen was a newline.
            // Thus we need to add the additional character and an extra newline.
            if (state(addition_needed) == 1)
            {
                *wStart++ = addition;
                *wStart++ = '\n';
                state(addition_needed) = 0;
                res = std::codecvt_base::ok;
                continue;
            }
            else
            {
                // Copy the next character.
                *wStart = *rStart;
            }

            // If the character copied was a '\n' mark that state
            if (*rStart == '\n')
            {
                state(addition_needed) = 1;
            }

            ++rStart;
            ++wStart;
        }

        if (rStart != rEnd)
        {
            res = std::codecvt_base::partial;
        }
        rNewStart   = rStart;
        wNewStart   = wStart;

        return res;
    }

    virtual bool do_always_noconv() const throw() override
    {
        return false; 
    }

};

int main(int argc, char* argv[]) {
    std::ios::sync_with_stdio(false);
    std::cout.imbue(std::locale(std::locale::classic(), 
                                new augmented_newline_facet{'-'}));

    for (int i = 0; i < 5; ++i)
    {
        std::cout << "Line " << i << std::endl;
    }
}
2
n314159 On

This is not as cool as @NicholasM's answer, but easier if the main really just contains std::cout << std::endl;:

#include <stdio.h> // We don't want any iostream stuff, so we use the C header.

namespace std
{
    struct dummy{};

    dummy cout;
    dummy endl;

    dummy& operator<<(dummy &d, const dummy& other){
        printf("\n-\n");
        return d;
    }
}

int main()
{
    std::cout << std::endl;
}

I feel a bit dirty just writing this code...

Edit: To be exceedingly clear: I do not encourage anyone to use this code in any meaningful way (As @uneven_mark remarked, it contains UB). It seems to me that the problem was posed to OP as some kind of gag or riddle, and hence I think something like this has the potential to be ok as an answer.

3
Sopel On

You can do it with a macro definition for endl.

I don't advise using it, but if you must...

#include <iostream>
#include <string>

#define endl string("\n-\n") << std::flush

int main()
{
    std::cout << std::endl;
    return 0;
}