简体   繁体   中英

Default “NULL” value for an ofstream

I have currently a function

int myfun(const int a) {
    ...
    return rval;
}

that performs several actions.

I mean to adapt it to write debug information on its behaviour or not according to some parameter that I can pass. In the cases I want to write that info, I also want to pass the ofstream to use. And I want applications that were using myfun to still work with no modifications.

So I would ideally change to

int myfun(const int a, ofstream & ofs) {
    ...
    if (!(ofs == NULL)) {
        ofs << ...
    }
    ...
    if (!(ofs == NULL)) {
        ofs << ...
    }
    return rval;
}

with a default value similar to &ofs=NULL . I know NULL is not appropriate.

What is an appropriate way of handling this?

Note 1 : I could pass an optional parameter with the output file name, but that is less flexible.

Note 2 : I could also change to

int myfun(const int a, const bool debug_info, ofstream & ofs) {
    ...
    if (debug_info) {
        ofs << ...
    }

with a default value debug_info=false . I guess this still requires a default value for ofs as above.

Note 3 : The accepted answer in Default ofstream class argument in function proposes an overload without the ofs parameter. In my case that may not work, as I mean to not write anything when " ofs=NULL ".

Note 4 : This other answer apparently works, but it seems somewhat contrived to me, and I am not sure it provides all the same functionality as with passing an ofs .

Related :

Is there a null std::ostream implementation in C++ or libraries?

I want applications that were using myfun to still work with no modifications.

If so, use an ofs with default nullptr

int myfun(const int a, ofstream *ofs = nullptr)
{
    if (ofs != nullptr)
    {
        // (*ofs) << ... ;
    }

    // ...
}

You can't use a reference parameter ofstream& ofs for such function because a reference cannot be null.

Make an abstract Logger class. It has a method for logging a message. In derived classes you can add logging to file (ofstream) or simply do nothing. You can use any logger, the implementation of myfun() stays the same.

#include <fstream>

class Logger {
public:
    virtual void log(const char*) = 0;
};

class NullLogger: public Logger {
public:
    void log(const char*) override {};
};

class FileLogger: public Logger {
public:
    FileLogger(std::ofstream& s): ofs(s){}
    void log(const char* msg) override {
        ofs << msg;
    }
private:
    std::ofstream& ofs;
};

static NullLogger defaultLogger;
int myfun(const int a, Logger& logger=defaultLogger)
{
    logger.log("hello");
    // ...
    logger.log("asdf");
}

int main(){
    std::ofstream ofs;
    FileLogger fileLogger(ofs);
    NullLogger nullLogger;
    myfun(10,fileLogger); // logs to file
    myfun(10,nullLogger); // logs nothing
    myfun(10); // also logs nothing
    return 0;
}

In C++17 there is a solution involving std::optional but since it requires default constructible types, std::reference_wrapper has to be used too.

#include <fstream>
#include <optional>
#include <functional>

int myfun(const int a, std::optional<std::reference_wrapper<std::ofstream>> ofs)
{
    if (ofs) {
        ofs->get() << "...";
        return 1;
    }
    else{
        return 0;
    }
}
#include <iostream>
int main(){
    std::ofstream file;
    //Calling is quite nice.
    std::cout<<myfun(10,{file})<<'\n'; //Prints 1
    std::cout<<myfun(10,{})<<'\n'; //Prints 0
}

The downside of this solution, although idiomatic, is being verbose and heavy on the syntax in some cases.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM