custom close func support for filestreams
This commit is contained in:
parent
de7225c8f6
commit
44d370183c
42
ostd/io.hh
42
ostd/io.hh
|
@ -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. */
|
||||||
|
|
22
src/io.cc
22
src/io.cc
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue