serialization; more docu
This commit is contained in:
		| @@ -82,6 +82,147 @@ | ||||
|  | ||||
|     @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. | ||||
| /*! 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 { | ||||
|     public: | ||||
|       no_parent(const Node& t) throw(): exception("node has no parent", t) {} | ||||
| @@ -487,6 +635,18 @@ namespace xml { | ||||
|           throw(); | ||||
|       virtual String& append(const Node& o) throw(cannot_have_children); | ||||
|       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: | ||||
|       std::string _text; | ||||
|   }; | ||||
| @@ -508,7 +668,11 @@ namespace xml { | ||||
|   class Factory { | ||||
|     public: | ||||
|       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, | ||||
|                                       const Factory& factory) throw(); | ||||
|       static std::ostream& print(std::ostream& os, const Node& node, | ||||
| @@ -522,7 +686,6 @@ namespace xml { | ||||
|                 wrong_node_number); | ||||
|     private: | ||||
|       friend class stream_error; | ||||
|       Factory(); // not implemented | ||||
|       Factory(const Factory&); // not implemented | ||||
|       bool ws(char c) throw(); | ||||
|       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() { | ||||
|     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, | ||||
| @@ -707,8 +776,21 @@ namespace xml { | ||||
|     _template[0].min(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]; | ||||
|   } catch (...) { | ||||
|     throw factory_not_valid(); | ||||
|   } | ||||
|   Factory::operator bool() const throw() { | ||||
|     return _template.children()>0; | ||||
|   } | ||||
|   std::ostream& operator<<(std::ostream& os, const Factory& factory) throw() { | ||||
|     return factory.print(os, *factory); | ||||
| @@ -779,7 +861,7 @@ namespace xml { | ||||
|     _open=0; | ||||
|     std::auto_ptr<Node> node(read(is, _template)); | ||||
|     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(); | ||||
|   } catch (exception& e) { | ||||
|     e.line(_line); | ||||
|   | ||||
| @@ -7,10 +7,11 @@ AM_CXXFLAGS += -I ${top_srcdir}/src | ||||
| AM_LDFLAGS = -L${top_builddir}/src | ||||
| LDADD = -lcppunit -lxml-cxx | ||||
|  | ||||
| check_PROGRAMS=xml_test | ||||
| check_PROGRAMS = xml_test serialization_test | ||||
| TESTS=${check_PROGRAMS} | ||||
|  | ||||
| xml_test_SOURCES = xml_test.cxx | ||||
| serialization_test_SOURCES = serialization_test.cxx | ||||
|  | ||||
| CLEANFILES =  | ||||
| MAINTAINERCLEANFILES = makefile.in | ||||
|   | ||||
| @@ -123,7 +123,7 @@ class StringTest: public CppUnit::TestFixture { | ||||
|     } | ||||
|     void operatorBrackets() { | ||||
|       xml::String t("test"); | ||||
|       CPPUNIT_ASSERT_THROW(t["zxy"], xml::access_error); | ||||
|       CPPUNIT_ASSERT_THROW(t[std::string("zxy")], xml::access_error); | ||||
|     } | ||||
|     void text() { | ||||
|       xml::String t("test"); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user