-3

I would like to do the same thing as the accepted answer here but instead of using an int I would like to use a std::string: C++ custom stream manipulator that changes next item on stream

Tom Wilhelm
  • 25
  • 1
  • 6
  • 1
    Ok, and what is your question? – Thomas Sablik Nov 21 '19 at 07:34
  • How can I do this? I don't find any other information regarding to this on the internet. I tried to change everything to int but this is also not working... I am very new into C++ – Tom Wilhelm Nov 21 '19 at 07:40
  • Please add a [mcve] and describe the problems. Show your efforts. Questions starting with "How can I ..." are usually too broad for stackoverflow. Ask a specific question for a specific problem. – Thomas Sablik Nov 21 '19 at 07:41

1 Answers1

0

The linked answer shows the "proper" way to achieve customisation of stream formatting behaviour, but it requires the formatted output operation to be extensible - this might be true for a couple of reasons:

  • because you have written an operator<< for your own type and have complete control over the behaviour, or
  • because you are printing a type which the Standard Library explicitly gives you control over via locale (e.g. printing integers with different digit separators).

Neither of these is the case for std::string. The intention is that the std::string contains exactly the characters you want to print. That doesn't make it impossible, but the solution I show below is hacky as hell and I really don't recommend it! A string should already contain the formatted content, and it shouldn't be up to the printing operation to change that.

The trick is that we want to call our own operator<<, and we can't control the type of the string, so we control the type of the stream instead.

#include <algorithm>
#include <iostream>
#include <string>

// Not really a stream, but works enough like one for our purposes
struct UpperStream {
    std::ostream& d_originalStream;
    UpperStream(std::ostream& originalStream)
      : d_originalStream(originalStream)
    {}
};

// Trick to turn a normal stream into our magic type
struct UpperHelper {} makeNextStringUpper;
UpperStream operator<<(std::ostream& os, UpperHelper)
{ return os; }

// Special printing for strings
std::ostream& operator<<(UpperStream const& us, std::string s)
{
    auto l = us.d_originalStream.getloc();
    std::transform(std::cbegin(s),
                   std::cend(s),
                   std::begin(s),
                   [=](char c){return std::toupper(c, l);});
    return us.d_originalStream << s;
}

// Default to normal printing for everything else, and then go back to normal
// stream behaviour
template <typename T>
std::ostream& operator<<(UpperStream const& us, T&& t)
{ return us.d_originalStream << t; }

int main()
{
    using namespace std::string_literals;
    std::cout << makeNextStringUpper << "Hello, World!\n"s;
    std::cout << makeNextStringUpper << 123 << " Hello, World!\n"s;
}

This prints:

HELLO, WORLD!
123 Hello, World!

To be honest, the whole business with trying to make this look like a stream manipulator is pretty odd. What's wrong with working on the string itself?

std::string toUpper(std::string s)
{
    std::locale l;
    std::transform(std::cbegin(s), std::cend(s), std::begin(s),
                   [=](char c){return std::toupper(c, l);});
    return s;
}

int main()
{
    std::cout << toUpper("Hello, World!\n"s);
}
BoBTFish
  • 19,167
  • 3
  • 49
  • 76