I need to write a program that output either to the std::cout
or to some file. I was reading this post to see how to do. However I would like to separate the manage
Since they both inherit from std::ostream
, you can just assign it to a std::ostream&
.
In your case, you can simply do something like this:
#include
#include
void do_stuff(const char* filename = nullptr) {
std::ofstream _f;
std::ostream& os = filename ? (_f.open(filename), _f) : std::cout;
os << "Output normally";
// If you want to check if it is a file somewhere else
if (std::ofstream* fp = dynamic_cast(&os)) {
std::ofstream& f = *fp;
// But here you can probably check the condition used to make the file
// (e.g. here `filename != nullptr`)
}
// After returning, `os` is invalid because `_f` dies, so you can't return it.
}
A simpler approach would be to not worry about this at all. Just put all of your code that outputs stuff inside one function that takes a std::ostream&
parameter, and call it with a std::ofstream
or another std::ostream
:
void do_stuff(std::ostream& os) {
os << "Write string\n";
}
int main() {
if (using_file) {
std::ofstream f("filename");
do_stuff(f);
} else {
do_stuff(std::cout);
}
}
If you want to be able to return the object without the file closing and becoming a dangling reference, you need to store it somewhere. This example stores it in a struct:
#include
#include
#include
#include
#include
struct sw_ostream {
private:
// std::optional f;
// Use raw storage and placement new pre-C++17 instead of std::optional
alignas(std::fstream) unsigned char f[sizeof(std::fstream)];
std::ostream* os;
bool did_construct_fstream() const noexcept {
// If `os` is a pointer to `f`, we placement new`d, so we need to destruct it
return reinterpret_cast(os) == f;
}
// Destroys currently held std::fstream
// (Must have been constructed first and have `os` point to it)
void destruct() noexcept {
static_cast(*os).~basic_fstream();
}
public:
sw_ostream() = default;
sw_ostream(std::ostream& os_) : os(&os_) {}
template
explicit sw_ostream(Args&&... args) {
os = new (f) std::fstream(std::forward(args)...);
}
sw_ostream(std::fstream&& f) : os(nullptr) {
*this = std::move(f);
}
sw_ostream(sw_ostream&& other) noexcept {
*this = std::move(other);
}
sw_ostream& operator=(sw_ostream&& other) {
if (did_construct_fstream()) {
if (other.did_construct_fstream()) {
static_cast(*os) = std::move(static_cast(*(other.os)));
} else {
destruct();
os = other.os;
}
} else {
if (other.did_construct_fstream()) {
os = new (f) std::fstream(std::move(static_cast(*other.os)));
} else {
os = other.os;
}
}
return *this;
}
sw_ostream& operator=(std::ostream& other) {
if (did_construct_fstream()) {
destruct();
}
os = &other;
return *this;
}
sw_ostream& operator=(std::fstream&& other) {
if (did_construct_fstream()) {
static_cast(*os) = std::move(other);
} else {
os = new (f) std::fstream(std::move(other));
}
return *this;
}
std::ostream& operator*() const noexcept {
return *os;
}
std::ostream* operator->() const noexcept {
return os;
}
operator std::ostream&() const noexcept {
return *os;
}
std::fstream* get_fstream() const noexcept {
if (did_construct_fstream()) return &static_cast(*os);
return dynamic_cast(os);
}
// `s << (...)` is a shorthand for `*s << (...)` (Where `s` is a `sw_ostream`)
template
const sw_ostream& operator<<(T&& o) const {
*os << std::forward(o);
return *this;
}
template
sw_ostream& operator<<(T&& o) {
*os << std::forward(o);
return *this;
}
~sw_ostream() {
if (did_construct_fstream()) {
destruct();
}
}
};
int main() {
sw_ostream s;
if (opening_file) {
s = std::fstream("filename");
} else {
s = std::cout;
}
if (std::fstream* fp = s.get_fstream()) {
assert(fp->is_open());
}
s << "Hello, world!\n";
s->flush();
}
I also came up with another solution that uses std::unique_ptr
so that you can use any derived class of std::ostream
, but that unnecessarily uses dynamic memory if you only want an existing std::ostream
(Like std::cout
) or a std::fstream
. See here.