/////////////////////////////////////////////////////////////////////////////////////// /// \file archive.h /// \brief Classes to make (de)serializing to/from streams convenient /// /// $Date: 2014-09-09 10:49:13 +0200 (mar, 09 sep 2014) $ /// /////////////////////////////////////////////////////////////////////////////////////// #ifndef LPJ_GUESS_ARCHIVE_H #define LPJ_GUESS_ARCHIVE_H #include #include #include /// Abstract base class for ArchiveInStream and ArchiveOutStream /** The base class declares the transfer function, which will read * data from a stream in ArchiveInStream, and write data to a stream * in ArchiveOutStream. By having the same interface for both * cases, classes can have one function for both serializing and * deserializing. * * Sometimes we do need to know which direction the ArchiveStream * is working in though, so that can be queried with the save * function. */ class ArchiveStream { public: /// Checks if this ArchiveStream is saving data to stream or reading virtual bool save() const = 0; /// Write or read data to/from a stream /** \param s Data buffer to write out, or read to. * Needs to be n bytes. * \param n Number of bytes to read or write */ virtual void transfer(char* s, std::streamsize n) = 0; }; /// Class for reading data from an istream /** \see ArchiveStream for more documentation */ class ArchiveInStream : public ArchiveStream { public: ArchiveInStream(std::istream& strm); bool save() const; void transfer(char* s, std::streamsize n); private: /// The stream we're reading from std::istream& in; }; /// Class for writing data to an ostream /** \see ArchiveStream for more documentation */ class ArchiveOutStream : public ArchiveStream { public: ArchiveOutStream(std::ostream& strm); bool save() const; void transfer(char* s, std::streamsize n); private: /// The stream we're writing to std::ostream& out; }; /// Interface showing that a class can serialize itself /** Classes which can serialize themselves through an ArchiveStream * should inherit from this class, and implement the serialize function. */ class Serializable { public: /// Needs to be implemented by all sub-classes virtual void serialize(ArchiveStream& arch) = 0; }; /// Function for checking if something inherits from Serializable /** This variant will be chosen by the compiler for anything which * isn't Serializable (because of the overload below), and so always * returns false. * * Only intended to be used from the operator& implementation below, * which has the added benefit that if & is used for a class which * doesn't inherit from Serializable, many modern compilers will * warn because we're trying to send a non-POD type to a variadic * function. */ inline bool inheritsFromSerializable(...) { return false; } /// Overload for Serializable, always returns true inline bool inheritsFromSerializable(const Serializable& s) { return true; } /// Operator overloading of &, allowing serialization to be chained /** Since the operator returns the original stream, we can serialize * multiple items in the following way: * * \code * ArchiveOutStream arch(os); * * arch & width & height & name; * \endcode * * This function can be used for any item where the memory representation * of the item can be written to file bit by bit, such as the primitive * data types, or classes implementing Serializable. */ template ArchiveStream& operator&(ArchiveStream& stream, T& data) { if (inheritsFromSerializable(data)) { ((Serializable&)data).serialize(stream); } else { stream.transfer((char*)&data, sizeof(data)); } return stream; } template ArchiveStream& operator&(ArchiveStream& stream, std::vector& data) { if (stream.save()) { size_t size = data.size(); stream & size; } else { size_t size; stream & size; data.resize(size); } for (size_t i = 0; i < data.size(); ++i) { stream & data[i]; } return stream; } #endif // LPJ_GUESS_ARCHIVE_H