serialization; more docu

master
Marc Wäckerlin 15 years ago
parent 9548b27052
commit 94f5fd4970
  1. 167
      src/xml-cxx/xml.hxx
  2. 86
      src/xml.cxx
  3. 3
      test/makefile.am
  4. 2
      test/xml_test.cxx

@ -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)

@ -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");

Loading…
Cancel
Save