custom close func support for filestreams

master
Daniel Kolesa 2017-05-08 01:07:32 +02:00
parent de7225c8f6
commit 44d370183c
2 changed files with 41 additions and 23 deletions

View File

@ -67,12 +67,15 @@ enum class stream_mode {
* used, they close the underlying stream on destruction (if still open). * used, they close the underlying stream on destruction (if still open).
*/ */
struct OSTD_EXPORT file_stream: stream { struct OSTD_EXPORT file_stream: stream {
/** @brief The callback type for closing foreign owned files */
using close_function = std::function<void(FILE *)>;
/** @brief Crates an empty file stream. /** @brief Crates an empty file stream.
* *
* The resulting file stream won't have an associated file. Any operations * The resulting file stream won't have an associated file. Any operations
* involving the potential associated file are considered unfedined. * 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; 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, * The other file stream is set to an empty state,
* i.e. it will not have any associated file set. * 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_f = nullptr;
s.p_owned = false;
} }
/** @brief Creates a file stream using a file path. /** @brief Creates a file stream using a file path.
@ -98,18 +100,21 @@ struct OSTD_EXPORT file_stream: stream {
* is a plain reading stream. * is a plain reading stream.
*/ */
file_stream(string_range path, stream_mode mode = stream_mode::READ): file_stream(string_range path, stream_mode mode = stream_mode::READ):
p_f() p_f(), p_closef()
{ {
open(path, mode); open(path, mode);
} }
/** @brief Creates a file stream using a C `FILE` pointer. /** @brief Creates a file stream using a C `FILE` pointer.
* *
* You can then manipulate the pointer using the stream, but it will * If `f` is non-empty, the stream will own the pointer and call the
* not be owned; you need to manually close it using the correct C * provided close function on close() or in the destructor, otherwise
* function when you're done. * 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. */ /** @brief Calls close() on the stream. */
~file_stream() { close(); } ~file_stream() { close(); }
@ -146,17 +151,16 @@ struct OSTD_EXPORT file_stream: stream {
* returns `false`. Otherwise, it will set the association and * returns `false`. Otherwise, it will set the association and
* returns `true`. * returns `true`.
* *
* In the end, is_open() will be true but is_owned() will be false. * The close function will be set when the file pointer is set
* You need to manually take care of the pointer because this stream * and that will also determine ownership of the pointer.
* will not close it.
*/ */
bool open(FILE *f); bool open(FILE *fptr, close_function f = close_function{});
/** @brief Checks if there is a resource associated with this stream. */ /** @brief Checks if there is a resource associated with this stream. */
bool is_open() const { return p_f != nullptr; } bool is_open() const { return p_f != nullptr; }
/** @brief Checks if we're owning the associated resource. */ /** @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. /** @brief Closes the associated file.
* *
@ -248,7 +252,7 @@ struct OSTD_EXPORT file_stream: stream {
void swap(file_stream &s) { void swap(file_stream &s) {
using std::swap; using std::swap;
swap(p_f, s.p_f); 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. /** @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; } 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: private:
FILE *p_f; FILE *p_f;
bool p_owned; close_function p_closef;
}; };
/** @brief Swaps two file streams including ownership. */ /** @brief Swaps two file streams including ownership. */

View File

@ -16,6 +16,10 @@ static char const *filemodes[] = {
"rb", "wb", "ab", "rb+", "wb+", "ab+" "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) { bool file_stream::open(string_range path, stream_mode mode) {
if (p_f || (path.size() > FILENAME_MAX)) { if (p_f || (path.size() > FILENAME_MAX)) {
return false; return false;
@ -23,7 +27,7 @@ bool file_stream::open(string_range path, stream_mode mode) {
char buf[FILENAME_MAX + 1]; char buf[FILENAME_MAX + 1];
std::memcpy(buf, &path[0], path.size()); std::memcpy(buf, &path[0], path.size());
buf[path.size()] = '\0'; buf[path.size()] = '\0';
p_owned = false; p_closef = nullptr;
#ifndef OSTD_PLATFORM_WIN32 #ifndef OSTD_PLATFORM_WIN32
p_f = std::fopen(buf, filemodes[std::size_t(mode)]); p_f = std::fopen(buf, filemodes[std::size_t(mode)]);
#else #else
@ -31,25 +35,27 @@ bool file_stream::open(string_range path, stream_mode mode) {
return false; return false;
} }
#endif #endif
p_owned = !!p_f; if (p_f) {
p_closef = default_close;
}
return is_open(); return is_open();
} }
bool file_stream::open(FILE *f) { bool file_stream::open(FILE *fptr, close_function f) {
if (p_f) { if (p_f) {
return false; return false;
} }
p_f = f; p_f = fptr;
p_owned = false; p_closef = f;
return is_open(); return is_open();
} }
void file_stream::close() { void file_stream::close() {
if (p_f && p_owned) { if (p_f && p_closef) {
std::fclose(p_f); p_closef(p_f);
} }
p_f = nullptr; p_f = nullptr;
p_owned = false; p_closef = nullptr;
} }
bool file_stream::end() const { bool file_stream::end() const {