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