2

I use functions like

void doStuff(type thing, bool print = false, std::ostream& S = std::cout)
{
    thing++;
    if(print)
        S << "new thing: " << thing << '\n';
}

so that I can use the same function and decide on call if I want it to print documentation of what is happening and if I wish I can print that on seperate streams -I don't know if I can even do that with std::ostream-

I now believe that it will be better to do

void doStuff(type thing, std::ostream& S = NULL)
{
    thing++;
    if(S)
        S << "new thing: " << thing << '\n';
}

but that doesn't work as std::ostream doesn't accept NULL

questions:
-is there some kind of constant of the stream type that stops the if condition?
-can I use a different type of stream that is more flexible to accept streams like string streams and file streams?
-is there a better way to handle flexible documentation?

ZKlack
  • 23
  • 3
  • 2
    Just create your own `std::ostream` implementation, which does finally nothing. That's not that hard. – πάντα ῥεῖ Oct 04 '22 at 10:28
  • You could use a pointer? – Quimby Oct 04 '22 at 10:28
  • 3
    Why not use overloading to have one implementation that uses the stream (and takes it as an argument) and another which doesn't use any streams (and doesn't take it as argument)? If the difference is *only* the printing at the end, then the stream-printing function could call the non-printing function for the common stuff, and then print the result. – Some programmer dude Oct 04 '22 at 10:30
  • 1
    More or less, that's what every decent logging framework supports category wise. – πάντα ῥεῖ Oct 04 '22 at 10:34
  • 1
    You could also take a `std::optional>` as argument – perivesta Oct 04 '22 at 10:49
  • If you want to be able to turn printing on or off in each function, then consider writing a macro for `if (print) S`, which would require you to define `print` and `S` in every function. If you want to turn it on or off globally, then consider logging or writing your own "print" function that does nothing if a global variable / define is a certain value. – darda Oct 04 '22 at 10:52
  • Duplicate: https://stackoverflow.com/questions/8243743/is-there-a-null-stdostream-implementation-in-c-or-libraries – cs1349459 Oct 04 '22 at 11:07
  • The downside for using a `onullstream << "new thing: " << thing << "\n";` is that the work is still done, even if it is a no-op and discarded. If the stream is used in performance critical code, that may be an issue (**profile** to determine if it's too costly). – Eljay Oct 04 '22 at 11:48
  • Either (as already said) create a specialisation of `std::ostream` that discards all output, or code the function to accept a pointer rather than a reference (since a pointer can be defaulted to a null pointer). A potential downside (you need to decide if it matters) of having a stream that discards output is that a fair amount of operations are performed (e.g. formatting) only to be discarded. – Peter Oct 04 '22 at 11:49
  • @Someprogrammerdude I would have really liked if you posted your comment as an answer since it seems (IMHO) to be the cleanest and most elegant solution :) – Fareanor Oct 04 '22 at 11:55
  • NULL is a hypothetical pointer that “points to nothing”. `std::ostream& S = NULL` is passing a pointer to an object reference, which is not allowed in C++. – F1yingC0der Oct 04 '22 at 12:42

4 Answers4

5

You can use the Null sink of the boost::iostream library.

Here is a working example:

#include <iostream>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/null.hpp>

boost::iostreams::stream<boost::iostreams::null_sink> nullstream{boost::iostreams::null_sink()};

void print_to_stream(std::ostream& s = nullstream)
{
  s << "hello" << std::endl;
}

int main()
{
  std::cout << "calling print_to_stream with s = std::cout" << std::endl;
  print_to_stream(std::cout);
  std::cout << "calling print_to_stream without argument" << std::endl;
  print_to_stream();

  return 0;
}

You may want to hide the variable nullstream inside some namespace or classs.

francesco
  • 7,189
  • 7
  • 22
  • 49
  • Hi @ZKlack, if this or another answers suits your question, you can consider to [accept it](https://stackoverflow.com/help/someone-answers). There is no obligation for that. – francesco Oct 05 '22 at 12:49
  • thank you, I'm new to this site your answer does exactly what I want to do and is the most suitable. I'll be reading about the boost library when I get the time thanks again :) <3 – ZKlack Oct 09 '22 at 18:06
3

Writing own streams is not difficult and can be found in good books.

I created a "NULL" stream class for you.

Please see the very simple example below.

#include <iostream>

// This is a stream which does not output anything
class NullStream : public std::ostream
{
    // streambuffer doing nothing
    class NullBuffer : public std::streambuf
    {
    public:
        int overflow(int c) noexcept override { return c; }
    } nullBuffer;

public:
#pragma warning(suppress: 26455)
    NullStream() : std::ostream(&nullBuffer) {}
    NullStream(const NullStream&) = delete;
    NullStream& operator=(const NullStream&) = delete;
};


// Define a global null stream
NullStream nout;

void doStuff(int& i, std::ostream& stream = nout)
{
    i++;
    stream << i << '\n';
}

int main() {

    int i{};
    doStuff(i, std::cout);
    doStuff(i, std::cout);
    doStuff(i, std::cout);
    doStuff(i);
    doStuff(i);
    doStuff(i);
    doStuff(i, std::cout);
}
A M
  • 14,694
  • 5
  • 19
  • 44
1

As mentioned in my comment, a possible solution could be to use simple function overloading.

// Function for not printing anything
void doStuff(type& thing)
{
    thing++;
}

// Function for printing things
void doStuff(type thing, std::ostream& S)
{
    // Do the common things first
    doStuff(thing);

    // And then print
    S << "new thing: " << thing << '\n';
}
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
0

Instead of using an ofstream reference, use a pointer so that you can accept a nullptr. The reason why you can't pass NULL is because references can't reference NULL because it may lead to dangerous undefined behavior like core dumping, segfaults, disk reformatting, etc.

Do something like this:

void doStuff(type thing, std::ostream* S = nullptr)
{
    thing++;
    if(S != nullptr)
        (*S) << "new thing: " << thing << '\n';
}

This is a bit safer and can accept a nullptr.

Overall, if you are just outputting either to a file or console, ofstream seems to be the way. On the flexible doc side, I wouldn't really have a problem doing it like this. I may just have a logging class and use that to do warnings, info logs, saving to files, etc.

Jude Davis
  • 98
  • 1
  • 5
  • 2
    This tends to become tedious in larger code bases. I'd prefer wrapping the `std::ostream` interface, such that any client code doesn't need to know, or care. – πάντα ῥεῖ Oct 04 '22 at 11:15