How to write a function wrapper for cout that allows for expressive syntax?
Asked Answered
I

4

5

I'd like to wrap std::cout for formatting, like so:

mycout([what type?] x, [optional args]) {
    ... // do some formatting on x first
    std::cout << x;
}

and still be able to use expressive syntax like

mycout("test" << i << endl << somevar, indent)

instead of being forced to be more verbose like

mycout(std::stringstream("test") << i ...)

How can I implement this? What type to make x?

Edit: added consideration for optional arguments

Inexpungible answered 8/5, 2013 at 15:6 Comment(0)
H
6

How about this:

struct MyCout {};

extern MyCout myCout;

template <typename T>
MyCout& operator<< (MyCout &s, const T &x) {
  //format x as you please
  std::cout << x;
  return s;
}

And put MyCout myCout; into any one .cpp file.

You can then use myCout like this:

myCout << "test" << x << std::endl;

And it will call the template operator<< which can do the formatting.

Of course, you can also provide overloads of the operator for special formatting of specific types if you want to.

EDIT

Apparently (thanks to @soon), for standard manipulators to work, a few more overloads are necessary:

MyCout& operator<< (MyCout &s, std::ostream& (*f)(std::ostream &)) {
  f(std::cout);
  return s;
}

MyCout& operator<< (MyCout &s, std::ostream& (*f)(std::ios &)) {
  f(std::cout);
  return s;
}

MyCout& operator<< (MyCout &s, std::ostream& (*f)(std::ios_base &)) {
  f(std::cout);
  return s;
}

EDIT 2

I may have misunderstoor your original requirements slightly. How about this (plus the same manipulator overloads as above):

struct MyCout
{
  std::stringstream s;

  template <typename T>
  MyCout& operator << (const T &x) {
    s << x;
    return *this;
  }

  ~MyCout() {
    somehow_format(s);
    std::cout << s.str();
  }
};

int main() {
  double y = 1.5;
  MyCout() << "test" << y;
}
Healthful answered 8/5, 2013 at 15:15 Comment(12)
"myCout << ... << std::endl;" will not works(in your case), since operator<< isn't overloaded for endl function.Pleasance
Thanks - how could optional arguments additionally be implemented? Overloading comma operator? e.g. myCout << "array" << myarr, indent, seperatorInexpungible
@MiloChen That should work, with custom types and singleton instances for indent and separator.Healthful
In your edits, should you not be returning s rather than myCout?Inexpungible
Unfortunately, I don't this will work if you want to format the entire line of input at once (e.g. "test" << ... << x) right? For example, part of the purpose of my formatter is to always put an endl at the end of each line. Would this not add an endl after each of "test", ..., x?Inexpungible
I must have misunderstood your original requirements; added another approach to answer. It's a nice problem :-)Healthful
Thanks. What is the significance of struct MyCout? Why not just have classes?Inexpungible
@MiloChen struct and class are almost the same in C++. struct uses public access by default and class uses private, but they're otherwise identical.Healthful
Oh OK, didnt realise that compiler would be so pedantic. I think also ~MyCout should print s.str()Inexpungible
@MiloChen Added a str() call.Healthful
Changed this to accepted answer, as this provides such a complete and elegant solution to my problem. Making this into a class with a constructor, you can start passing optional parameters like myCout(indent, ...) << "hello" << x which is exactly what I'm after.Inexpungible
@MiloChen Happy to have helped :-)Healthful
W
5

This comes easy with variadic template arguments:

template <class T>
void print(T t)
{
    std::cout << t;
}

template <class T, class... Args>
void print(T t, Args... args)
{
    std::cout << t << std::endl;
    print(args...);
}

int main()
{
    std::cout << std::boolalpha;
    print(3, 's', true, false);
}

Output:

3
s
true
false

Live Demo

Whilom answered 8/5, 2013 at 15:10 Comment(3)
What about consideration for optional arguments e.g. indenting, various delimiters, units?Inexpungible
And also is it possible to pass the varadic args by reference?Inexpungible
@MiloChen A) This function takes any type, so I'm sure you'll be able to pass those values in. B) Yes, you can pass varadic arguments by reference by using the Args&... syntax. rvalue-reference: Args&&...Whilom
E
1

A variation from the answers:

#include <iostream>

using namespace std;

class MyCout 
{
public:
  MyCout& operator()(bool indent) { 
    if ( indent ) cout << '\t'; 
    return *this;
  }

  template<class T>
  MyCout& operator<<(T t) {
    cout << t;
    return *this;
  }

  MyCout& operator<<(ostream& (*f)(ostream& o)) {
    cout << f;
    return *this;
  };
};

int main()
{
  MyCout mycout;
  int x = 10;
  mycout(true)<< "test" << 2 << x << endl ;
}
Elvinelvina answered 8/5, 2013 at 15:55 Comment(0)
S
0

You can use this kind of class:

#include <iostream>

using namespace std;

class CustomOut
{
public:

    template<class T>
    CustomOut& operator<<(const T& obj)
    {
        cout << " my-cout " << obj;
        return *this;
    }

};

int main()
{
    CustomOut mycout;
    mycout << "test" << 4 << "\n" << 3.4;
}

You'd need more code to use std::endl and other functors, so i've used here simple \n instead.

Supremacy answered 8/5, 2013 at 15:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.