Is there a good idiom to deal with alternative output streams?

会有一股神秘感。 提交于 2019-12-09 20:24:50

问题


I want to write a simple program that depending on the options passed it the executable will print the output to the screen or to a file. The program is simple.

#include<iostream>
int main(int argc, char* argv[]){
    ... process options...

    std::ostream& out = ... // maybe std::cout, maybe a *new* std::ofstream;
    out << "content\n";
}

Is there a good idiom to make out refer alternatively to std::cout or a file stream at runtime?

I tried with pointers, but it is horrible. I couldn't avoid using pointers (Not to mention that more ugly code is needed to delete the pointer later).

#include<iostream>
#include<ofstream>
int main(int argc, char* argv[]){

    std::string file = argc>1?argv[1]:"";
    std::clog << "file: " << file << '\n';
    // if there is no argument it will print to screen
    std::ostream* out = (file=="")?&std::cout:(new std::ofstream(file)); // horrible code
    *out << "content" << std::endl;
    if(out != &std::cout) delete out;

}

I don't know, perhaps there is some feature of C++ streams that allows this. Perhaps I have to use some kind of type erasure. The problem, I think, is that std::cout is something that already exists (is global), but std::ofstream is something that has to be created.

I managed to use open and avoid pointers but it is still ugly:

int main(int argc, char* argv[]){

    std::string file = argc>1?argv[1]:"";
    std::clog << "file: " << file << '\n';

    std::ofstream ofs; 
    if(file != "") ofs.open(file); 
    std::ostream& out = (file=="")?std::cout:ofs;
    out << "content" << std::endl;
}

回答1:


My preference is to use streams with suitable stream buffers installed. Here is one way direct output to a file or to std::cout:

#include <iostream>
#include <fstream>
int main(int ac, char* av) {
    std::ofstream ofs;
    if (1 < ac) {
       ofs.open(av[1]);
       // handle errors opening the file here
    }
    std::ostream os(file? file.rdbuf(): std::cout.rdbuf());
    // use os ...
}



回答2:


So much over-engineering.

#include <iostream>
#include <fstream>

int main(int argc, char* argv[]) {

    std::ofstream ofs(argc > 1 ? argv[1] : "");
    std::ostream& os = ofs.is_open() ? ofs : std::cout;

    // use os ...
}



回答3:


A runtime binding of the desired stream will pretty much need to look like what you already have.

On the pointer issue, sure you can clean it up a bit... maybe something like this? This is assuming you only want to create the ofstream if the argument exists.

int main(int argc, char* argv[]){ 
    std::string file = argc > 1 ? argv[1] : "";
    std::clog << "file: " << file << '\n';

    // if there is no argument it will print to screen
    std::unique_ptr<std::ostream> fp;
    if (file == "")
        fp = std::make_unique<std::ofstream>(file);

    std::ostream& out = (fp && fp->is_open()) ? std::cout : *fp; // not so horrible code

    out << "content" << std::endl;
}

If the dynamic object is not required, the easiest may be something list this;

int main(int argc, char* argv[]){
    std::string filename = (argc > 1) ? argv[1] : "";

    std::ofstream file(filename);

    // if there is no argument (file) it will print to screen
    std::ostream& out = file.is_open() ? file : std::cout;

    out << "content" << std::endl;
}



回答4:


I often use something like this for command-line tools:

int main(int, char* argv[])
{
    std::string filename;

    // args processing ... set filename from command line if present
    if(argv[1])
        filename = argv[1];

    std::ofstream ofs;

    // if a filename was given try to open
    if(!filename.empty())
        ofs.open(filename);

    // bad ofs means tried to open but failed
    if(!ofs)
    {
        std::cerr << "Error opeing file: " << filename << '\n';
        return EXIT_FAILURE;
    }

    // Here either ofs is open or a filename was not provided (use std::cout)

    std::ostream& os = ofs.is_open() ? ofs : std::cout;

    // write to output
    os << "Some stuff" << '\n';

    return EXIT_SUCCESS;
}



回答5:


You could use a shared pointer to a stream for the polymorphic behavior:

#include <memory>
#include <fstream>
#include <sstream>
#include <iostream>

void nodelete(void*) {}

std::shared_ptr<std::ostream> out_screen_stream() { return std::shared_ptr<std::ostream>(&std::cout, nodelete); }
std::shared_ptr<std::ostream> out_file_stream() { return std::make_shared<std::ofstream>(); }
std::shared_ptr<std::ostream> out_string_stream() { return std::make_shared<std::ostringstream>(); }

int main ()
{
    std::shared_ptr<std::ostream> out;

    // case condition:
    out = out_screen_stream();
    out = out_file_stream();
    out = out_string_stream();

    *out << "content" << std::endl;
    return 0;
}

Note: A std::shared_ptr allows managing different possible streams, where some streams should not get deleted (e.g.: std::cout).

Similar, but with std::unique_ptr:

#include <memory>
#include <fstream>
#include <sstream>
#include <iostream>

class Deleter
{
    public:
    Deleter(bool use_delete = true) : use_delete(use_delete) {}

    template <typename T>
    void operator () (const T* p) {
        if(use_delete)
            delete p;
    }

    bool nodelete() const { return ! use_delete; }

    private:
    bool use_delete;
};

using unique_ostream_ptr = std::unique_ptr<std::ostream, Deleter>;
unique_ostream_ptr out_screen_stream() { return unique_ostream_ptr(&std::cout, false); }
unique_ostream_ptr out_file_stream() { return unique_ostream_ptr{ new std::ofstream }; }
unique_ostream_ptr out_string_stream() { return unique_ostream_ptr{ new std::ostringstream  }; }

int main ()
{
    unique_ostream_ptr out;

    // case condition:
    out = out_screen_stream();
    out = out_file_stream();
    out = out_string_stream();

    *out << "content" << std::endl;
    return 0;
}



回答6:


Maybe a reference?

#include<iostream>
#include<ofstream>


int main(int argc, char* argv[])
{
    auto &out = std::cout;
    std::ofstream outFile;

    std::string fileName = argc>1?argv[1]:"";

    std::clog << "file: " << file << '\n';
    // if there is no argument it will print to screen
    if(!fileName.empty())
    {
        outFile.open(fileName);
        out = outFile;
    }

    out<<"one, one, two";

    return 0;
}


来源:https://stackoverflow.com/questions/38454951/is-there-a-good-idiom-to-deal-with-alternative-output-streams

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!