0

Can anyone suggest a way to have a null std::ostringstream that avoids doing any work on the parameters passed to it with <<?

There are two related posts here Implementing a no-op std::ostream and Printing to nowhere with ostream , so far the most promising solution is https://stackoverflow.com/a/760353/826203 , but while test it

int main() {
    onullstream os;
    os << 666;

//  std::ostringstream & oss = os; // error C2440: 'initializing' : cannot convert from 'onullstream' to 'std::ostringstream &'
    oss << "hello, world";
}

however, this could only be used like os<<666, but could not be used as a std::ostringstream &. any way out here?

Community
  • 1
  • 1
athos
  • 6,120
  • 5
  • 51
  • 95
  • 1
    You get an error because `std::ostringstream` and `onullstream` are different parts of the inheritance tree. While both inherits from `std::ostream` they are otherwise unrelated. Instead I think the requirement to use `std::ostringstream` here is a false requirement, something that you don't really need. For example, if you have a function that you call with the stream you would have it take a `std::ostream` reference. Then you could pass a `std::ostringstream` object *or* a `onullstream` object. – Some programmer dude Oct 17 '16 at 09:21

1 Answers1

3

The easiest way to create a non-operational stream is to actually not create a custom stream class but rather to disable an existing stream. For example, you can disable formatting to an std::ostream by setting its stream buffer to null:

std::ostringstream out;
out.std::ostream::rdbuf(0);
// any attempt to write anything to out will fail.

If you need a stream which successfully fails to format data you can create a stream buffer which doesn't store any bytes and is always successful. However, when using this stream buffer the actually formatting will be performed:

struct nullbuf: std::streambuf {
    std::streambuf::int_type overflow(std::streambuf::int_type c) {
        return std::char_traits<char>::not_eof(c);
    }
};
// ...
nullbuf            buf;
std::ostringstream out;
out.std::ostream::rdbuf(&buf);

Note that I would also recommend not to have functions take a std::ostringstream as arguments. Instead, any function which doesn't construct the stream should travel in terms of std::ostream&. If your existing interfaces already take an std::ostringstream you can create a a null stream by deriving from std::ostringstream and setting the stream buffer appropriately:

class onullstream
    : private virtual nullbuf
    , public std::ostringstream {
public:
    nullstring()
        : std::ios(this)
        , std::ostringstgream() {
        this->std::ostream::rdbuf(this);
    }
};
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • thank you! May I ask 1) what do you mean by "the actually formatting will be performed", and 2) why recommend not to take a `std::ostringstream` as arguments, instead, take `std::ostream&`? – athos Oct 17 '16 at 22:31
  • @athos: sorry, I keep forgetting that the derived streams have overloads for `rdbuf()` (which are rather misguided). Just direct the call to `std::ios`-version of the function, e.g.: `out.std::ostream::rdbuf(&buf)` (likewise for the other version). – Dietmar Kühl Oct 17 '16 at 22:48
  • @athos: with respect to your other questions: 1. the `nullbuf` simply eats all characters sent to it. However, since it is successful all characters are produced. That is, when you insert, e.g., an `int` the `int` will be transformed into a sequence of `char`s representing the `int`'s value and the `char`s won't be used. That is, there is potentially a substantial amount of work done just for ignoring characters! Invalidating the stream fairly forcibly, i.e., setting the stream buffer to `0`, causes the stream operations to not convert the value but all operations fail. – Dietmar Kühl Oct 17 '16 at 22:51
  • @athos: whether the stream failing matters, I don't know - most likely it doesn't: output streams are rarely tested for successful operations. Note, that in all cases the actual arguments are evaluated. For example, when using `out << f()` the function `f()` is called but the result isn't used or, at least, the characters are not used. For 2.: formatting generally doesn't care what stream it gets - it could be an `std::ostringstream`, an `std::ofstream`, or some other custom stream. Correspondingly, the functions are best off traveling in terms of `std::ostream&`. – Dietmar Kühl Oct 17 '16 at 22:54
  • @athos: I also updated the code in the answer (which still haven't compiled - I'm currently using a mobile device. It shoudl be sufficient to direct the compiler to call the base class version of `rdbuf()`. – Dietmar Kühl Oct 17 '16 at 22:57
  • @athos: well, there was a `public` missing to make the default constructor accessible. – Dietmar Kühl Oct 17 '16 at 22:58
  • Thank you! I just update your code, pls check if it's what you mean? Still two more questions: 1. am I understand it correctly, that in *all* 3 approaches, the formmating will take pace, i.e. all characters are produced -- when you insert, e.g., an int the int will be transformed into a sequence of chars representing the int's value and the chars won't be used. 2. in the #2 and #3 approach, what will be the `c` to be passed into `std::streambuf::int_type overflow(std::streambuf::int_type c)`? will it be `0`, so that it's almost the same as approach #1 that has 0 length buffer? – athos Oct 17 '16 at 23:17