From 44d370183cf3efa993925543b1c371cdd6c032ec Mon Sep 17 00:00:00 2001 From: q66 Date: Mon, 8 May 2017 01:07:32 +0200 Subject: [PATCH] custom close func support for filestreams --- ostd/io.hh | 42 +++++++++++++++++++++++++++--------------- src/io.cc | 22 ++++++++++++++-------- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/ostd/io.hh b/ostd/io.hh index 68c350b..ad9d1bd 100644 --- a/ostd/io.hh +++ b/ostd/io.hh @@ -67,12 +67,15 @@ enum class stream_mode { * used, they close the underlying stream on destruction (if still open). */ struct OSTD_EXPORT file_stream: stream { + /** @brief The callback type for closing foreign owned files */ + using close_function = std::function; + /** @brief Crates an empty file stream. * * The resulting file stream won't have an associated file. Any operations * involving the potential associated file are considered unfedined. */ - file_stream(): p_f(), p_owned(false) {} + file_stream(): p_f(), p_closef() {} file_stream(file_stream const &) = delete; @@ -81,9 +84,8 @@ struct OSTD_EXPORT file_stream: stream { * The other file stream is set to an empty state, * i.e. it will not have any associated file set. */ - file_stream(file_stream &&s): p_f(s.p_f), p_owned(s.p_owned) { + file_stream(file_stream &&s): p_f(s.p_f), p_closef(std::move(s.p_closef)) { s.p_f = nullptr; - s.p_owned = false; } /** @brief Creates a file stream using a file path. @@ -98,18 +100,21 @@ struct OSTD_EXPORT file_stream: stream { * is a plain reading stream. */ file_stream(string_range path, stream_mode mode = stream_mode::READ): - p_f() + p_f(), p_closef() { open(path, mode); } /** @brief Creates a file stream using a C `FILE` pointer. * - * You can then manipulate the pointer using the stream, but it will - * not be owned; you need to manually close it using the correct C - * function when you're done. + * If `f` is non-empty, the stream will own the pointer and call the + * provided close function on close() or in the destructor, otherwise + * the pointer is not owwned (is_owned() will be false) and will + * remain open. */ - file_stream(FILE *f): p_f(f), p_owned(false) {} + file_stream(FILE *fptr, close_function f = close_function{}): + p_f(fptr), p_closef(std::move(f)) + {} /** @brief Calls close() on the stream. */ ~file_stream() { close(); } @@ -146,17 +151,16 @@ struct OSTD_EXPORT file_stream: stream { * returns `false`. Otherwise, it will set the association and * returns `true`. * - * In the end, is_open() will be true but is_owned() will be false. - * You need to manually take care of the pointer because this stream - * will not close it. + * The close function will be set when the file pointer is set + * and that will also determine ownership of the pointer. */ - bool open(FILE *f); + bool open(FILE *fptr, close_function f = close_function{}); /** @brief Checks if there is a resource associated with this stream. */ bool is_open() const { return p_f != nullptr; } /** @brief Checks if we're owning the associated resource. */ - bool is_owned() const { return p_owned; } + bool is_owned() const { return !!p_closef; } /** @brief Closes the associated file. * @@ -248,7 +252,7 @@ struct OSTD_EXPORT file_stream: stream { void swap(file_stream &s) { using std::swap; swap(p_f, s.p_f); - swap(p_owned, s.p_owned); + swap(p_closef, s.p_closef); } /** @brief Gets an underlying C `FILE` pointer backing the stream. @@ -261,9 +265,17 @@ struct OSTD_EXPORT file_stream: stream { */ FILE *get_file() const { return p_f; } + /** @brief Gets the function used to close the file stream. + * + * If is_owned() is not true, this function will be empty. It will + * implicitly point to a function that calls the normal `fclose` + * when the stream is opened using a path. + */ + close_function get_close_function() const { return p_closef; } + private: FILE *p_f; - bool p_owned; + close_function p_closef; }; /** @brief Swaps two file streams including ownership. */ diff --git a/src/io.cc b/src/io.cc index 5d2f66c..258abdb 100644 --- a/src/io.cc +++ b/src/io.cc @@ -16,6 +16,10 @@ static char const *filemodes[] = { "rb", "wb", "ab", "rb+", "wb+", "ab+" }; +static void default_close(FILE *f) { + std::fclose(f); +} + bool file_stream::open(string_range path, stream_mode mode) { if (p_f || (path.size() > FILENAME_MAX)) { return false; @@ -23,7 +27,7 @@ bool file_stream::open(string_range path, stream_mode mode) { char buf[FILENAME_MAX + 1]; std::memcpy(buf, &path[0], path.size()); buf[path.size()] = '\0'; - p_owned = false; + p_closef = nullptr; #ifndef OSTD_PLATFORM_WIN32 p_f = std::fopen(buf, filemodes[std::size_t(mode)]); #else @@ -31,25 +35,27 @@ bool file_stream::open(string_range path, stream_mode mode) { return false; } #endif - p_owned = !!p_f; + if (p_f) { + p_closef = default_close; + } return is_open(); } -bool file_stream::open(FILE *f) { +bool file_stream::open(FILE *fptr, close_function f) { if (p_f) { return false; } - p_f = f; - p_owned = false; + p_f = fptr; + p_closef = f; return is_open(); } void file_stream::close() { - if (p_f && p_owned) { - std::fclose(p_f); + if (p_f && p_closef) { + p_closef(p_f); } p_f = nullptr; - p_owned = false; + p_closef = nullptr; } bool file_stream::end() const {