diff --git a/include/cereal/archives/json.hpp b/include/cereal/archives/json.hpp index f86bcd40..75cec197 100644 --- a/include/cereal/archives/json.hpp +++ b/include/cereal/archives/json.hpp @@ -169,15 +169,36 @@ namespace cereal itsNodeStack.push(NodeType::StartObject); } - //! Destructor, flushes the JSON - ~JSONOutputArchive() CEREAL_NOEXCEPT + //! Explicitly finishes the JSON output and flushes to the stream + /*! This method can be used to detect and handle errors that occur during + finalization of the JSON output, similar to std::fstream::close(). + If not called explicitly, the destructor will attempt the same + finalization but will silently swallow any exceptions. + + It is safe to call close() multiple times; subsequent calls are no-ops. + + @throws RapidJSONException if rapidjson encounters an error during finalization */ + void close() { + if( itsClosed ) + return; + itsClosed = true; + if (itsNodeStack.top() == NodeType::InObject) itsWriter.EndObject(); else if (itsNodeStack.top() == NodeType::InArray) itsWriter.EndArray(); } + //! Destructor, flushes the JSON + /*! Silently catches any exceptions to guarantee noexcept. + Call close() explicitly before destruction to detect errors. */ + ~JSONOutputArchive() CEREAL_NOEXCEPT + { + try { close(); } + catch(...) {} + } + //! Saves some binary data, encoded as a base64 string, with an optional name /*! This will create a new node, optionally named, and insert a value that consists of the data encoded as a base64 string */ @@ -385,6 +406,7 @@ namespace cereal char const * itsNextName; //!< The next name std::stack itsNameCounter; //!< Counter for creating unique names for unnamed nodes std::stack itsNodeStack; + bool itsClosed = false; //!< Whether close() has been called }; // JSONOutputArchive // ###################################################################### diff --git a/include/cereal/archives/xml.hpp b/include/cereal/archives/xml.hpp index a86ab466..b75e4864 100644 --- a/include/cereal/archives/xml.hpp +++ b/include/cereal/archives/xml.hpp @@ -185,14 +185,36 @@ namespace cereal itsOS.precision( options.itsPrecision ); } - //! Destructor, flushes the XML - ~XMLOutputArchive() CEREAL_NOEXCEPT + //! Explicitly finishes the XML output and flushes to the stream + /*! This method can be used to detect and handle errors that occur during + finalization of the XML output (e.g., stream I/O failures), similar + to std::fstream::close(). + If not called explicitly, the destructor will attempt the same + finalization but will silently swallow any exceptions. + + It is safe to call close() multiple times; subsequent calls are no-ops. + + @throws std::exception if the stream operations fail */ + void close() { + if( itsClosed ) + return; + itsClosed = true; + const int flags = itsIndent ? 0x0 : rapidxml::print_no_indenting; rapidxml::print( itsStream, itsXML, flags ); itsXML.clear(); } + //! Destructor, flushes the XML + /*! Silently catches any exceptions to guarantee noexcept. + Call close() explicitly before destruction to detect errors. */ + ~XMLOutputArchive() CEREAL_NOEXCEPT + { + try { close(); } + catch(...) {} + } + //! Saves some binary data, encoded as a base64 string, with an optional name /*! This can be called directly by users and it will automatically create a child node for the current XML node, populate it with a base64 encoded string, and optionally name @@ -361,6 +383,7 @@ namespace cereal bool itsOutputType; //!< Controls whether type information is printed bool itsIndent; //!< Controls whether indenting is used bool itsSizeAttributes; //!< Controls whether lists have a size attribute + bool itsClosed = false; //!< Whether close() has been called }; // XMLOutputArchive // ######################################################################