some small changes to accommodate gcc4.0's stricter template checking
[dyninst.git] / dynutil / h / Serialization.h
1
2 #if !defined (SERIALIZATION_PUBLIC_H)
3 #define SERIALIZATION_PUBLIC_H
4 //  Hopefully just a few definitions allowing for a public interface to 
5 //  serializing user-providede annotations
6
7 #include <stdexcept>
8 #include <stdio.h>
9 #include "dynutil/h/dyntypes.h"
10 #include "common/h/Types.h"
11
12 //  SER_ERR("msg") -- an attempt at "graceful" failure.  If debug flag is set
13 //  it will assert, otherwise it throws...  leaving the "graceful" aspect
14 //  to the next (hopefully top-level) exception handler.
15
16
17 #define SER_ERR(cmsg) \
18    do { \
19       if (serializer_debug_flag()) { \
20          fprintf(stderr, "%s", cmsg); \
21          assert (0); \
22       } else { \
23          throw SerializerError(FILE__, __LINE__, std::string(cmsg)); \
24       } \
25    } while (0)
26
27
28 class SerializerBase;
29 typedef enum {sd_serialize, sd_deserialize} iomode_t;
30
31 class DLLEXPORT Serializable {
32    protected:
33       Serializable() {}
34       virtual ~Serializable() {}
35
36    public:
37       virtual void serialize(SerializerBase *,  const char * = NULL) = 0;
38 };
39
40 class SerializerError : public std::runtime_error {
41    //  SerializerError:  a small class that is thrown by serialization/deserialization
42    //  routines.  This exists as an attempt to standardize and simplify error handling
43    //  for ser-des routines that are possibly deeply nested.
44    //  Here's the rub:  we don't want stray, unhandled exceptions finding their way into
45    //  the larger system...  thus all entry points to serialization/deserialization need
46    //  to catch this exception to render it transparent to the rest of the system.
47
48    public:
49
50    typedef enum {
51       ser_err_unspecified,
52       ser_err_no_err,
53       ser_err_disabled
54    } SerializerErrorType;
55
56    private:
57
58    std::string file__;
59    int line__;
60    SerializerErrorType err__;
61
62    public:
63
64
65    SerializerError(const std::string &__file__, 
66          const int &__line__, 
67          const std::string &msg, 
68          SerializerErrorType __err__ = ser_err_unspecified) :
69       runtime_error(msg),
70       file__(__file__),
71       line__(__line__),
72       err__(__err__)
73    {}
74
75    virtual ~SerializerError() throw() {}
76
77    std::string file() const {return file__;}
78    int line() const {return line__;}
79    SerializerErrorType code() const {return err__;}
80 };
81
82
83 DLLEXPORT void printSerErr(const SerializerError &err);
84
85 template <class S, class T>
86 void translate_vector(S *ser, std::vector<T> &vec,
87       const char *tag = NULL, const char *elem_tag = NULL)
88 {
89    unsigned int nelem = vec.size();
90    ser->vector_start(nelem, tag);
91
92    if (ser->iomode() == sd_deserialize) 
93    {
94       if (vec.size())
95          SER_ERR("nonempty vector used to create");
96
97       //  zero size vectors are allowed
98       //  what it T is a complex type (with inheritance info)??
99       //  does resize() call default ctors, or should we do that
100       //  manually here? look this up.
101       if (nelem)
102          vec.resize(nelem);
103    }
104
105    for (unsigned int i = 0; i < vec.size(); ++i) 
106    {
107       T &t = vec[i];
108       gtranslate(ser, t, elem_tag);
109    }
110
111    ser->vector_end();
112 }
113
114
115 template <class S, class T>
116 void translate_vector(S *ser, std::vector<T *> &vec, 
117       const char *tag = NULL, const char *elem_tag = NULL) 
118 {
119    unsigned int nelem = vec.size();
120    ser->vector_start(nelem, tag);
121
122    if (ser->iomode() == sd_deserialize) 
123    {
124       if (vec.size()) 
125          SER_ERR("nonempty vector used to create");
126
127       //  zero size vectors are allowed
128       if (nelem) 
129       {
130          //  block-allocate array of underlying type, then assign to our vector
131          //  What happens if an individual elem is later deleted??
132          T *chunk_alloc = new T[nelem];
133          vec.resize(nelem);
134          for (unsigned int i = 0; i < nelem; ++i)
135             vec[i] = &(chunk_alloc[i]);
136       }
137    }
138
139    for (unsigned int i = 0; i < vec.size(); ++i) 
140    {
141       T &t = *(vec[i]);
142       gtranslate(ser, t, elem_tag);
143    }
144
145    ser->vector_end();
146 }
147
148 template <class S, class T>
149 void translate_vector(S *ser, std::vector<std::vector<T> > &vec, 
150       const char *tag = NULL, const char *elem_tag = NULL) 
151 {
152    fprintf(stderr, "%s[%d]:  welcome to translate vector of vectors\n", 
153          FILE__, __LINE__);
154
155    unsigned int nelem = vec.size();
156    ser->vector_start(nelem, tag);
157    if (ser->iomode() == sd_deserialize) 
158    {
159       if (vec.size())
160          SER_ERR("nonempty vector used to create");
161
162       //  zero size vectors are allowed
163       //  what it T is a complex type (with inheritance info)??
164       //  does resize() call default ctors, or should we do that
165       //  manually here? look this up.
166       if (nelem)
167          vec.resize(nelem);
168    }
169
170    for (unsigned int i = 0; i < vec.size(); ++i) 
171    {
172       std::vector<T> &tv = vec[i];
173       translate_vector(ser,tv, tag, elem_tag);
174    }
175
176    ser->vector_end();
177 }
178
179 template <class S, class K, class V>
180 void translate_hash_map(S *ser, dyn_hash_map<K, V> &hash, 
181       const char *tag = NULL, const char *key_tag = NULL, const char *value_tag = NULL)
182 {   
183    fprintf(stderr, "%s[%d]:  welcome to translate_hash_map<%s, %s>()\n", 
184          FILE__, __LINE__,
185          typeid(K).name(), typeid(V).name()); 
186
187    unsigned int nelem = hash.size();
188    ser->hash_map_start(nelem, tag);
189    fprintf(stderr, "%s[%d]:  after hash_map start, mode = %sserialize\n", 
190          FILE__, __LINE__, ser->iomode() == sd_serialize ? "" : "de"); 
191
192    if (ser->iomode() == sd_serialize) 
193    {
194       typename dyn_hash_map<K,V>::iterator iter = hash.begin();
195       fprintf(stderr, "%s[%d]:  about to serialize hash with %d elements\n", 
196             FILE__, __LINE__, hash.size());
197
198       while (iter != hash.end()) 
199       {
200          K k = iter->first;
201          V v = iter->second;
202          ser->translate_base(k, key_tag);
203          ser->translate_base(v, value_tag);
204          iter++;           
205       }
206    }
207    else 
208    {
209       //  can we do some kind of preallocation here?
210       for (unsigned int i = 0; i < nelem; ++i) 
211       {
212          K k;
213          V v;
214          ser->translate_base(k, key_tag);
215          ser->translate_base(v, value_tag);
216          hash[k] = v;
217       }
218    }
219
220    ser->hash_map_end();
221 }
222
223 template <class S, class K, class V>
224 void translate_hash_map(S *ser, dyn_hash_map<K, V *> &hash,
225       const char *tag = NULL, const char *key_tag = NULL, const char *value_tag = NULL)
226 {
227    fprintf(stderr, "%s[%d]:  welcome to translate_hash_map<%s, %s*>()\n", 
228          FILE__, __LINE__,
229          typeid(K).name(), typeid(V).name());
230
231    unsigned int nelem = hash.size();
232    ser->hash_map_start(nelem, tag);
233
234    fprintf(stderr, "%s[%d]:  after hash_map start, mode = %sserialize\n", 
235          FILE__, __LINE__, ser->iomode() == sd_serialize ? "" : "de");
236
237    if (ser->iomode() == sd_serialize) 
238    {
239       typename dyn_hash_map<K,V *>::iterator iter = hash.begin();
240
241       while (iter != hash.end()) 
242       {
243          K k = iter->first;
244          V *v = iter->second;
245          ser->translate_base(k, key_tag);
246          ser->translate_base(*v, value_tag);
247          iter++;
248       }
249    }
250    else 
251    {
252       //  can we do some kind of preallocation here?
253       for (unsigned int i = 0; i < nelem; ++i) 
254       {
255          K k;
256          V *v = new V();
257          ser->translate_base(k, key_tag);
258          ser->translate_base(*v, value_tag);
259          hash[k] = v;
260       }
261    }
262    ser->hash_map_end();
263 }
264
265 template <class S, class K, class V>
266 void translate_hash_map(S *ser, dyn_hash_map<K, char *> &hash,
267       const char *tag = NULL, const char *key_tag = NULL, const char *value_tag = NULL)
268 {
269    //  THIS SPECIALIZATION DOES NOT WORK CORRECTLY (YET)
270    fprintf(stderr, "%s[%d]:  welcome to translate_hash_map<%s, %s*>()\n", 
271          FILE__, __LINE__,
272          typeid(K).name(), typeid(V).name());
273
274    unsigned int nelem = hash.size();
275    ser->hash_map_start(nelem, tag);
276
277    fprintf(stderr, "%s[%d]:  after hash_map start, mode = %sserialize\n", 
278          FILE__, __LINE__, ser->iomode() == sd_serialize ? "" : "de");
279
280    if (ser->iomode() == sd_serialize) 
281    {
282       typename dyn_hash_map<K,V *>::iterator iter = hash.begin();
283       
284       while (iter != hash.end()) 
285       {
286          K k = iter->first;
287          V v = iter->second;
288          ser->translate_base(k, key_tag);
289          ser->translate_base(v, value_tag);
290          iter++;
291       }
292    }
293    else 
294    {
295       //  can we do some kind of preallocation here?
296       for (unsigned int i = 0; i < nelem; ++i) 
297       {
298          K k;
299          V v;
300          ser->translate_base(k, key_tag);
301          ser->translate_base(*v, value_tag);
302          hash[k] = v;
303       }
304    }
305    ser->hash_map_end();
306 }
307
308
309 DLLEXPORT void trans_adapt(SerializerBase *ser, Serializable &it,  const char *tag);
310 DLLEXPORT void trans_adapt(SerializerBase *ser, Serializable *itp,  const char *tag);
311
312 DLLEXPORT void trans_adapt(SerializerBase *ser, bool &it,  const char *tag);
313 DLLEXPORT void trans_adapt(SerializerBase *ser, int &it,  const char *tag);
314 DLLEXPORT void trans_adapt(SerializerBase *ser, unsigned int &it,  const char *tag);
315 DLLEXPORT void trans_adapt(SerializerBase *ser, long &it,  const char *tag);
316 DLLEXPORT void trans_adapt(SerializerBase *ser, unsigned long &it,  const char *tag);
317 DLLEXPORT void trans_adapt(SerializerBase *ser, char &it,  const char *tag);
318 DLLEXPORT void trans_adapt(SerializerBase *ser, char *&it,  const char *tag);
319 DLLEXPORT void trans_adapt(SerializerBase *ser, std::string &it,  const char *tag);
320 DLLEXPORT void trans_adapt(SerializerBase *ser, float &it,  const char *tag);
321 DLLEXPORT void trans_adapt(SerializerBase *ser, double &it,  const char *tag);
322
323 typedef void NOTYPE_T;
324 template<class S, class T, class T2 = NOTYPE_T>
325 class trans_adaptor {
326    public:
327       trans_adaptor() 
328       {
329          fprintf(stderr, "%s[%d]:  trans_adaptor  -- general\n", FILE__, __LINE__);
330       } 
331
332       T * operator()(S *ser, T & it, const char *tag = NULL, const char * /*tag2*/ = NULL)
333       {
334          trans_adapt(ser, it, tag);
335          return &it;
336       }
337 };
338
339 template<class S, class T2>
340 class trans_adaptor<S, Serializable, T2> {
341    public:
342       trans_adaptor() 
343       {
344          fprintf(stderr, "%s[%d]:  trans_adaptor  -- general\n", FILE__, __LINE__);
345       } 
346
347       Serializable * operator()(S *ser, Serializable & it, const char *tag = NULL, 
348             const char * /*tag2*/ = NULL)
349       {
350          gtranslate(ser, it, tag);
351          return &it;
352       }
353 };
354
355 template<class S, class T, class TT2>
356 class trans_adaptor<S, std::vector<T>, TT2 > {
357    public:
358       trans_adaptor()
359       {
360          fprintf(stderr, "%s[%d]:  trans_adaptor  -- vectorl\n", FILE__, __LINE__);
361       }
362
363       std::vector<T> * operator()(S *ser, std::vector<T> &v, const char *tag = NULL, 
364             const char *tag2 = NULL) 
365       {
366          translate_vector(ser, v, tag, tag2);         //  maybe catch errors here?
367          return &v;
368       }
369 };
370
371 template<class S, class T, class TT2>
372 class trans_adaptor<S, std::vector<T *>, TT2>  {
373    public: 
374       trans_adaptor() 
375       {
376          fprintf(stderr, "%s[%d]:  trans_adaptor  -- vector of ptrs\n", FILE__, __LINE__);
377       }
378
379       std::vector<T*> * operator()(S *ser, std::vector<T *> &v, const char *tag = NULL, 
380             const char *tag2 = NULL) 
381       {
382          translate_vector(ser, v, tag, tag2);
383          //  maybe catch errors here?
384          return &v;
385       }
386 };
387
388
389 template <class S, class TT>
390 void gtranslate(S *ser, TT&it, const char *tag = NULL, const char *tag2 = NULL)
391 {
392    fprintf(stderr, "%s[%d]:  welcome to gtranslate<%s, %s>(%p)\n",
393          FILE__, __LINE__,
394          "SerializerBase",
395          typeid(TT).name(), &it);
396
397    //  Maybe just need to do try/catch here since the template mapping may 
398    //  change the type of return value thru template specialization
399
400    trans_adaptor<S, TT> ta;
401    fprintf(stderr, "%s[%d]: gtranslate: before operation\n", FILE__, __LINE__);
402
403    TT *itp = ta(ser, it, tag, tag2);
404
405    if (!itp) 
406    {
407       fprintf(stderr, "%s[%d]: translate adaptor failed to de/serialize\n", 
408             FILE__, __LINE__);
409    }
410 }
411
412 DLLEXPORT bool ifxml_start_element(SerializerBase *sb, const char *tag);
413 DLLEXPORT bool ifxml_end_element(SerializerBase *sb, const char * /*tag*/);
414
415 class SerializerBin;
416 class SerializerXML;
417
418 bool sb_is_input(SerializerBase *sb);
419 bool sb_is_output(SerializerBase *sb);
420
421 template <class T>
422 bool ifinput(bool (*f)(SerializerBase *, T*), SerializerBase *sb, T *itp)
423 {
424    if (!sb_is_input(sb))
425       return false;
426
427
428    return (*f)(sb, itp);
429 }
430
431 template <class T>
432 bool ifoutput(bool (*f)(SerializerBase *, T*), SerializerBase *sb, T *itp)
433 {
434    if (!sb_is_output(sb))
435       return false;
436
437    return (*f)(sb, itp);
438 }
439
440 template <class T>
441 bool ifbin(bool (*f)(SerializerBase *, T*), SerializerBase *sb, T *itp)
442 {
443    SerializerBin *sbin = dynamic_cast<SerializerBin *>(sb);
444
445    if (!sbin)
446       return false;
447
448    return (*f)(sbin, itp);
449 }
450
451 template <class T>
452 bool ifxml(bool (*f)(SerializerBase *, T*), SerializerBase *sb, T *itp)
453 {
454    SerializerXML *sxml = dynamic_cast<SerializerXML *>(sb);
455
456    if (!sxml)
457       return false;
458
459    return (*f)(sxml, itp);
460 }
461
462 #if 0
463 template <class S, class TT>
464 void gtranslate(S *ser, TT&it, void (*use_func)(SerializerBase *, TT &, void *), const char *tag = NULL, const char *tag2)
465 {
466    fprintf(stderr, "%s[%d]:  welcome to gtranslate<%s, %s>(%p)\n",
467          FILE__, __LINE__,
468          "SerializerBase",
469          typeid(TT).name(), &it);
470
471    //  Maybe just need to do try/catch here since the template mapping may 
472    //  change the type of return value thru template specialization
473    assert(use_func);
474    (*use_func)(ser, it);
475 }
476
477 template<class S, class TT>
478 void trans_enum(S *ser, TT &it, std::vector<std::string> *enum_tags_ptr) 
479 {
480    assert(enum_tags_ptr);
481    std::vector<std::string> &enum_tags = *enum_tags_ptr;
482    assert(it < enum_tags.size());
483    unsigned int enum_int = (unsigned int) it;
484    gtranslate(ser, it);
485 }
486 #endif
487
488 DLLEXPORT bool isBinary(SerializerBase *ser);
489 DLLEXPORT bool isOutput(SerializerBase *ser);
490
491 template <class S, class TT>
492 void gtranslate(S *ser, 
493       TT &it, 
494       const char * (*to_str_func)(TT), 
495       const char *tag = NULL, 
496       const char * /*tag2*/ = NULL)
497 {
498    assert(ser);
499    int enum_int = (int) it;
500
501    if (!isBinary(ser)) 
502    {
503       assert(isOutput(ser));
504
505       // use human-readable tag
506       const char *enum_tag = (*to_str_func)(it);
507       std::string enum_tag_str(enum_tag);
508       assert(enum_tag);
509
510       gtranslate(ser, enum_tag_str, tag, NULL);
511    }
512    else 
513    {
514       //  just in/output raw binary value 
515       gtranslate(ser, enum_int, tag, NULL);
516       it = (TT) enum_int;
517    }
518 }
519
520 class SerializerError;
521
522 template <class S, class TT>
523 bool gtranslate_w_err(S *ser, TT&it, const char *tag = NULL, const char *tag2 = NULL)
524 {
525
526    try 
527    {
528       gtranslate(ser, it, tag, tag2);
529    }
530
531    catch (const SerializerError &err_) 
532    {
533       fprintf(stderr, "%s[%d]:  gtranslate failed\n", FILE__, __LINE__);
534       printSerErr(err_);
535       return false;
536    }
537    return true;
538 }
539
540 #endif