4

I was messing around with std::ostringstream whilst looking at this question: sprintf in c++?, and noticed the stringbuilder() wrapper by Nawaz and thought, well that ought to work with std::ostringstream.

So my first attempt was the following:

std::cout << (std::ostringstream("select * from foo limit") << max_limit).str() << std::endl;

Now this obviously fails to compile (correctly) as the result of the operator<< is a std::ostream - which doesn't have the member str(). So I thought a cast should do the trick, and specifically a cast to a const reference (works with a cast to a normal reference too), so second attempt:

std::cout << static_cast<std::ostringstream const&>(std::ostringstream("select * from foo limit") << max_limit).str() << std::endl;

Now this compiles fine and runs, however the output is, well, not what I was expecting.

10lect * from foo limit

Now - here's the question, am I invoking some undefined behaviour somewhere - and if so where? And how is this different to the approach that Nawaz has taken (I guess aside from the result of his operator is the stringbuilder itself rather than std::ostream).

EDIT: here is the ideone code.

EDIT: oops - forgot to specify, max_limit is int.

Community
  • 1
  • 1
Nim
  • 33,299
  • 2
  • 62
  • 101

3 Answers3

4

You need to move the stream's position to the end of the internal buffer used by ostringstream:

  std::ostringstream out("select * from foo limit ", std::ios_base::app);
  out << max_limit;
  std::cout << out.str () << std::endl;

See the documentation on ostringstream constructor.

Vijay Mathew
  • 26,737
  • 4
  • 62
  • 93
  • Ah, that may be it. It's shocking that the two-argument ctor for `ostringstream` doesn't have `app` as the default. – Lightness Races in Orbit May 11 '11 at 12:17
  • BTW, cplusplus.com is notoriously error-ridden. Recommending it is not recommended. :) – Lightness Races in Orbit May 11 '11 at 12:18
  • @Tomalak What would you suggest as an online c++ reference instead? – Captain Giraffe May 11 '11 at 12:30
  • 1
    @Nim - A stringstream initialized with a string is positioned to **read** from the string. If we changed from the current default, we would just get the other set of questions! :-) – Bo Persson May 11 '11 at 12:30
  • @Nim: As did I. :) @Bo: For an `ostringstream` as opposed to an `istringstream` or bi-directional `stringstream`, this still feels backwards. – Lightness Races in Orbit May 11 '11 at 12:34
  • @Bo Persson, it's a `std::ostringstream` - which AFAIK is only ever used for outputting content to a string, so isn't it realistic to assume that append is the default mode of operation? – Nim May 11 '11 at 12:35
  • @Nim - It sort of is appending, if you just don't provide the first string in the constructor. I believe the decision was made for stringstream, which is both in and out. Where should that start? Read or write as the first operation? And now that you know how it works, you can add an extra parameter to the constructor. – Bo Persson May 11 '11 at 13:30
  • @Bo, still not convinced, it makes no sense for `ostringstream`, ah well, there's little that can be done about it - we have to live with it... – Nim May 11 '11 at 14:09
  • @Tomalak Thanks, I didn't expect to find the current draft in that collection; big thanks. Also a big thanks to your very active involvement and knowledge here. – Captain Giraffe May 11 '11 at 23:01
  • @Captain: Glad to help. Thanks to you, too! – Lightness Races in Orbit May 12 '11 at 00:37
2

What's maxLimit?

Some of the ostream operator<< overloads are free functions, like:

ostream& operator<<(ostream& os, T const&);

If the stream is a temporary (which in your case it is), it cannot bind to that ref-to-non-const, and the overload cannot be chosen.

So you may be using a non-preferred overload by accident; possibly something like the overload for char const*. Hard to tell without knowing what maxLimit is.

This is a limitation when trying to do this serialisation on a single line, and you can't get around it.

You're also attempting to stream std::cout to std::cout, which is obviously not what you intended to do.

Update Vijay figured it out.

std::cout << static_cast<std::ostringstream const&>(
   std::ostringstream("select * from foo limit", std::ios_base::app) << max_limit
).str() << std::endl

The above is still definitely worth bearing in mind, though.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
0

You could also simply use flush as:

static_cast<std::ostringstream const&>(std::ostringstream().flush() << "select * from foo limit " << max_limit).str() << std::endl;

But then the code is not same. Its using default constructor.

As a sidenote : isn't dynamic_cast more appropriate here?

Demo : http://www.ideone.com/06xNS

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • why `dynamic_cast`? I could understand if `std::ostringstream` derived from multiple bases, it doesn't just `std::ostream`, so surely the `static_cast` is sufficient? – Nim May 11 '11 at 12:42
  • @Nim: Its downcasting, so I think `dynamic_cast` is more appropriate, though `static_cast` would work fine since you're sure that *dynamic* type is `std::ostringstream`. – Nawaz May 11 '11 at 12:44
  • Generally you should avoid casts. There is no need for any casts here, just take out the `ostringstream` from the `std::cout <<` call. – Vijay Mathew May 11 '11 at 13:59
  • @Vijay: That would produce the same result, I agree; I know that @Nim knows that as well. But that isn't what Nim was trying to do; he wanted to do that just in one line (if I understood his intention correctly). – Nawaz May 11 '11 at 14:22