0

I'm debugging a program and I would like to make printing from that pattern:

 std::cout << firstVar << ", " << secondVar << ", " << thirdVar << endl ;

shorter, i.e, the same thing should happen if we will write the code:

shortPrint(std::cout) << firstVar << secondVar << thirdVar;

note: there is no limit for the variables quantity, it can be 1 and it can be 20, so that should also work:

shortPrint(std::cout) << firstVar << secondVar << thirdVar << anotherVar << oneMoreVar;

Someone told me that the easiest wat to do that is to create class that is called "shortPrint".

Can anyone help me figuring this out?

For start, I would say that I only need to implement a constructor and operator << overloading, but I'm not sure how to do that exactly in that case.

wannabe programmer
  • 653
  • 1
  • 9
  • 23
  • what do you want `shortPrint` to do exactly ? – Raxvan Jan 23 '14 at 09:51
  • I would say, it's close to impossible. If you create your own class, that provides operator<<, you have to provide it for all types. Or the other way round, all types that implemented << for ostream, would have to do the same for your new class now. – Torsten Robitzki Jan 23 '14 at 09:52
  • it's possible to create a wrapper on top of `stringstream` and based on the requirements of `shortPrint` process only a fiew arguments or insert delimiters , etc.. – Raxvan Jan 23 '14 at 09:54
  • You could use a template and then get a simpler syntax, like: `shortPrint(std::cout, firstVar, secondVar, thirdVar);` – Torsten Robitzki Jan 23 '14 at 09:54
  • You have two major problems: The first one is that you don't know what types may be outputted, this can be solved with templates. The second problem is that you don't know when the end of the output list is, so you have to figure out another way when to print the command or not. – Some programmer dude Jan 23 '14 at 09:54
  • @Raxvan I want to have the class shortPrint and it's only use will be using it's constructor and getting std::ostream &out as a parameter. that way when we'll construct that class it will use the operator << implemented in the class. – wannabe programmer Jan 23 '14 at 09:55

4 Answers4

6

Yes create a shortPrint class with an appropriate overloaded operator. Something like this :

class shortPrint {
  ostream &o;
public:
  shortPrint(ostream &o) : o(o) {}
  template <typename T> shortPrint &operator<<(const T &t) {
    o << t << ',';
    return *this;
  }
  // support for endl, which is not a value but a function (stream->stream)
  shortPrint &operator<<(ostream& (*pf)(std::ostream&)) {
    o << pf;
    return *this;
 }
};

That should work (basically).

To eliminate the problem of extra commas use this :

class shortPrint {
  class shortPrint2{
    shortPrint &s;
  public:
    shortPrint2(shortPrint &s) : s(s) {}
    template <typename T> shortPrint2 &operator<<(const T &t) {
      s.o << ',' << t ;
      return *this;
    }
    shortPrint &operator<<(ostream& (*pf)(std::ostream&)) {
      s.o << pf;
      return s;
    }
  };
  ostream &o;
  shortPrint2 s2;
public:
  shortPrint(ostream &o) : o(o), s2(*this) {}
  template <typename T> shortPrint2 &operator<<(const T &t) {
    o << t;
    return s2;
  }
  shortPrint &operator<<(ostream& (*pf)(std::ostream&)) {
    o << pf;
    return *this;
  }
};
Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
  • this is great! except for 1 problem, when i write the code: shortPrint(cout) << "hello" << "hi" ; instead of printing: hello, hi (end of line) it prints: hello, hi, – wannabe programmer Jan 23 '14 at 10:31
  • No I just considered that this is a very minor problem that anyone can correct easily. The solution implements the right concept, details are left aside... Something like boolean first implemented by @jrok should work ;-) But if you want something much more elegant then use two classes (shortPrint and shortPrint2), the 1st don't print '`,`' and the second one does... This will be much more efficient as no test will be involved. – Jean-Baptiste Yunès Jan 23 '14 at 11:22
2

You can do something like the following:

class _ostream_wrap
{
public:
    template <class T>
    _ostream_wrap& operator << (const T & v)
    {
        m_ost << "[ " << v << " ]";
        return (*this);
    }
    explicit _ostream_wrap(std::ostream & ost)
        :m_ost(ost)
    {}
private:
    std::ostream&   m_ost;
};

template <class OSTREAM>
_ostream_wrap shortPrint(OSTREAM & o)
{
    return _ostream_wrap(o);
}

//...
shortPrint(std::cout) << 1 << 2;

however this implementation will not work on rvalues:

//you can't do something like the following:
shortPrint(MyOstream(some_args)) << 1;

This is because the class _ostream_wrap keeps a reference to the stream and for rvalues case it needs to make a copy. To make a copy you need to have two implementations (this would be the final version):

template <class OSTREAM>
class _ostream_wrap
{
public:
    template <class T>
    _ostream_wrap<OSTREAM>& operator << (const T & v)
    {
        m_ost << "[ " << v << " ]";
        return (*this);
    }


public:
    //the constructor is harder to write so i decided 
    //that for this i will keep the member public
    OSTREAM     m_ost;
};

template <class OSTREAM>
_ostream_wrap<OSTREAM&> shortPrint(OSTREAM & o)
{
    _ostream_wrap<OSTREAM&> rvalue;
    rvalue.m_ost = o;
    return rvalue;
}
template <class OSTREAM>
_ostream_wrap<OSTREAM> shortPrint(const OSTREAM & o)
{
    _ostream_wrap<OSTREAM> rvalue;
    rvalue.m_ost = o;
    return rvalue;
}
Raxvan
  • 6,257
  • 2
  • 25
  • 46
1

Here's something with very basic functionality:

#include <iostream>

struct shortPrint {
    explicit shortPrint(std::ostream& os)
        : strm(&os), first(true) {}

    template<typename T>
    shortPrint& operator<<(T&& t)
    {
        if (first) {
            first = false;
        } else {
            *strm << ", ";
        }
        *strm << std::forward<T>(t);
        return *this;
    }

    shortPrint& operator<<( std::ostream& (*func)(std::ostream&) )
    {
        *strm << func;
        return *this;
    }

private:

    std::ostream* strm;
    bool first;
};

int main()
{
    int i = 3;
    shortPrint(std::cout) << "1" << 2 << i << std::endl;
    shortPrint(std::cout) << 4;
}

The tricky part is getting the commas to be printed correctly. I chose to print them before every streamed object, except before the very first one. As you can see, there's one general operator<< template that simply prints the comma and forwards the argument. The next problem are stream manipulators, because we don't want to print commas before them. The other operator<< overload takes care of that. If you want to be more generic, there's two more you need to take care of (see no. 9 here ).

Hope that helps.

jrok
  • 54,456
  • 9
  • 109
  • 141
  • I missed the fact that you want an automatic endl. One option is to do that in the destructor. About `std:forward` - it's a C++11 feature. Check the documentation of your compiler to see how you can enable it. If you're stuck with C++03, drop it and take the parameter by const reference instead. – jrok Jan 23 '14 at 10:46
0

Return the ostream from the function. Something like:

std::ostream &shortPrint(std::ostream &out) {
    //whatever you need here
    return out;
}

Edit: you the kind of formatting you need, you need to make a class with overloaded stream operator that returns the class. But you need to keep the reference to the needed stream in the class.

Gasim
  • 7,615
  • 14
  • 64
  • 131