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