From 080d202c75f0c0887d11daf5186ee51c1e82b6c3 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 10 Sep 2009 16:21:55 +0000 Subject: New iostream-style diamond inheritance pattern for FILE_Cookie: iFILE_Cookie, oFILE_Cookie, ioFILE_Cookie. --- libdimensionxx/cookie-fopencookie.cpp | 123 +++++++++++----------------------- libdimensionxx/cookie-tmpfile.cpp | 102 +++++++++------------------- libdimensionxx/dimensionxx/cookie.hpp | 70 ++++++++++++++----- libdimensionxx/png.cpp | 8 +-- 4 files changed, 128 insertions(+), 175 deletions(-) diff --git a/libdimensionxx/cookie-fopencookie.cpp b/libdimensionxx/cookie-fopencookie.cpp index f44ae7b..b44b5dd 100644 --- a/libdimensionxx/cookie-fopencookie.cpp +++ b/libdimensionxx/cookie-fopencookie.cpp @@ -34,20 +34,24 @@ namespace Dimension { + // FILE_Cookie pure virtual destructor: close the file + FILE_Cookie::~FILE_Cookie() { std::fclose(m_file); } + namespace { // Cookie read function ssize_t cookie_read(void* cookie, char* buf, size_t size) { - FILE_Cookie* streams = reinterpret_cast(cookie); + FILE_Cookie* fcookie = reinterpret_cast(cookie); + iFILE_Cookie& ifcookie = dynamic_cast(*fcookie); // Do the unformatted read - streams->istr().read(buf, size); + ifcookie.istr().read(buf, size); - if (streams->istr().eof() || streams->istr().good()) { - return streams->istr().gcount(); // This returns 0 on an immediate EOF - // for us. + if (ifcookie.istr().eof() || ifcookie.istr().good()) { + return ifcookie.istr().gcount(); // This returns 0 on an immediate EOF + // for us. } else { // Some non-EOF error return -1; @@ -58,12 +62,13 @@ namespace Dimension ssize_t cookie_write(void* cookie, const char* buf, size_t size) { - FILE_Cookie* streams = reinterpret_cast(cookie); + FILE_Cookie* fcookie = reinterpret_cast(cookie); + oFILE_Cookie& ofcookie = dynamic_cast(*fcookie); // Do the unformatted write - streams->ostr().write(buf, size); + ofcookie.ostr().write(buf, size); - if (streams->ostr().good()) { + if (ofcookie.ostr().good()) { // Write operation succeeded, so we must've written size bytes return size; } else { @@ -76,42 +81,44 @@ namespace Dimension int cookie_seek(void* cookie, off64_t* offset, int whence) { - FILE_Cookie* streams = reinterpret_cast(cookie); + FILE_Cookie* fcookie = reinterpret_cast(cookie); + iFILE_Cookie* ifcookie = dynamic_cast(fcookie); + oFILE_Cookie* ofcookie = dynamic_cast(fcookie); - if (streams->is_input()) { + if (ifcookie) { // If we have an input stream, seek it switch (whence) { case SEEK_SET: - streams->istr().seekg(*offset, std::ios::beg); + ifcookie->istr().seekg(*offset, std::ios::beg); break; case SEEK_CUR: - streams->istr().seekg(*offset, std::ios::cur); + ifcookie->istr().seekg(*offset, std::ios::cur); break; case SEEK_END: - streams->istr().seekg(*offset, std::ios::end); + ifcookie->istr().seekg(*offset, std::ios::end); break; } - if (!streams->istr().good()) { + if (!ifcookie->istr().good()) { // Seek failed return 1; } } - if (streams->is_output()) { + if (ofcookie) { // If we have an output stream, seek it switch (whence) { case SEEK_SET: - streams->ostr().seekp(*offset, std::ios::beg); + ofcookie->ostr().seekp(*offset, std::ios::beg); break; case SEEK_CUR: - streams->ostr().seekp(*offset, std::ios::cur); + ofcookie->ostr().seekp(*offset, std::ios::cur); break; case SEEK_END: - streams->ostr().seekp(*offset, std::ios::end); + ofcookie->ostr().seekp(*offset, std::ios::end); } - if (!streams->ostr().good()) { + if (!ofcookie->ostr().good()) { // Seek failed return 1; } @@ -123,8 +130,8 @@ namespace Dimension } // Make an input FILE_Cookie - FILE_Cookie::FILE_Cookie(std::istream& istr) - : m_istr(&istr), m_ostr(0) + iFILE_Cookie::iFILE_Cookie(std::istream& istr) + : m_istr(&istr) { cookie_io_functions_t io_funcs; io_funcs.read = &cookie_read; @@ -132,12 +139,13 @@ namespace Dimension io_funcs.seek = &cookie_seek; io_funcs.close = 0; - m_file = fopencookie(reinterpret_cast(this), "r", io_funcs); + // Set the FILE* + file(fopencookie(reinterpret_cast(this), "r", io_funcs)); } // Make an output FILE_Cookie - FILE_Cookie::FILE_Cookie(std::ostream& ostr) - : m_istr(0), m_ostr(&ostr) + oFILE_Cookie::oFILE_Cookie(std::ostream& ostr) + : m_ostr(&ostr) { cookie_io_functions_t io_funcs; io_funcs.read = 0; @@ -145,12 +153,16 @@ namespace Dimension io_funcs.seek = &cookie_seek; io_funcs.close = 0; - m_file = fopencookie(reinterpret_cast(this), "w", io_funcs); + // Set the FILE* + file(fopencookie(reinterpret_cast(this), "w", io_funcs)); } + // No-op oFILE_Cookie destructor + oFILE_Cookie::~oFILE_Cookie() { } + // Make an I/O FILE_Cookie - FILE_Cookie::FILE_Cookie(std::iostream& iostr) - : m_istr(&iostr), m_ostr(&iostr) + ioFILE_Cookie::ioFILE_Cookie(std::iostream& iostr) + : iFILE_Cookie(iostr, 0), oFILE_Cookie(iostr, 0) { cookie_io_functions_t io_funcs; io_funcs.read = &cookie_read; @@ -158,61 +170,6 @@ namespace Dimension io_funcs.seek = &cookie_seek; io_funcs.close = 0; - m_file = fopencookie(reinterpret_cast(this), "r+", io_funcs); - } - - // Close the file - FILE_Cookie::~FILE_Cookie() { std::fclose(m_file); } - - // Get the FILE* - FILE* FILE_Cookie::file() { return m_file; } - const FILE* FILE_Cookie::file() const { return m_file; } - - bool FILE_Cookie::is_input() const { return m_istr; } - bool FILE_Cookie::is_output() const { return m_ostr; } - - // Get the C++ streams - - std::istream& - FILE_Cookie::istr() - { - if (is_input()) { - return *m_istr; - } else { - throw Dimension_Error("Attempted to get input stream from non-input" - " FILE_Cookie."); - } - } - - const std::istream& - FILE_Cookie::istr() const - { - if (is_input()) { - return *m_istr; - } else { - throw Dimension_Error("Attempted to get input stream from non-input" - " FILE_Cookie."); - } - } - - std::ostream& - FILE_Cookie::ostr() - { - if (is_output()) { - return *m_ostr; - } else { - throw Dimension_Error("Attempted to get output stream from non-input" - " FILE_Cookie."); - } - } - - const std::ostream& FILE_Cookie::ostr() const - { - if (is_output()) { - return *m_ostr; - } else { - throw Dimension_Error("Attempted to get output stream from non-input" - " FILE_Cookie."); - } + file(fopencookie(reinterpret_cast(this), "r+", io_funcs)); } } diff --git a/libdimensionxx/cookie-tmpfile.cpp b/libdimensionxx/cookie-tmpfile.cpp index 7efefce..9e513e6 100644 --- a/libdimensionxx/cookie-tmpfile.cpp +++ b/libdimensionxx/cookie-tmpfile.cpp @@ -25,6 +25,9 @@ namespace Dimension { + // Close the tmpfile + FILE_Cookie::~FILE_Cookie() { std::fclose(m_file); } + namespace { // Write an input stream completely to a FILE*; this works poorly for @@ -91,99 +94,56 @@ namespace Dimension } // Make an input FILE_Cookie - FILE_Cookie::FILE_Cookie(std::istream& istr) - : m_file(std::tmpfile()), m_istr(&istr), m_ostr(0) + iFILE_Cookie::iFILE_Cookie(std::istream& istr) + : m_istr(&istr) { - if (!m_file) { + FILE* tmp = std::tmpfile(); + if (!tmp) { throw Dimension_Error("Error opening temporary file for C++/C I/O" " interface."); } // Write the input stream to the temporary file - write_cookie(m_file, *m_istr); - } + write_cookie(tmp, *m_istr); - // Make an output FILE_Cookie - FILE_Cookie::FILE_Cookie(std::ostream& ostr) - : m_file(std::tmpfile()), m_istr(0), m_ostr(&ostr) - { - if (!m_file) { - throw Dimension_Error("Error opening temporary file for C++/C I/O" - " interface."); - } + // Set the FILE* + file(tmp); } - // Make an I/O FILE_Cookie - FILE_Cookie::FILE_Cookie(std::iostream& iostr) - : m_file(std::tmpfile()), m_istr(&iostr), m_ostr(&iostr) + // Make an output FILE_Cookie + oFILE_Cookie::oFILE_Cookie(std::ostream& ostr) + : m_ostr(&ostr) { - if (!m_file) { + FILE* tmp = std::tmpfile(); + if (!tmp) { throw Dimension_Error("Error opening temporary file for C++/C I/O" " interface."); } - // Write the input stream to the temporary file - write_cookie(m_file, *m_istr); - } - - // Close the tmpfile, maybe syncing a C++ stream to it first - FILE_Cookie::~FILE_Cookie() { - if (is_output()) { - read_cookie(*m_ostr, m_file); - } - - std::fclose(m_file); + // Set the FILE* + file(tmp); } - // Get the FILE* - FILE* FILE_Cookie::file() { return m_file; } - const FILE* FILE_Cookie::file() const { return m_file; } - - bool FILE_Cookie::is_input() const { return m_istr; } - bool FILE_Cookie::is_output() const { return m_ostr; } - - // Get the C++ streams - - std::istream& - FILE_Cookie::istr() + // Write the temporary file to the output stream + oFILE_Cookie::~oFILE_Cookie() { - if (is_input()) { - return *m_istr; - } else { - throw Dimension_Error("Attempted to get input stream from non-input" - " FILE_Cookie."); - } + read_cookie(ostr(), file()); } - const std::istream& - FILE_Cookie::istr() const + // Make an I/O FILE_Cookie + ioFILE_Cookie::ioFILE_Cookie(std::iostream& iostr) + : iFILE_Cookie(iostr, 0), oFILE_Cookie(iostr, 0) { - if (is_input()) { - return *m_istr; - } else { - throw Dimension_Error("Attempted to get input stream from non-input" - " FILE_Cookie."); + FILE* tmp = std::tmpfile(); + if (!tmp) { + throw Dimension_Error("Error opening temporary file for C++/C I/O" + " interface."); } - } - std::ostream& - FILE_Cookie::ostr() - { - if (is_output()) { - return *m_ostr; - } else { - throw Dimension_Error("Attempted to get output stream from non-input" - " FILE_Cookie."); - } - } + // Write the input stream to the temporary file + write_cookie(tmp, istr()); - const std::ostream& FILE_Cookie::ostr() const - { - if (is_output()) { - return *m_ostr; - } else { - throw Dimension_Error("Attempted to get output stream from non-input" - " FILE_Cookie."); - } + // Set the FILE* + file(tmp); } } diff --git a/libdimensionxx/dimensionxx/cookie.hpp b/libdimensionxx/dimensionxx/cookie.hpp index 0b8beb9..d5b042e 100644 --- a/libdimensionxx/dimensionxx/cookie.hpp +++ b/libdimensionxx/dimensionxx/cookie.hpp @@ -29,38 +29,74 @@ namespace Dimension { - // Simple RAII class for FILE*'s which interface with a C++ stream. + // Simple RAII classes for FILE*'s which interface with a C++ stream. + class FILE_Cookie { public: - FILE_Cookie(std::istream& istr); - FILE_Cookie(std::ostream& ostr); - FILE_Cookie(std::iostream& iostr); - ~FILE_Cookie(); + // Destructor made pure virtual + virtual ~FILE_Cookie() = 0; // Get the magic FILE* - FILE* file(); - const FILE* file() const; + FILE* file() { return m_file; } + const FILE* file() const { return m_file; } - // Are we an input or output stream? - bool is_input() const; - bool is_output() const; + protected: + FILE_Cookie() { } - // Get the C++ streams - std::istream& istr(); - const std::istream& istr() const; - std::ostream& ostr(); - const std::ostream& ostr() const; + // Set the underlying FILE* + void file(FILE* file) { m_file = file; } private: std::FILE* m_file; - std::istream* m_istr; - std::ostream* m_ostr; // Copying prohibited FILE_Cookie(const FILE_Cookie& cookie); FILE_Cookie& operator=(const FILE_Cookie& cookie); }; + + class iFILE_Cookie : public virtual FILE_Cookie + { + public: + iFILE_Cookie(std::istream& istr); + // virtual ~iFILE_Cookie(); + + // Get the C++ streams + std::istream& istr() { return *m_istr; } + const std::istream& istr() const { return *m_istr; } + + protected: + // Just set the istream without initializing the FILE* + iFILE_Cookie(std::istream& istr, int) : m_istr(&istr) { } + + private: + std::istream* m_istr; + }; + + class oFILE_Cookie : public virtual FILE_Cookie + { + public: + oFILE_Cookie(std::ostream& ostr); + virtual ~oFILE_Cookie(); + + // Get the C++ streams + std::ostream& ostr() { return *m_ostr; } + const std::ostream& ostr() const { return *m_ostr; } + + protected: + // Just set the istream without initializing the FILE* + oFILE_Cookie(std::ostream& ostr, int) : m_ostr(&ostr) { } + + private: + std::ostream* m_ostr; + }; + + class ioFILE_Cookie : public iFILE_Cookie, public oFILE_Cookie + { + public: + ioFILE_Cookie(std::iostream& iostr); + // virtual ~ioFILE_Cookie(); + }; } #endif /* DIMENSIONXX_COOKIE_HPP */ diff --git a/libdimensionxx/png.cpp b/libdimensionxx/png.cpp index 8853c5b..92bfc15 100644 --- a/libdimensionxx/png.cpp +++ b/libdimensionxx/png.cpp @@ -55,7 +55,7 @@ namespace Dimension } // Make the C++/C I/O interface - FILE_Cookie cookie(*m_ostr); + oFILE_Cookie cookie(*m_ostr); // Write the PNG file if (dmnsn_png_write_canvas(m_canvas->dmnsn(), cookie.file()) != 0) { @@ -81,7 +81,7 @@ namespace Dimension Persister persister; // Make the C++/C I/O interface - FILE_Cookie* cookie = new FILE_Cookie(*m_ostr); + FILE_Cookie* cookie = new oFILE_Cookie(*m_ostr); persister.persist(cookie); // Start the asynchronous task @@ -110,7 +110,7 @@ namespace Dimension } // Make the C++/C I/O interface - FILE_Cookie cookie(*m_istr); + iFILE_Cookie cookie(*m_istr); // Read the canvas from a PNG file dmnsn_canvas* canvas = dmnsn_png_read_canvas(cookie.file()); @@ -146,7 +146,7 @@ namespace Dimension persister.persist(canvas); // Make the C++/C I/O interface - FILE_Cookie* cookie = new FILE_Cookie(*m_istr); + iFILE_Cookie* cookie = new iFILE_Cookie(*m_istr); persister.persist(cookie); // Start the asynchronous task -- cgit v1.2.3