7

Is it possible to write a method that takes a stringstream and have it look something like this,

void method(string str)
void printStringStream( StringStream& ss)
{
    method(ss.str());
}

And can be called like this

stringstream var;
printStringStream( var << "Text" << intVar << "More text"<<floatvar);

I looked up the << operator and it looks like it returns a ostream& object but I'm probably reading this wrong or just not implementing it right.

Really all I want is a clean way to concatenate stuff together as a string and pass it to a function. The cleanest thing I could find was a stringstream object but that still leaves much to be desired.

Notes:

I can't use much of c++11 answers because I'm running on Visual Studio 2010 (against my will, but still)

I have access to Boost so go nuts with that.

I wouldn't be against a custom method as long as it cleans up this mess.

Edit:

With @Mooing Duck's answer mixed with @PiotrNycz syntax I achieved my goal of written code like this,

try{

    //code

}catch(exception e)
{   
    printStringStream( stringstream() << "An exception has occurred.\n"
                            <<"    Error: " << e.message 
                            <<"\n If this persists please contact "<< contactInfo
                            <<"\n Sorry for the inconvenience");
}

This is as clean and readable as I could have hoped for.

Hopefully this helps others clean up writing messages.

Community
  • 1
  • 1
Dan
  • 2,625
  • 7
  • 39
  • 52
  • 2
    Visual Studio 11 has the entire C++11 library, and several of the language features. _Which_ Visual Studio are you limited to? Also, other than capitalization, the code looks fine to my eye; should work fine. – Mooing Duck Sep 20 '12 at 19:49
  • Did you try that? If so, what happened? If not - why? – Kiril Kirov Sep 20 '12 at 19:49
  • Why don't you just fill the stream first, then pass it as argument? – Kiril Kirov Sep 20 '12 at 19:52
  • @KirilKirov: I'd hazard a guess the word 'macro' may have something to do with the answer to that question. – WhozCraig Sep 20 '12 at 19:55
  • @MooingDuck 2010 and that's **BLEEDING** edge here. – Dan Sep 20 '12 at 20:03
  • 2
    @Dan VS 2010 has quite a lot of C++11 features implemented. It even has ;) – Mihai Todor Sep 20 '12 at 20:06
  • @KirilKirov I have tried that and I get a message saying no method matches these arguments. – Dan Sep 20 '12 at 20:07
  • @MihaiTodor Thanks for the info. I keep getting answers that 2010 can't handle and was told by some co-works 2010 didn't handle `C++11`. I stand corrected. – Dan Sep 20 '12 at 20:17
  • @Dan: http://wiki.apache.org/stdcxx/C++0xCompilerSupport – Mooing Duck Sep 20 '12 at 21:19
  • @MooingDuck You're a gentleman and a scholar! Thanks for the great info. – Dan Sep 20 '12 at 21:39
  • You can replace `stringstream()` call by defining a macro. Something like `#define printDebug(x) printStringStream(std::stringstream() << x)` does the trick for me. – Pavlo Dyban Oct 31 '13 at 10:02

5 Answers5

8

Ah, took me a minute. Since operator<< is a free function overloaded for all ostream types, it doesn't return a std::stringstream, it returns a std::ostream like you say.

void printStringStream(std::ostream& ss)

Now clearly, general ostreams don't have a .str() member, but they do have a magic way to copy one entire stream to another:

std::cout << ss.rdbuf();

Here's a link to the full code showing that it compiles and runs fine http://ideone.com/DgL5V

EDIT

If you really need a string in the function, I can think of a few solutions:

First, do the streaming seperately:

stringstream var;
var << "Text" << intVar << "More text"<<floatvar;
printStringStream(var);

Second: copy the stream to a string (possible performance issue)

void printStringStream( ostream& t)
{
    std::stringstream ss;
    ss << t.rdbuf();
    method(ss.str());
}

Third: make the other function take a stream too

Community
  • 1
  • 1
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • And here I was experimenting with `copy` instead of `rdbuf`. – chris Sep 20 '12 at 19:57
  • So I know this wasn't really clear because I simplifying my problem for the example but I need a string on the inside of this method to pass it to another function that looks like this. `void method(string str)` how do I turn `rdbuf()` into something that works with that? – Dan Sep 20 '12 at 20:32
  • That's prefect! My `method` actually uses another `stringstream` to concatenate a time stamp and some other info for logging so I can overload it with one that takes a `streambuf*` instead of a `string` and my problems are over. As a noob i didn't realize I could `<<` it. Thanks so much! – Dan Sep 20 '12 at 21:00
3

For ease of writing objects that can be inserted into a stream, all these classes overload operator<< on ostream&. (Operator overloading can be used by subclasses, if no closer match exists.) These operator<< overloads all return ostream&.

What you can do is make the function take an ostream& and dynamic_cast<> it to stringstream&. If the wrong type is passed in, bad_cast is thrown.

void printStringStream(ostream& os) {
    stringstream &ss = dynamic_cast<stringstream&>(os);
    cout << ss.str();
}

Note: static_cast<> can be used, it will be faster, but not so bug proof in the case you passed something that is not a stringstream.

quantum
  • 3,672
  • 29
  • 51
3

Make your wrapper over std::stringstream. In this new class you can define whatever operator << you need:

class SSB {
public:
   operator std::stringstream& () { return ss; }

   template <class T>
   SSB& operator << (const T& v) { ss << v; return *this; }
   template <class T>
   SSB& operator << (const T* v) { ss << v; return *this; }
   SSB& operator << (std::ostream& (*v)(std::ostream&)) { ss << v; return *this; }
   // Be aware - I am not sure I cover all <<'s       
private:
   std::stringstream ss;
};

void print(std::stringstream& ss)
{
    std::cout << ss.str() << std::endl;
}

int main() {
  SSB ssb;
  print (ssb << "Hello" << " world in " << 2012 << std::endl);
  print (SSB() << "Hello" << " world in " << 2012 << std::endl);
}
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
  • +1 for showing me I could have been doing `stringstream()< – Dan Sep 20 '12 at 20:44
  • This is perfect! Just what I was looking for so I could print any and all objects easily using the `<<` notation, and having to go through a Print() function wrapper before going to `cout`. – gbmhunter Jan 07 '14 at 04:34
2

Since you know you've got a stringstream, just cast the return value:

stringstream var;
printStringStream(static_cast<stringstream&>(var << whatever));
Pete Becker
  • 74,985
  • 8
  • 76
  • 165
0

Just to add to the mix: Personally, I would create a stream which calls whatever function I need to call upon destruction:

#include <sstream>
#include <iostream>

void someFunction(std::string const& value)
{
    std::cout << "someFunction(" << value << ")\n";
}

void method(std::string const& value)
{
    std::cout << "method(" << value << ")\n";
}

class FunctionStream
    : private virtual std::stringbuf
    , public std::ostream
{
public:
    FunctionStream()
        : std::ostream(this)
        , d_function(&method)
    {
    }
    FunctionStream(void (*function)(std::string const&))
    : std::ostream(this)
    , d_function(function)
    {
    }
    ~FunctionStream()
    {
        this->d_function(this->str());
    }
private:
    void (*d_function)(std::string const&);
};

int main(int ac, char* av[])
{
    FunctionStream() << "Hello, world: " << ac;
    FunctionStream(&someFunction) << "Goodbye, world: " << ac;
}

It is worth noting that the first object sent to the temporary has to be of a specific set of types, namely one of those, the class std::ostream knows about: Normally, the shift operator takes an std::ostream& as first argument but a temporary cannot be bound to this type. However, there are a number of member operators which, being a member, don't need to bind to a reference! If you want to use a user defined type first, you need to extract a reference temporary which can be done by using one of the member input operators.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • I think I almost get what's happening here. Could you add some example input and output so I could lock in what you did here? – Dan Sep 21 '12 at 14:50
  • I think it's something like `method(Hello, world: : ##) someFunction(Goodbye, world: ##)` where `##` is the number of arguments. Is that about right? – Dan Sep 21 '12 at 14:53
  • Yes: the respective function is called with string formatted. The main point is that there is only one special entity involved (the `FunctionStream` object) and no cast or anything is needed. – Dietmar Kühl Sep 21 '12 at 18:04