#ifndef PERSISTENT_HPP #define PERSISTENT_HPP /*------------------------------------------------------------------------------ Author: Andy Rushton Copyright: (c) Andy Rushton, 2004 License: BSD License, see ../docs/license.html Revision history:\n 13 Nov 2014: Matt Strait - Fixed many shadowed variable warnings ------------------------------------------------------------------------------*/ #include "os_fixes.hpp" #include "textio.hpp" #include "clonable.hpp" #include #include #include #include #include #include #include #include #include #include #include //////////////////////////////////////////////////////////////////////////////// // The format version number currently supported //////////////////////////////////////////////////////////////////////////////// extern unsigned char PersistentVersion; //////////////////////////////////////////////////////////////////////////////// // Exceptions thrown by the persistence functions // exception thrown if you try to dump or restore an illegal polymorphic type class persistent_illegal_type : public std::logic_error { public: persistent_illegal_type(const std::string& type) throw(); persistent_illegal_type(unsigned short key) throw(); ~persistent_illegal_type(void) throw(); }; // exception thrown if a dump fails for any reason - but typically because the output stream couldn't take the data class persistent_dump_failed : public std::runtime_error { public: persistent_dump_failed(const std::string& message) throw(); ~persistent_dump_failed(void) throw(); }; // exception thrown if you try to restore from an out of date or unrecognised byte stream class persistent_restore_failed : public std::runtime_error { public: persistent_restore_failed(const std::string& message) throw(); ~persistent_restore_failed(void) throw(); }; //////////////////////////////////////////////////////////////////////////////// // dump_context controls the formatting of a persistent dump //////////////////////////////////////////////////////////////////////////////// class dump_context_body; class dump_context { public: // types used in making polymorphous classes persistent using the callback approach // callback function for dumping the class typedef void (*dump_callback)(dump_context&,const void*); // data stored per class registered typedef std::pair callback_data; // type of callback function used to install all polymorphic classes for either approach typedef void (*installer)(dump_context&); ////////////////////////////////////////////////////////////////////////////// dump_context(const otext& device, unsigned char version = PersistentVersion) throw(persistent_dump_failed); ~dump_context(void); // low level output used to dump a byte void put(unsigned char data) throw(persistent_dump_failed); // access the device, for example to check the error status const otext& device(void) const; // recover the version number of the dumped output unsigned char version(void) const; // test whether the current platform uses little-endian or big-endian addressing of bytes // this is used in dump/restore of integers bool little_endian(void) const; // Assist functions for Pointers // the return pair is a flag saying whether this is a new pointer and the magic key to dump to file std::pair pointer_map(const void* const pointer); // Assist functions for Polymorphous classes (i.e. subclasses) using callback approach unsigned short register_type(const std::type_info& info, dump_callback); bool is_callback(const std::type_info& info) const; callback_data lookup_type(const std::type_info&) const throw(persistent_illegal_type); // Assist functions for Polymorphous classes (i.e. subclasses) using interface approach unsigned short register_interface(const std::type_info& info); bool is_interface(const std::type_info& info) const; unsigned short lookup_interface(const std::type_info&) const throw(persistent_illegal_type); // Register all Polymorphous classes using either approach by calling an installer callback void register_all(installer); private: friend class dump_context_body; dump_context_body* m_body; // disallow copying by making assignment and copy constructor private dump_context(const dump_context&); dump_context& operator=(const dump_context&); }; //////////////////////////////////////////////////////////////////////////////// // restore_context controls the reading of the persistent data during a restore class persistent; class restore_context_body; class restore_context { public: // types used in making polymorphous classes persistent using the callback approach // callback function for creating a new object of the class and returning the pointer typedef void* (*create_callback)(void); // callback for restoring the contents of a new object created by the create_callback // and passed as the second argument typedef void (*restore_callback)(restore_context&,void*); // data stored per class registered typedef std::pair callback_data; // types used in making polymorphous classes persistent using the interface approach friend class persistent; typedef std::pair interface_data; // type of callback function used to install all polymorphic classes for either approach typedef void (*installer)(restore_context&); ////////////////////////////////////////////////////////////////////////////// restore_context(const itext& device) throw(persistent_restore_failed); ~restore_context(void); // low level input used to restore a byte int get(void) throw(persistent_restore_failed); // access the device, for example to check the error status const itext& device(void) const; // access the version number of the input being restored unsigned char version(void) const; // test whether the current platform uses little-endian or big-endian addressing of bytes // this is used in dump/restore of integers bool little_endian(void) const; // Assist functions for Pointers std::pair pointer_map(unsigned magic); void pointer_add(unsigned magic, void* new_pointer); // Assist functions for Polymorphous classes using the callback approach unsigned short register_type(create_callback,restore_callback); bool is_callback(unsigned short) const; callback_data lookup_type(unsigned short) const throw(persistent_illegal_type); // Assist functions for Polymorphous classes using the interface approach // the object class must be a derivative of class persistent - i.e. it must implement the persistent interface unsigned short register_interface(const persistent&); bool is_interface(unsigned short) const; const persistent& lookup_interface(unsigned short) const throw(persistent_illegal_type); // Register all Polymorphous classes using either approach by calling an installer callback void register_all(installer); private: friend class restore_context_body; restore_context_body* m_body; // disallow copying by making assignment and copy constructor private restore_context(const restore_context&); restore_context& operator=(const restore_context&); }; //////////////////////////////////////////////////////////////////////////////// // Class defines an interface for use in making polymorphous classes persistent //////////////////////////////////////////////////////////////////////////////// // Note that it is derived from the clonable interface - so you must provide // that interface too class persistent : public clonable { public: virtual void dump(dump_context&) const throw(persistent_dump_failed) = 0; virtual void restore(restore_context&) throw(persistent_restore_failed) = 0; virtual ~persistent() {}; }; //////////////////////////////////////////////////////////////////////////////// // Integers void dump(dump_context&, const bool& data) throw(persistent_dump_failed); void restore(restore_context&, bool& data) throw(persistent_restore_failed); void dump(dump_context&, const char& data) throw(persistent_dump_failed); void restore(restore_context&, char& data) throw(persistent_restore_failed); void dump(dump_context&, const signed char& data) throw(persistent_dump_failed); void restore(restore_context&, signed char& data) throw(persistent_restore_failed); void dump(dump_context&, const unsigned char& data) throw(persistent_dump_failed); void restore(restore_context&, unsigned char& data) throw(persistent_restore_failed); void dump(dump_context&, const short& data) throw(persistent_dump_failed); void restore(restore_context&, short& data) throw(persistent_restore_failed); void dump(dump_context&, const unsigned short& data) throw(persistent_dump_failed); void restore(restore_context&, unsigned short& data) throw(persistent_restore_failed); void dump(dump_context&, const int& data) throw(persistent_dump_failed); void restore(restore_context&, int& data) throw(persistent_restore_failed); void dump(dump_context&, const unsigned& data) throw(persistent_dump_failed); void restore(restore_context&, unsigned& data) throw(persistent_restore_failed); void dump(dump_context&, const long& data) throw(persistent_dump_failed); void restore(restore_context&, long& data) throw(persistent_restore_failed); void dump(dump_context&, const unsigned long& data) throw(persistent_dump_failed); void restore(restore_context&, unsigned long& data) throw(persistent_restore_failed); //////////////////////////////////////////////////////////////////////////////// // Floating point types // // Note: despite years and years of IEEE standardisation, not all // architectures use IEEE-standard representations of floating-point numbers. // Therefore a binary dump is not necessarily portable between platforms. // Solving this is (currently) beyond the scope of the STLplus project. void dump(dump_context&, const float& data) throw(persistent_dump_failed); void restore(restore_context&, float& data) throw(persistent_restore_failed); void dump(dump_context&, const double& data) throw(persistent_dump_failed); void restore(restore_context&, double& data) throw(persistent_restore_failed); //////////////////////////////////////////////////////////////////////////////// // enumeration types template void dump_enum(dump_context&, const T& data) throw(persistent_dump_failed); template void restore_enum(restore_context&, T& data) throw(persistent_restore_failed); //////////////////////////////////////////////////////////////////////////////// // C-style char arrays. // These are handled differently to other pointer types as below // Warning! This means that pointers to char cannot be supported, since there // is no type difference between a pointer to char and a C-style array of char // Warning! The restore deletes any old value of the data parameter and // allocates a new char* which is (just) big enough and assigns it to the data // field. This is because there is no way of knowing how long a char* is so // the passed parameter is not safe to use. The allocation is done using // standard new. If the data field is non-null on entry it will be deleted by // standard delete. Best to make it null in the first place. void dump(dump_context&, char*& data) throw(persistent_dump_failed); void restore(restore_context&, char*& data) throw(persistent_restore_failed); //////////////////////////////////////////////////////////////////////////////// // STL strings template void dump_basic_string(dump_context&, const std::basic_string& data) throw(persistent_dump_failed); template void restore_basic_string(restore_context&, std::basic_string& data) throw(persistent_restore_failed); void dump(dump_context&, const std::string& data) throw(persistent_dump_failed); void restore(restore_context&, std::string& data) throw(persistent_restore_failed); //////////////////////////////////////////////////////////////////////////////// // Pointers // Supports null pointers too! // Warning! The pointer must be a dynamically-allocated type, since the implementation uses new/delete // If the data field to restore is null and the file format non-null, allocates a new T() // If the data field is non-null and the file format is null, deletes it and sets it null // Multiple pointers to the same object *will* be restored as multiple // pointers to the same object. The object is dumped only the first time it is // encountered along with a "magic key". Subsequent pointers to the same // object cause only the magic key to be dumped. On restore, the object is // only restored once and the magic keys are matched up so that the other // pointers now pojnt to the restored object. template void dump_pointer(dump_context&, const T* const data) throw(persistent_dump_failed); template void restore_pointer(restore_context&, T*& data) throw(persistent_restore_failed); //////////////////////////////////////////////////////////////////////////////// // Cross-references // A cross-reference is a pointer to an object that has definitely been dumped // already by one of dump_pointer, dump_interface or dump_polymorph, i.e. by // one of the dump routines for pointers to objects. // // These are typically used in data structures as back-pointers or pointers // between nodes. // // For example, you may have a tree with cross links. Dump the tree as the // primary data structure and then dump the cross links as cross-references. // These functions will throw an exception if the cross-reference points to // something not dumped before. template void dump_xref(dump_context&, const T* const data) throw(persistent_dump_failed); template void restore_xref(restore_context&, T*& data) throw(persistent_restore_failed); //////////////////////////////////////////////////////////////////////////////// // Polymorphous types using the callback approach // These are always dumped/restored as pointers to the superclass T. // Multiple pointers to the same object are handled in the same way as above // for simple pointers // Only classes registered with the context can be dumped and restored as // polymorphic types - see dump_context::register_type and restore_context::register_type. template void dump_polymorph(dump_context&, const T* const data) throw(persistent_dump_failed); template void restore_polymorph(restore_context&, T*& data) throw(persistent_restore_failed); //////////////////////////////////////////////////////////////////////////////// // Polymorphous types using the interface approach // These are always dumped/restored as pointers to the superclass T. // Multiple pointers to the same object are handled in the same way as above // for simple pointers // Only classes registered with the context can be dumped and restored as // polymorphic types - see dump_context::register_interface and restore_context::register_interface template void dump_interface(dump_context&, const T* const data) throw(persistent_dump_failed); template void restore_interface(restore_context&, T*& data) throw(persistent_restore_failed); //////////////////////////////////////////////////////////////////////////////// // STL Containers template void dump_bitset(dump_context&, const std::bitset& data) throw(persistent_dump_failed); template void restore_bitset(restore_context&, std::bitset& data) throw(persistent_restore_failed); template void dump_complex(dump_context&, const std::complex& data) throw(persistent_dump_failed); template void restore_complex(restore_context&, std::complex& data) throw(persistent_restore_failed); template void dump_deque(dump_context&, const std::deque& data) throw(persistent_dump_failed); template void restore_deque(restore_context&, std::deque& data) throw(persistent_restore_failed); template void dump_list(dump_context&, const std::list& data) throw(persistent_dump_failed); template void restore_list(restore_context&, std::list& data) throw(persistent_restore_failed); template void dump_pair(dump_context&, const std::pair& data) throw(persistent_dump_failed); template void restore_pair(restore_context&, std::pair& data) throw(persistent_restore_failed); template void dump_map(dump_context&, const std::map& data) throw(persistent_dump_failed); template void restore_map(restore_context&, std::map& data) throw(persistent_restore_failed); template void dump_multimap(dump_context&, const std::multimap& data) throw(persistent_dump_failed); template void restore_multimap(restore_context&, std::multimap& data) throw(persistent_restore_failed); template void dump_set(dump_context&, const std::set& data) throw(persistent_dump_failed); template void restore_set(restore_context&, std::set& data) throw(persistent_restore_failed); template void dump_multiset(dump_context&, const std::multiset& data) throw(persistent_dump_failed); template void restore_multiset(restore_context&, std::multiset& data) throw(persistent_restore_failed); template void dump_vector(dump_context&, const std::vector& data) throw(persistent_dump_failed); template void restore_vector(restore_context&, std::vector& data) throw(persistent_restore_failed); //////////////////////////////////////////////////////////////////////////////// // short-cut functions for dumping and restoring to common targets // // These functions use the installer callback function to install any // polymorphic type handlers required. If there are no polymorphic types used // in the data structure, then the callback can be set to null (i.e. 0). template void dump_to_device(const T& source, otext& result, dump_context::installer installer) throw(persistent_dump_failed); template void restore_from_device(itext& source, T& result, restore_context::installer installer) throw(persistent_restore_failed); template void dump_to_string(const T& source, std::string& result, dump_context::installer installer) throw(persistent_dump_failed); template void restore_from_string(const std::string& source, T& result, restore_context::installer installer) throw(persistent_restore_failed); template void dump_to_file(const T& source, const std::string& filename, dump_context::installer installer) throw(persistent_dump_failed); template void restore_from_file(const std::string& filename, T& result, restore_context::installer installer) throw(persistent_restore_failed); //////////////////////////////////////////////////////////////////////////////// #include "persistent.tpp" #endif