-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathxml_pack_base.h
283 lines (238 loc) · 7.58 KB
/
xml_pack_base.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/*
@copyright Russell Standish 2000-2013
@author Russell Standish
This file is part of Classdesc
Open source licensed under the MIT license. See LICENSE for details.
*/
/**\file
\brief XML serialisation descriptor
*/
#ifndef CLASSDESC_XML_PACK_BASE_H
#define CLASSDESC_XML_PACK_BASE_H
#include <cmath>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <assert.h>
#include <stdarg.h>
#include <classdesc.h>
#include <classdesc_access.h>
#include <xml_common.h>
#include <stdexcept>
namespace classdesc
{
inline std::string xml_quote(char c)
{
switch (c)
{
case '&': return "&";
case '<': return "<";
case '>': return ">";
case '\'': return "'";
case '"': return """;
}
if (!isgraph(c))
{
std::ostringstream s;
s<<"&#"<<std::setfill('0')<<std::setw(4)<<int(c)<<";";
return s.str();
}
return std::string(1,c);
}
inline std::ostream& operator<<(std::ostream& o, const CDATA& x)
{return o<<"<![CDATA["<<static_cast<const std::string&>(x)<<"]]>";}
/**
XML serialisation object
*/
class xml_pack_t
{
std::ostream* o; // weak reference, allows for assignability
int taglevel;
// count number of ids separated by '.'s in a string
int level(const string& xx) {
const char* x=xx.c_str();
int l;
if (*x=='\0') return 0;
for (l=1; *x!='\0'; x++) if (*x=='.') l++;
return l;
}
void pretty(const string& d) {if (prettyPrint) *o << std::setw(level(d)) <<"";}
void endpretty() {if (prettyPrint) *o<<std::endl;}
/**
emit a tag \a d if current nesting level allows
return true if tag created
*/
bool tag(const string& d) {
int l=level(d);
bool ret = taglevel < l; //return true if tag created
if (ret)
{
pretty(d);
*o<<"<"<<tail(d);
if (l==1 && !schema.empty())
*o<<" xmlns=\""<<schema<<"\"";
*o<<">";
endpretty();
taglevel=l;
}
assert(taglevel==level(d));
return ret;
}
/** emit end tag */
void endtag(const string& d) {
taglevel--;
pretty(d);
*o<<"</"<<tail(d)<<">";
endpretty();
}
friend class Tag;
CLASSDESC_ACCESS(xml_pack_t);
public:
string schema;
bool prettyPrint; /// if true, the layout XML in more human friendly form
volatile bool abort; /// set to true to cancel packing from another thread
struct PackAborted: public std::exception {};
xml_pack_t(std::ostream& o, const string& schema=""):
o(&o), taglevel(0), schema(schema), prettyPrint(false), abort(false) {}
class Tag ///<utility structure for handling tag/endtag
{
xml_pack_t* t;
string d;
public:
Tag(xml_pack_t& t, const string& d): t(t.tag(d)? &t: 0), d(d) {}
~Tag() {if (t) t->endtag(d);}
};
// handle peculiar case sensitivity of floating point special values in XML
template <class T>
typename enable_if<is_floating_point<T>, std::ostream&>::T
put(std::ostream& o, T x)
{
if (std::isnan(x))
return o<<"NaN";
else if (std::isinf(x))
if (x<0)
return o<<"-INF";
else
return o<<"INF";
else
return o<<x;
}
template <class T>
typename enable_if<Not<is_floating_point<T> >, std::ostream&>::T
put(std::ostream& o, const T& x)
{
return o<<x;
}
/**
pack simple type
*/
template <class T>
void pack(const string& d, const T&x)
{
if (abort) throw PackAborted();
std::string tag=tail(d);
pretty(d);
*o << "<"<<tag<<">";
put(*o,x) << "</"<<tag<<">";
endpretty();
if (!*o) throw std::runtime_error("failed to serialise");
}
/**
pack an untagged simple type
*/
template <class T>
void pack_notag(const string& d, const T&x) {
if (abort) throw PackAborted();
*o<<x;
if (!*o) throw std::runtime_error("failed to serialise");
}
};
template <class T>
typename enable_if<is_fundamental<T>, void>::T
xml_packp(xml_pack_t& x,const string& d,T& a)
{x.pack(d,a);}
template <> inline void xml_packp(xml_pack_t& x,const string& d, bool& a)
{x.pack(d, a? "true": "false");}
template <> inline void xml_packp(xml_pack_t& x,const string& d, char& a)
{x.pack(d,classdesc::xml_quote(a));}
/**
handle enums
*/
template <class T>
typename enable_if<is_enum<T>, void>::T
xml_packp(xml_pack_t& x, const string& d, T& arg)
{x.pack(d,string(Enum_handle<T>(arg)));}
template <class T> void xml_pack(xml_pack_t&,const string&, const T&);
template <class T> void xml_pack(xml_pack_t&,const string&, T&);
template <class T> xml_pack_t& operator<<(xml_pack_t& t, const T& a);
inline void xml_pack(xml_pack_t& x,const string& d, std::string& a)
{
std::string tmp;
for (std::string::size_type i=0; i<a.length() && !x.abort; i++) tmp+=classdesc::xml_quote(a[i]);
x.pack(d,tmp);
}
inline void xml_pack(xml_pack_t& x,const string& d, const std::string& a)
{xml_pack(x,d,const_cast<std::string&>(a));}
/* now define the array version */
template <class T> void xml_pack(xml_pack_t& x,const string& d,is_array ia,
T& a, int dims,size_t ncopies,...)
{
va_list ap;
va_start(ap,ncopies);
for (int i=1; i<dims; i++) ncopies*=va_arg(ap,int); //assume that 2 and higher D arrays dimensions are int
va_end(ap);
xml_pack_t::Tag tag(x,d);
// element name is given by the type name
string eName=typeName<T>().c_str();
// strip leading namespace and qualifiers
const char *e=eName.c_str()+eName.length();
while (e!=eName.c_str() && *(e-1)!=' ' && *(e-1)!=':') e--;
for (size_t i=0; i<ncopies; i++) xml_pack(x,d+"."+e,(&a)[i]);
}
template <class T1, class T2>
void xml_pack(xml_pack_t& x, const string& d, const std::pair<T1,T2>& arg)
{
xml_pack_t::Tag t(x,d);
xml_pack(x,d+".first",arg.first);
xml_pack(x,d+".second",arg.second);
}
template <class T> typename
enable_if<Or<is_sequence<T>, is_associative_container<T> >, void>::T
xml_packp(xml_pack_t& x, const string& d, T& arg, dummy<1> dum=0)
{
xml_pack_t::Tag tag(x,d);
// element name is given by the type name
string eName=typeName<typename T::value_type>().c_str();
eName=eName.substr(0,eName.find('<')); //trim off any template args
// strip leading namespace and qualifiers
const char *e=eName.c_str()+eName.length();
while (e!=eName.c_str() && *(e-1)!=' ' && *(e-1)!=':') e--;
for (typename T::const_iterator i=arg.begin(); i!=arg.end(); ++i)
xml_pack(x,d+"."+e,*i);
}
template <class T>
void xml_pack_onbase(xml_pack_t& x,const string& d,T& a)
{xml_pack(x,d+basename<T>(),a);}
/* const static members */
template<class T>
void//typename enable_if<Not<is_pointer<T> >,void>::T
xml_pack(xml_pack_t& targ, const string& desc, is_const_static i, T arg)
{}
template<class T>
void xml_pack(xml_pack_t& targ, const string& desc, Exclude<T>&) {}
// special handling of shared pointers to avoid a double wrapping problem
template<class T>
void xml_pack(xml_pack_t& x, const string& d, shared_ptr<T>& a);
template<class T>
void xml_pack(xml_pack_t& targ, const string& desc, is_graphnode, T&)
{
throw exception("xml_pack of arbitrary graphs not supported");
}
}
#include "use_mbr_pointers.h"
CLASSDESC_USE_OLDSTYLE_MEMBER_OBJECTS(xml_pack)
CLASSDESC_FUNCTION_NOP(xml_pack)
using classdesc::xml_pack;
using classdesc::xml_pack_onbase;
#endif