2

if I want to create a logging class, say

class logging_class {
public:
    std::ofstream error;
    std::ofstream event;
    logging_class() {}
    logging_class(string err, string evt) {
        error.open(err);
        event.open(evt);
    }
    ~logging_class() {
        error.close();
        event.close();
    }
};

so that later I can easily create program log:

logging_class logger("Log_Error.txt","Log_Event.txt");
logger.error << "something wrong";
logger.event << "something interesting";

Now, at the early stage of development, I want to redirect all the output to screen (i.e. std::cout). I don't want to change the usage of logger.error<<"something wrong"; to std::cout<<"something wrong";, because later I will need to change all instances of std::cout to either logger.error or logger.event (maybe thousands of them). Can I make some easy changes in the class logger to do this?

Update: Following Enlico's instruction, I can modify the logging_class as:

class logging_class {
public:
    //std::ofstream error;
    //std::ofstream event;
    std::ostream& error;
    std::ostream& event;
    logging_class():error(std::cout),event(std::cout) {}
    //logging_class(string err, string evt) {
    //    error.open(err);
    //    event.open(evt);
    //}
    //~logging_class() {
    //    error.close();
    //    event.close();
    //}
};

and after creating object with default constructor: logging_class logger; I can now redirect all the logging to screen display.

Enlico
  • 23,259
  • 6
  • 48
  • 102
zhouqin
  • 53
  • 5
  • 3
    `ostream& error` and initialize it with the desired stream. – zdf May 18 '21 at 04:22
  • https://onlinegdb.com/NXfdSYLXv is a way of doing it using a pointer so that you can change between streams - that way you can have it set to cout until the program reads its config file to find out where the logging should go. You could use an ostream reference but then you need to know where it is going when you construct the logger class. Or, you could always just make a ostream and change the rdbuf so that it points to different places: https://stackoverflow.com/questions/14860267/what-can-go-wrong-if-cout-rdbuf-is-used-to-switch-buffer-and-never-set-it-back – Jerry Jeremiah May 18 '21 at 05:30

1 Answers1

3

Think of std::cout of what it actually is: an object. It is just that:

The global objects std::cout and std::wcout control output to […]

So when you say that you want to temporarily use std::cout in place of std::ofstream you're mixing apples (object std::cout) with oranges (class std::ofstream).

What you want to do, instead, is to use std::cout instead of an object of class std::ofstream.

But std::cout is not of class std::ofstream, so error can't hold it; it is of class std::ostream, which is a superclass of the former.

Therefore, as suggested in a comment, you can make error/event references to an object of that class, std::ofstreamstd::ostream&, and initialize them with std::cout or with std::ofstream{"filename.txt"} via an appropriate constructor of the logging_class.

Enlico
  • 23,259
  • 6
  • 48
  • 102