6

I would like to be able to do:

foo(stringstream()<<"number = " << 500);

EDIT: single line solution is crucial since this is for logging purposes. These will be all around the code.

inside foo will print the string to screen or something of the sort.

now since stringstream's operator<< returns ostream&, foo's signature must be:

foo(ostream& o);

but how can I convert ostream& to string? (or char*). Different approaches to achieving this use case are welcome as well.

Leo
  • 1,213
  • 2
  • 13
  • 26

8 Answers8

8

The obvious solution is to use dynamic_cast in foo. But the given code still won't work. (Your example will compile, but it won't do what you think it should.) The expression std::ostringstream() is a temporary, you can't initialize a non-const reference with a temporary, and the first argument of std::operator<<( std::ostream&, char const*) is a non-const reference. (You can call a member function on a temporary. Like std::ostream::operator<<( void const* ). So the code will compile, but it won't do what you expect.

You can work around this problem, using something like:

foo( std::ostringstream().flush() << "number = " << 500 );

std::ostream::flush() returns a non-const reference, so there are no further problems. And on a freshly created stream, it is a no-op. Still, I think you'll agree that it isn't the most elegant or intuitive solution.

What I usually do in such cases is create a wrapper class, which contains it's own std::ostringstream, and provides a templated member operator<< which forwards to the contained std::ostringstream. Your function foo would take a const reference to this—or what I offen do is have the destructor call foo directly, so that the client code doesn't even have to worry about it; it does something like:

log() << "number = " << 500;

The function log() returns an instance of the wrapper class (but see below), and the (final) destructor of this class calls your function foo.

There is one slight problem with this. The return value may be copied, and destructed immediately after the copy. Which will wreck havoc with what I just explained; in fact, since std::ostringstream isn't copyable, it won't even compile. The solution here is to put all of the actual logic, including the instance of std::ostringstream and the destructor logic calling foo in a separate implementation class, have the public wrapper have a boost::shared_ptr to it, and forward. Or just reimplement a bit of the shared pointer logic in your class:

class LogWrapper
{
    std::ostringstream* collector;
    int* useCount;
public:
    LogWrapper()
        : collector(new std::ostringstream)
        , useCount(new int(1))
    {
    }

    ~LogWrapper()
    {
        -- *useCount;
        if ( *useCount == 0 ) {
            foo( collector->str() );
            delete collector;
            delete useCount;
        }
    }

    template<typename T>
    LogWrapper& operator<<( T const& value )
    {
        (*collector) << value;
        return *this;
    }
};

Note that it's easy to extend this to support optional logging; just provide a constructor for the LogWrapper which sets collector to NULL, and test for this in the operator<<.

EDITED:

One other thing occurs to me: you'll probably want to check whether the destructor is being called as a result of an exception, and not call foo in that case. Logically, I'd hope that the only exception you might get is std::bad_alloc, but there will always be a user who writes something like:

log() << a + b;

where the + is a user defined overload which throws.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Interesting use of a reference count to allow for different `LogWrapper` objects to reuse the same `ostringstream`. As of the edit, how would you go about detecting whether we are unwinding the stack? – David Rodríguez - dribeas Nov 02 '11 at 11:47
  • @DavidRodríguez-dribeas `std::uncaught_exception`. (I'll admit that in practice, I rarely bother. But since I'm posting what is supposed to be a good example, I might as well try to be as correct as possible.) – James Kanze Nov 02 '11 at 12:37
  • The main downside of this seems to be that it tightly couples the string construction with the usage of the final string; calling `foo` from the destructor doesn't make this approach terribly reusable. – Frerich Raabe Nov 02 '11 at 13:25
  • @FrerichRaabe: Still the approach is appropriate for the intended use in the question, and generalizing it to call any other code is simple (just store a `std::/boost::function` passed in the constructor with the operation to call at the end. – David Rodríguez - dribeas Nov 02 '11 at 13:50
  • @FrerichRaabe Certainly. My own implementation uses a form of callback (not `boost::function`, since my code pre-dates boost by some years). Today, I'd probably make it a template on a functional object, and that's what gets called. – James Kanze Nov 02 '11 at 14:20
  • thanks, actually my use is slightly different and avoids the mentioned problems. foo(someInfo, log()<<"asdf"<<2); So I don't need to worry about log object being destroyed – Leo Nov 02 '11 at 16:37
  • "The return value may be copied, and destructed immediately after the copy." Can you explain how this is possible? operator<< returns a reference to "this". Why would a copy be involved? – Leo Nov 02 '11 at 16:44
  • @user991339 The log object is often created in a function (my `log()`) which returns it. The _copy_ actually occurs (if it occurs) in the called function, when constructing the return value. – James Kanze Nov 02 '11 at 16:56
  • Petru Marginean's light weight logger article here: http://www.ddj.com/cpp/201804215 uses a similar principle and provides a fully functional MT safe logger. – ForeverLearning Nov 03 '11 at 12:54
  • @Dilip That's another advantage of the pattern. It's very simple to make thread safe, since it tends to isolate the individual uses. – James Kanze Nov 03 '11 at 13:34
  • @James: Exactly! The idea of using a temporary object to log your stuff just-in-time is very appealing. – ForeverLearning Nov 03 '11 at 15:28
7

I would suggest you to use this utility struct:

struct stringbuilder
{
   std::stringstream ss;
   template<typename T>
   stringbuilder & operator << (const T &data)
   {
        ss << data;
        return *this;
   }
   operator std::string() { return ss.str(); }
};

And use it as:

void f(const std::string & s );

int main() 
{
  char const *const pc = "hello";

  f(stringbuilder() << '{' << pc << '}' );

  //this is my most favorite line
  std::string s = stringbuilder() << 25  << " is greater than " << 5 ;
}

Demo (with few more example) : http://ideone.com/J995r

More on my blog : Create string on the fly just in one line

Nawaz
  • 353,942
  • 115
  • 666
  • 851
3

You could use a proxy object for this; this is a bit of framework, but if you want to use this notation in a lot of places then it may be worth it:

#include <iostream>
#include <sstream>

static void foo( std::string const &s )
{
    std::cout << s << std::endl;
}

struct StreamProxy
{
    std::stringstream stream;
    operator std::string() { return stream.str(); }
};

template <typename T>
StreamProxy &operator<<( StreamProxy &s, T v )
{
    s.stream << v;
    return s;
}

static StreamProxy make_stream()
{
    return StreamProxy();
}

int main()
{
    foo( make_stream() << "number = " << 500 );
}

This program prints

number = 500

The idea is to have a little wrapper class which can be implicitely converted into a std::string. The << operator is simply forwarded to the contained std::stringstream. The make_stream() function is strictly speaking not necessary (you could also say StreamProxy(), but I thought it looks a bit nicer.

Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207
  • The basic idea is sound, but as written, it won't compile, since you cannot call `operator<<` with a temporary `StreamProxy`. – James Kanze Nov 02 '11 at 11:26
  • You could create a singleton object and clear it's contents in `operator std::string`. But I'm not sure if this is good design ;) – hochl Nov 02 '11 at 11:32
  • @James Kanze: I only quickly tried it with MSVC6 (yeah, yeah, I know...) while waiting for another build. It's indeed possible that there are other issues. I hope I got the general idea across. :-) – Frerich Raabe Nov 02 '11 at 11:37
  • @FrerichRaabe I fear that even more recent versions of VC++ fail to enforce this rule. Most other compilers do, however. – James Kanze Nov 02 '11 at 12:35
  • @JamesKanze: That is precisely the reason why I made `operator<<` a member function of the `stringbuilder` in my solution. I'm referring to your first comment :-). – Nawaz Nov 02 '11 at 12:38
1

If you don't mind using macros functions, you can make the logging function accept const string&, and use the following macro

#define build_string(expr) \
    (static_cast<ostringstream*>(&(ostringstream().flush() << expr))->str())

And suppose you foo has signature void foo(const string&), you only need the one-liner

foo(build_string("number = " << 500))

This was inspired by James Kanze's answer about static_cast and stringstream.flush. Without the .flush() the above method fails with unexpected output.

Please note that this method should not leak memory, as temporary values, whether in the pointer form or not, are still allocated on the stack and hence destroyed upon return.

Interarticle
  • 385
  • 4
  • 9
1

A couple of options other than the nice proxy solution just presented by Frerich Raabe:

  • Define a static string stream variable in the header that defines the logging function and use the comma operator in your invocation of the logging function so that this variable is passed rather than the ostream& returned by the stream insertion operator. You can use a logging macro to hide this ugliness. The problem with this solution is that it is a bit on the ugly side, but this is a commonly used approach to logging.

  • Don't use C++ I/O. Use a varargs C-style solution instead. Pass a format string as the first argument, with the remaining arguments being targets for that format string. A problem with this solution is that even if your compiler is smart enough to ensure that printf and its cousins are safe, the compiler probably won't know that this new function is a part of the printf family. Nonetheless, this is also a commonly used approach.

David Hammen
  • 32,454
  • 9
  • 60
  • 108
0

Since you're converting to string anyways, why not

void foo(const std::string& s)
{
    std::cout << "foo: " << s << std::endl;
}

...

std::stringstream ss;
ss << "number = " << 500;
foo(ss.str());
hochl
  • 12,524
  • 10
  • 53
  • 87
0

This is not possible. As the name ostream implies, it is used for output, for writing to it. You could change the parameter to stringstream&. This class has the method str() which returns a std::string for your use.

EDIT I did not read the issue with operator << returning ostream&. So I guess you cannot simply write your statements within the functions argument list but have to write it before.

Constantinius
  • 34,183
  • 8
  • 77
  • 85
  • but then I'd have to define stringstream in another line everytime I want to use foo(....) – Leo Nov 02 '11 at 11:08
  • That is true. Maybe you could cast the reference inside `foo` but I'm not sure if that is possible with descendants of `std::ostream`. – Constantinius Nov 02 '11 at 11:10
0

You can create a small wrapper around std::ostringstream that will convert back to std::string on use, and have the function take a std::string const &. The first approach to this solution can be found in this answer to a different question.

On top of that, you can add support for manipulators (std::hex) if needed.

Community
  • 1
  • 1
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • That's basically the solution I proposed, but there are a couple of subtilities to watch out for: the `operator<<` must be a member, of course (or you can lie a bit about `const`-ness, making `operator<<` take a const reference to the wrapper). – James Kanze Nov 02 '11 at 11:28