serialization; more docu
This commit is contained in:
		| @@ -82,6 +82,147 @@ | |||||||
|  |  | ||||||
|     @example doc/examples/address.cxx */ |     @example doc/examples/address.cxx */ | ||||||
|  |  | ||||||
|  | /*! @defgroup serialization Class Serialization | ||||||
|  |  | ||||||
|  |     @section serIntro Introduction | ||||||
|  |  | ||||||
|  |     Boost library (http://boost.org) offers a serialization framework, | ||||||
|  |     which is able to serialize even complex class structures, you only | ||||||
|  |     need to overwrite one or two serialization macros. The | ||||||
|  |     disadvantages are that a lot of macros are needed and it becomes | ||||||
|  |     quite complex as soon as you need inheritance. Also the generated | ||||||
|  |     XML is not very enhanced, especially for Lists and | ||||||
|  |     optional. Editing the boost serialization code by hand is a pain. | ||||||
|  |  | ||||||
|  |     Classes could also be serialized using gSOAP (http://gsoap.sf.net) | ||||||
|  |     which is designed for the SOA-Protocol. This serialization is much | ||||||
|  |     more flexible, but it requires a pseudo C++ declaration and a C++ | ||||||
|  |     parser/generator. Also it has very bad memory management, since it | ||||||
|  |     is plain C internally. | ||||||
|  |  | ||||||
|  |     Our requirements are: | ||||||
|  |       - No precompiler, plain C++. | ||||||
|  |       - Automatic memory management. | ||||||
|  |       - Nice looking XML code that is easy to edit manually. | ||||||
|  |       - Good error messages (exception) in case of bad XML files. | ||||||
|  |       - As few ugly overflow as possible. | ||||||
|  |  | ||||||
|  |     @section serActual Actual Status | ||||||
|  |  | ||||||
|  |     Instead of: | ||||||
|  |  | ||||||
|  |     @code | ||||||
|  |     class A { | ||||||
|  |       protected: | ||||||
|  |         int _anInteger; | ||||||
|  |         bool _aBool; | ||||||
|  |         double _aDouble; | ||||||
|  |         std::string _aString; | ||||||
|  |         std::string _anotherString; | ||||||
|  |         unsigned long _aLong; | ||||||
|  |     }; | ||||||
|  |     @endcode | ||||||
|  |  | ||||||
|  |     You have to write: | ||||||
|  |  | ||||||
|  |     @code | ||||||
|  |     class A { | ||||||
|  |       protected: | ||||||
|  |         XML_DECLARE_MEMBER(int, anInteger); | ||||||
|  |         XML_DECLARE_MEMBER(bool, aBool); | ||||||
|  |         XML_DECLARE_MEMBER(double, aDouble); | ||||||
|  |         XML_DECLARE_MEMBER(std::string, aString); | ||||||
|  |         XML_DECLARE_MEMBER(std::string, anotherString); | ||||||
|  |         XML_DECLARE_MEMBER(unsigned long, aLong); | ||||||
|  |       private: | ||||||
|  |         XML_INIT_BEGIN(A); | ||||||
|  |         XML_INIT_MEMBER(A, anInteger); | ||||||
|  |         XML_INIT_MEMBER(A, aBool); | ||||||
|  |         XML_INIT_MEMBER(A, aDouble); | ||||||
|  |         XML_INIT_MEMBER(A, aString); | ||||||
|  |         XML_INIT_MEMBER(A, anotherString); | ||||||
|  |         XML_INIT_MEMBER(A, aLong); | ||||||
|  |         XML_INIT_END; | ||||||
|  |     }; | ||||||
|  |     @endcode | ||||||
|  |  | ||||||
|  |     You get: | ||||||
|  |      - All the members (with @c _ as member prefix) | ||||||
|  |      - method <code>void saveXml(std::ostream&) const</code> | ||||||
|  |      - method <code>void loadXml(std::istream&)</code> | ||||||
|  |  | ||||||
|  |     @todo Up to now: Only base types plus std::string are supported, | ||||||
|  |           no list, no inheritance is possible and no optional members. */ | ||||||
|  | //@{ | ||||||
|  | #define XML_DECLARE_MEMBER(TYPE, MEMBER) \ | ||||||
|  |   void MEMBER##FromNode(xml::Node& node) { \ | ||||||
|  |     _##MEMBER = (TYPE)dynamic_cast<xml::String&>(node[#MEMBER]);        \ | ||||||
|  |   } \ | ||||||
|  |   void MEMBER##ToNode(xml::Node& node) const { \ | ||||||
|  |     std::stringstream ss; \ | ||||||
|  |     ss<<_##MEMBER; \ | ||||||
|  |     node[#MEMBER].text(ss.str());               \ | ||||||
|  |   } \ | ||||||
|  |   TYPE _##MEMBER | ||||||
|  |  | ||||||
|  | #define XML_INIT_BEGIN(CLASS_NAME) \ | ||||||
|  |     const CLASS_NAME& saveXml(std::ostream& os) const { \ | ||||||
|  |       const_cast<CLASS_NAME*>(this)->initXmlMembers(); \ | ||||||
|  |       xml::Node node(*_xmlFactory); \ | ||||||
|  |       for (std::vector<XmlSaveMethod>::const_iterator \ | ||||||
|  |              it(_saveXmlMemberList.begin()); \ | ||||||
|  |            it!=_saveXmlMemberList.end(); ++it) \ | ||||||
|  |         (this->*(*it))(node);                  \ | ||||||
|  |       os<<node; \ | ||||||
|  |     } \ | ||||||
|  |     CLASS_NAME& loadXml(std::istream& is) { \ | ||||||
|  |       initXmlMembers(); \ | ||||||
|  |       std::auto_ptr<xml::Node> node(_xmlFactory.read(is)); \ | ||||||
|  |       for (std::vector<XmlLoadMethod>::const_iterator \ | ||||||
|  |              it(_loadXmlMemberList.begin()); \ | ||||||
|  |            it!=_loadXmlMemberList.end(); ++it) \ | ||||||
|  |         (this->*(*it))(*node);                  \ | ||||||
|  |     } \ | ||||||
|  |   private: \ | ||||||
|  |     typedef void(CLASS_NAME::*XmlSaveMethod)(xml::Node&) const; \ | ||||||
|  |     typedef void(CLASS_NAME::*XmlLoadMethod)(xml::Node&); \ | ||||||
|  |     std::vector<XmlSaveMethod> _saveXmlMemberList; \ | ||||||
|  |     std::vector<XmlLoadMethod> _loadXmlMemberList; \ | ||||||
|  |     void initXmlMembers() { \ | ||||||
|  |       if (_xmlFactory) return; \ | ||||||
|  |       xml::Node schema(#CLASS_NAME) | ||||||
|  | #define XML_INIT_MEMBER(CLASS_NAME, MEMBER)        \ | ||||||
|  |     schema<<xml::String(#MEMBER).limits(1,1);             \ | ||||||
|  |     _saveXmlMemberList.push_back(&CLASS_NAME::MEMBER##ToNode);     \ | ||||||
|  |     _loadXmlMemberList.push_back(&CLASS_NAME::MEMBER##FromNode) | ||||||
|  | #define XML_INIT_END \ | ||||||
|  |     _xmlFactory = schema;\ | ||||||
|  |   } \ | ||||||
|  |   xml::Factory _xmlFactory | ||||||
|  | //@} | ||||||
|  |  | ||||||
|  | //! Deefine a string for a node name | ||||||
|  | /*! Put this macro in the global part of your header files.  After | ||||||
|  |     declaration of e.g. <code>XML_NODE(tagname)</code> you can use | ||||||
|  |     <code>xml::node::tagname</code> as constant std::string with | ||||||
|  |     contents @c "tagname" in your code. | ||||||
|  |  | ||||||
|  |     @code | ||||||
|  |     XML_NAME(base); | ||||||
|  |     XML_NAME(child); | ||||||
|  |  | ||||||
|  |     int main(int, char**) { | ||||||
|  |       xml::Factory test(xml::Node(xml::node::base) | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |     @endcode */ | ||||||
|  | #define XML_NODE(NAME) \ | ||||||
|  |   namespace xml {\ | ||||||
|  |     namespace node {\ | ||||||
|  |       static const std::string xml::Node NAME(#name);\ | ||||||
|  |     }\ | ||||||
|  |   } | ||||||
|  |  | ||||||
| //! Represents classes for handling C++ access to XML files with strict schema. | //! Represents classes for handling C++ access to XML files with strict schema. | ||||||
| /*! The schema ist not presented through xsd, but it can be declared in C++. | /*! The schema ist not presented through xsd, but it can be declared in C++. | ||||||
|  |  | ||||||
| @@ -181,6 +322,13 @@ namespace xml { | |||||||
|       } |       } | ||||||
|   }; |   }; | ||||||
|   //---------------------------------------------------------------------------- |   //---------------------------------------------------------------------------- | ||||||
|  |   class factory_not_valid: public exception { | ||||||
|  |     public: | ||||||
|  |       factory_not_valid() throw(): | ||||||
|  |           exception("a factory must be given a template node") { | ||||||
|  |       } | ||||||
|  |   }; | ||||||
|  |   //---------------------------------------------------------------------------- | ||||||
|   class no_parent: public exception { |   class no_parent: public exception { | ||||||
|     public: |     public: | ||||||
|       no_parent(const Node& t) throw(): exception("node has no parent", t) {} |       no_parent(const Node& t) throw(): exception("node has no parent", t) {} | ||||||
| @@ -487,6 +635,18 @@ namespace xml { | |||||||
|           throw(); |           throw(); | ||||||
|       virtual String& append(const Node& o) throw(cannot_have_children); |       virtual String& append(const Node& o) throw(cannot_have_children); | ||||||
|       Node& operator=(const std::string& contents) throw(); |       Node& operator=(const std::string& contents) throw(); | ||||||
|  |       operator std::string() const throw(); | ||||||
|  |       operator bool() const throw(); | ||||||
|  |       operator signed char() const throw(); | ||||||
|  |       operator unsigned char() const throw(); | ||||||
|  |       operator signed short() const throw(); | ||||||
|  |       operator unsigned short() const throw(); | ||||||
|  |       operator signed int() const throw(); | ||||||
|  |       operator unsigned int() const throw(); | ||||||
|  |       operator signed long() const throw(); | ||||||
|  |       operator unsigned long() const throw(); | ||||||
|  |       operator float() const throw(); | ||||||
|  |       operator double() const throw(); | ||||||
|     protected: |     protected: | ||||||
|       std::string _text; |       std::string _text; | ||||||
|   }; |   }; | ||||||
| @@ -508,7 +668,11 @@ namespace xml { | |||||||
|   class Factory { |   class Factory { | ||||||
|     public: |     public: | ||||||
|       Factory(const Node& t) throw(); |       Factory(const Node& t) throw(); | ||||||
|       const Node& operator*() const throw(); |       Factory() throw(); | ||||||
|  |       Factory& operator=(const Node& t) throw(); | ||||||
|  |       Factory& append(const Node& node) throw(); | ||||||
|  |       const Node& operator*() const throw(factory_not_valid); | ||||||
|  |       operator bool() const throw(); | ||||||
|       friend std::ostream& operator<<(std::ostream& os, |       friend std::ostream& operator<<(std::ostream& os, | ||||||
|                                       const Factory& factory) throw(); |                                       const Factory& factory) throw(); | ||||||
|       static std::ostream& print(std::ostream& os, const Node& node, |       static std::ostream& print(std::ostream& os, const Node& node, | ||||||
| @@ -522,7 +686,6 @@ namespace xml { | |||||||
|                 wrong_node_number); |                 wrong_node_number); | ||||||
|     private: |     private: | ||||||
|       friend class stream_error; |       friend class stream_error; | ||||||
|       Factory(); // not implemented |  | ||||||
|       Factory(const Factory&); // not implemented |       Factory(const Factory&); // not implemented | ||||||
|       bool ws(char c) throw(); |       bool ws(char c) throw(); | ||||||
|       std::auto_ptr<Node> read(std::istream& is, const Node& position) |       std::auto_ptr<Node> read(std::istream& is, const Node& position) | ||||||
|   | |||||||
							
								
								
									
										86
									
								
								src/xml.cxx
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								src/xml.cxx
									
									
									
									
									
								
							| @@ -612,6 +612,75 @@ namespace xml { | |||||||
|   Node& String::operator=(const std::string& contents) throw() { |   Node& String::operator=(const std::string& contents) throw() { | ||||||
|     return text(contents); |     return text(contents); | ||||||
|   } |   } | ||||||
|  |   String::operator std::string() const throw() { | ||||||
|  |     return text(); | ||||||
|  |   } | ||||||
|  |   String::operator bool() const throw() { | ||||||
|  |     bool res; | ||||||
|  |     std::stringstream ss(text()); | ||||||
|  |     ss>>res; | ||||||
|  |     return res; | ||||||
|  |   } | ||||||
|  |   String::operator signed char() const throw() { | ||||||
|  |     signed char res; | ||||||
|  |     std::stringstream ss(text()); | ||||||
|  |     ss>>res; | ||||||
|  |     return res; | ||||||
|  |   } | ||||||
|  |   String::operator unsigned char() const throw() { | ||||||
|  |     unsigned char res; | ||||||
|  |     std::stringstream ss(text()); | ||||||
|  |     ss>>res; | ||||||
|  |     return res; | ||||||
|  |   } | ||||||
|  |   String::operator signed short() const throw() { | ||||||
|  |     signed short res; | ||||||
|  |     std::stringstream ss(text()); | ||||||
|  |     ss>>res; | ||||||
|  |     return res; | ||||||
|  |   } | ||||||
|  |   String::operator unsigned short() const throw() { | ||||||
|  |     unsigned short res; | ||||||
|  |     std::stringstream ss(text()); | ||||||
|  |     ss>>res; | ||||||
|  |     return res; | ||||||
|  |   } | ||||||
|  |   String::operator signed int() const throw() { | ||||||
|  |     signed int res; | ||||||
|  |     std::stringstream ss(text()); | ||||||
|  |     ss>>res; | ||||||
|  |     return res; | ||||||
|  |   } | ||||||
|  |   String::operator unsigned int() const throw() { | ||||||
|  |     unsigned int res; | ||||||
|  |     std::stringstream ss(text()); | ||||||
|  |     ss>>res; | ||||||
|  |     return res; | ||||||
|  |   } | ||||||
|  |   String::operator signed long() const throw() { | ||||||
|  |     signed long res; | ||||||
|  |     std::stringstream ss(text()); | ||||||
|  |     ss>>res; | ||||||
|  |     return res; | ||||||
|  |   } | ||||||
|  |   String::operator unsigned long() const throw() { | ||||||
|  |     unsigned long res; | ||||||
|  |     std::stringstream ss(text()); | ||||||
|  |     ss>>res; | ||||||
|  |     return res; | ||||||
|  |   } | ||||||
|  |   String::operator float() const throw() { | ||||||
|  |     float res; | ||||||
|  |     std::stringstream ss(text()); | ||||||
|  |     ss>>res; | ||||||
|  |     return res; | ||||||
|  |   } | ||||||
|  |   String::operator double() const throw() { | ||||||
|  |     double res; | ||||||
|  |     std::stringstream ss(text()); | ||||||
|  |     ss>>res; | ||||||
|  |     return res; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   //---------------------------------------------------------------------------- |   //---------------------------------------------------------------------------- | ||||||
|   /*! @copydoc Node::Node(std::string name, |   /*! @copydoc Node::Node(std::string name, | ||||||
| @@ -707,8 +776,21 @@ namespace xml { | |||||||
|     _template[0].min(1); |     _template[0].min(1); | ||||||
|     _template[0].max(1); |     _template[0].max(1); | ||||||
|   } |   } | ||||||
|   const Node& Factory::operator*() const throw() { |   Factory::Factory() throw(): | ||||||
|  |       _template(xml::Node("<xml::start>")), _line(0) { | ||||||
|  |   } | ||||||
|  |   Factory& Factory::operator=(const Node& t) throw() { | ||||||
|  |     _template = xml::Node("<xml::start>")<<t; | ||||||
|  |     _template[0].min(1); | ||||||
|  |     _template[0].max(1); | ||||||
|  |   } | ||||||
|  |   const Node& Factory::operator*() const throw(factory_not_valid) try { | ||||||
|     return _template[0]; |     return _template[0]; | ||||||
|  |   } catch (...) { | ||||||
|  |     throw factory_not_valid(); | ||||||
|  |   } | ||||||
|  |   Factory::operator bool() const throw() { | ||||||
|  |     return _template.children()>0; | ||||||
|   } |   } | ||||||
|   std::ostream& operator<<(std::ostream& os, const Factory& factory) throw() { |   std::ostream& operator<<(std::ostream& os, const Factory& factory) throw() { | ||||||
|     return factory.print(os, *factory); |     return factory.print(os, *factory); | ||||||
| @@ -779,7 +861,7 @@ namespace xml { | |||||||
|     _open=0; |     _open=0; | ||||||
|     std::auto_ptr<Node> node(read(is, _template)); |     std::auto_ptr<Node> node(read(is, _template)); | ||||||
|     if (node->children()==0) |     if (node->children()==0) | ||||||
|       throw tag_expected(_template[0], _template[0].name()); |       throw tag_expected(_template[0], "nothing found, possibly empty stream"); | ||||||
|     return (*node)[0].clone(); |     return (*node)[0].clone(); | ||||||
|   } catch (exception& e) { |   } catch (exception& e) { | ||||||
|     e.line(_line); |     e.line(_line); | ||||||
|   | |||||||
| @@ -7,10 +7,11 @@ AM_CXXFLAGS += -I ${top_srcdir}/src | |||||||
| AM_LDFLAGS = -L${top_builddir}/src | AM_LDFLAGS = -L${top_builddir}/src | ||||||
| LDADD = -lcppunit -lxml-cxx | LDADD = -lcppunit -lxml-cxx | ||||||
|  |  | ||||||
| check_PROGRAMS=xml_test | check_PROGRAMS = xml_test serialization_test | ||||||
| TESTS=${check_PROGRAMS} | TESTS=${check_PROGRAMS} | ||||||
|  |  | ||||||
| xml_test_SOURCES = xml_test.cxx | xml_test_SOURCES = xml_test.cxx | ||||||
|  | serialization_test_SOURCES = serialization_test.cxx | ||||||
|  |  | ||||||
| CLEANFILES =  | CLEANFILES =  | ||||||
| MAINTAINERCLEANFILES = makefile.in | MAINTAINERCLEANFILES = makefile.in | ||||||
|   | |||||||
| @@ -123,7 +123,7 @@ class StringTest: public CppUnit::TestFixture { | |||||||
|     } |     } | ||||||
|     void operatorBrackets() { |     void operatorBrackets() { | ||||||
|       xml::String t("test"); |       xml::String t("test"); | ||||||
|       CPPUNIT_ASSERT_THROW(t["zxy"], xml::access_error); |       CPPUNIT_ASSERT_THROW(t[std::string("zxy")], xml::access_error); | ||||||
|     } |     } | ||||||
|     void text() { |     void text() { | ||||||
|       xml::String t("test"); |       xml::String t("test"); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user