52

I am wondering if there is way that std::cout automatically will insert some predefined value between printed sequences.

For example:

std::cout << 2 << 3 << 33 << 45 << std::endl;

outputs

233345

and I would like it to output

2 3 33 45

and I know, that it's easy to:

std::cout << 2 << " " << 3 <<  " " << 33 <<  " " << 45 << std::endl;

But I'am wondering if there is a way to automate this, such as:

std::cout << set_some_separator(" ") << 2 << 3 << 33 << 45 << std::endl;

Anyone aware of something like this being possible?

Ferenc Deak
  • 34,348
  • 17
  • 99
  • 167
  • 4
    I guess you could derive from cout and overload the << operator – Jimmy May 06 '15 at 09:59
  • You can overload the operator << and print a " " char after your string. You have to know that if you use std::cout in your class the << will always be overload. http://en.cppreference.com/w/cpp/language/operators – Laykker May 06 '15 at 10:03
  • @Laykker but we cannot overload primitive data types, we can only overload class or enumerated, so i dont think it will be possible like, `ostreamObj << 2<<3`; – pola sai ram May 06 '15 at 10:06
  • @Jimmy isn't std::cout variable? I understand you meant "in short" how to do it... – W.F. May 06 '15 at 10:07
  • You can take a look at http://stackoverflow.com/q/22840258/3235496. Quite often values are stored in a container and `std::copy` + [`infix_iterator`](http://stackoverflow.com/a/3497021/3235496) are the answer. – manlio May 06 '15 at 10:09
  • 2
    yes @WojciechFrohmberg - I meant derive from std::ostream. Another option would be to define a macro #define SEPARATE( x ) x << " " – Jimmy May 06 '15 at 10:13
  • @polasairam You're right ! An other way is to create an interface with overload operator. – Laykker May 06 '15 at 10:26
  • 1
    @Jimmy It's a good idea until you try to copy-construct your base stream ;) – Quentin May 06 '15 at 11:06
  • You could use the [44 lines of code](http://stackoverflow.com/a/30073885/52074) to do this... or you could use one line of code: `cout << boost::format("%s %s %s %s") % 2 % 3 % 33 % 45 << endl;` and not have to worry about copy/pasting the 44 lines of code everywhere. I think this fulfills the OP's unwritten "want" but not his written "requirements". – Trevor Boyd Smith May 06 '15 at 18:42
  • @Jimmy you can't derive from a variable ; and deriving from the type of cout is no good as you cannot convert `cout` to that derived type – M.M Oct 07 '17 at 00:54

7 Answers7

54

Well, I got beaten to it. I'll post this anyway.

Edit : well, after reading Nim's answer, mine does achieve the exact syntax OP wished for.

#include <iostream>
#include <algorithm>

struct with_separator {
    with_separator(std::string sep)
    : sep(std::move(sep)) {}

    std::string sep;
};

struct separated_stream {
    separated_stream(std::ostream &stream, std::string sep)
    : _stream(stream), _sep(std::move(sep)), _first(true) {}

    template <class Rhs>
    separated_stream &operator << (Rhs &&rhs) {
        if(_first)
            _first = false;
        else
            _stream << _sep;

        _stream << std::forward<Rhs>(rhs);
        return *this;
    }

    separated_stream &operator << (std::ostream &(*manip)(std::ostream&)) {
        manip(_stream);
        return *this;
    }

    private:
    std::ostream &_stream;
    std::string _sep;
    bool _first;
};

separated_stream operator << (std::ostream &stream, with_separator wsep) {
    return separated_stream(stream, std::move(wsep.sep));
}

int main()
{
    std::cout << with_separator(", ") << 1 << 2 << 3 << std::endl;
}

Output :

1, 2, 3
Quentin
  • 62,093
  • 7
  • 131
  • 191
  • I'm sorry to ask this trivial question, but could I say that `_sep(std::move(sep))` "grabs" the `std::string` that was passed by parameter, and then keep it to the class itself? – user2018675 May 07 '15 at 15:16
  • 1
    @user2018675 Absolutely ! But do notice that said string is local to the function, because it's passed by value :) – Quentin May 07 '15 at 15:19
  • Unfortunately, this doesn't work when forwarding the stream to other functions taking ostream&, because operator<< is not virtual. – Jon Watte Aug 28 '16 at 18:05
  • 1
    @JonWatte yes, in fact that `separated_stream` is not an `std::ostream` at all : I went for the simplest approach for a syntax-only trick here. But inheriting from `std::basic_ostream` and adding the required interface should be feasible. – Quentin Aug 29 '16 at 07:54
16

Simple answer is No, however, you can roll your own...

#include <iostream>
#include <sstream>

using namespace std;

struct set_some_separator{
    set_some_separator(const char* sep) : _sep(sep)
    { };

    template <typename T>
    set_some_separator& operator<<(const T& v)
    {
        _str << v << _sep;
        return *this;
    }

    friend
    ostream& operator<<(ostream& os, const set_some_separator& s)
    { return os << s._str.str(); }

    const char* _sep;
    ostringstream _str;
};

int main()
{
    cout << (set_some_separator(" ") << 2 << 3 << 33 << 45) << endl;
}

Okay the format of the cout is slightly different, hey-ho...

Nim
  • 33,299
  • 2
  • 62
  • 101
8

Not quite the same thing, but:

#include <array>
#include <iostream>
#include <iterator>

int main() {
    std::array<int, 3> data = { 1, 2, 3 };
    std::ostream_iterator<int> out(std::cout, " ");
    std::copy(data.begin(), data.end(), out);
    std::cout << '\n';
    return 0;
}
Pete Becker
  • 74,985
  • 8
  • 76
  • 165
5

How about using the ostream_iterator

int main()
{
     std::vector<int> data {2,3,33,45};
     std::copy(std::begin(data), std::end(data),
               std::ostream_iterator(std::cout, " "));
     std::cout << "\n";
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
5

A C++17 fold expression with the comma operator can create a nice one-liner:

[](auto &&...xs){ ((std::cout << xs << ',') , ...); }(2,3,33,45);
user2023370
  • 10,488
  • 6
  • 50
  • 83
4

Simple solution, maybe you can tweak it to use <<

template<typename T> 
void myout(T value) 
{ 
   std::cout << value << std::endl; 
} 

template<typename First, typename ... Rest> 
void myout(First first, Rest ... rest) 
{ 
   std::cout << first << " ";
   myout(rest...); 
}


myout('a',"Hello",1,2,3,22/7.0);
tejas
  • 1,795
  • 1
  • 16
  • 34
3

If you're just looking for a way to print a vector with its elements properly delimited (and without an extra delimiter at the end), try this:

#include <iostream>
#include <vector>

int main()
{
  std::vector<int> v{1, 2, 3};

  auto it = v.begin();
  if (it != v.end())
  {
    std::cout << *it;
    ++it;
  }

  for (; it != v.end(); ++it)
  {
    std::cout << ", " << *it;
  }
}
Resigned June 2023
  • 4,638
  • 3
  • 38
  • 49