Search code examples
c++boostiostreamboost-iostreams

How to cast to a shared_ptr<std::ofstream> from std::shared_ptr<boost::iostreams::stream<boost::iostreams::null_sink>>


I have a member that is a shared_ptr<ofstream>.

In a specific case, I want to suppress the output, and making the stream a boost::iostreams::stream<boost::iostreams::null_sink>. Should do what I want.

I would prefer to avoid the ofstream(nullptr) suggested in this thread, it doesn't compile when wrapped in make_shared().

I'm open to other null ofstream shared_ptr suggestions, but it needs to be cross platform.

Initializing it with an output (non suppressed) works:

InputReader::InputReader(std::string filepath2, std::string filepathmain){
    this->thestreamstruct.themainWriter = std::move(std::make_shared<std::ofstream>(filepathmain.c_str()));
}

In the suppressed initializer, I declare the null sink stream shared_ptr instead of initializing the ofstream, and then want to cast this to an ofstream and assign it to the member:

InputReader::InputReader(std::string filepath2, Model* amodel){
    std::shared_ptr<boost::iostreams::stream<boost::iostreams::null_sink>> nullOstream = std::make_shared<boost::iostreams::stream<boost::iostreams::null_sink>>((boost::iostreams::null_sink()));
    std::shared_ptr<std::ofstream> nullost = std::move(std::dynamic_pointer_cast<std::ofstream>(nullOstream)); 
    this->thestreamstruct.themainWriter = nullost; 
}

This fails because there's no direct cast between std::ofstream and the boost stream.

But I can assign an ofstream ref as the boost null sink??

boost::iostreams::stream<boost::iostreams::null_sink> nullout { boost::iostreams::null_sink{} };
std::ostream& out = nullout;

This is fine, so there must be the underlying ofstream somewhere? I just don't know how to get it wrapped up into a shared_ptr.


Solution

  • Taking some pains to get life-time correct without leaking the buffer:

    Live On Coliru

    #include <boost/iostreams/device/null.hpp>
    #include <boost/iostreams/stream_buffer.hpp>
    #include <filesystem>
    #include <fstream>
    #include <iostream>
    #include <memory>
    
    using std::filesystem::path;
    using sofstream = std::shared_ptr<std::ofstream>;
    
    sofstream make_null() {
        struct both {
            boost::iostreams::stream_buffer<boost::iostreams::null_sink> b{{}};
            std::ofstream                                                ofs;
        };
        auto s = std::make_shared<both>();
        static_cast<std::ios&>(s->ofs).rdbuf(&s->b);
        return sofstream(s, &s->ofs);
    }
    
    sofstream make(path p) { return std::make_shared<std::ofstream>(p); }
    
    int main() {
    for (auto ss : { make_null(), make("test.txt") }) {
            *ss << "hello world\n";
            assert(ss->good());
            *ss << "bye world\n";
            assert(ss->good());
        }
    }
    

    I personally think this whole approach might be to clever by half and you should use std::ostream& instead of std::ofstream& anyways. (e.g. http://coliru.stacked-crooked.com/a/333cc9871c5e977b)