source: trunk/FACT++/src/Converter.cc@ 10319

Last change on this file since 10319 was 10293, checked in by tbretz, 14 years ago
Fixed the conversion for cases like 'C:5'
File size: 23.8 KB
Line 
1// **************************************************************************
2/** @class Converter
3
4@brief A compiler for the DIM data format string
5
6The Converter class interprets arguments in a string accoring to the
7given format definition and produces a corresponding memory block from it
8which can be attached to an event later.
9
10The format is given according to the Dim format description:
11
12 The format parameter specifies the contents of the structure in the
13 form T:N[;T:N]*[;T] where T is the item type: (I)nteger, (C)haracter,
14 (L)ong, (S)hort, (F)loat, (D)ouble, X(tra long==long long) and N is the
15 number of such items. The type alone at the end means all following items
16 are of the same type. Example: "I:3;F:2;C" means 3 Integers, 2 Floats and
17 Characters until the end. The format parameter is used for
18 communicating between different platforms.
19
20For example:
21
22\code
23 Converter c(cout, "I:1;F:2;I:2", );
24 vector<char> v = c.GetVector("COMMAND 1 2.5 4.2 3 4");
25\endcode
26
27would produce a 20 byte data block with the integers 1, the floats
282.5 and 4.2, and the intergers 3 and 4, in this order.
29
30The opposite direction is also possible
31
32\code
33 Converter c(cout, "I:1;F:2;I:2");
34 cout << c.GetString(pointer, size) << endl;
35 \endcode
36
37Other conversion functions also exist.
38
39To check if the compilation of the format string was successfull
40the valid() member functio is provided.
41
42The format parameter \b W(ord) is dedicated to this kind of conversion and
43not understood by Dim. In addition there are \b O(ptions) which are like
44Words but can be omitted. They should only be used at the end of the string.
45\b B(ool) is also special. It evaluates true/false, yes/no, on/off, 1/0.
46
47The non-DIM like format options can be switched on and off by using the
48strict argument in the constructor.
49
50@remark Note that all values are interpreted as signed, except the single
51char (e.g. C:5)
52
53*/
54// **************************************************************************
55#include "Converter.h"
56
57#include <iostream>
58#include <iomanip>
59#include <sstream>
60
61#include <cctype> // std::tolower
62#include <algorithm> // std::transform
63
64#include <boost/regex.hpp>
65
66#include "Readline.h"
67#include "WindowLog.h"
68
69using namespace std;
70/*
71In addition to converting the options from a string into binary format
72or back all found values are also put into a vector of boost::any objects.
73They can be accessed using e.g.
74
75\code
76Converter c(cout, "I:1;F:1;B:1;A:1;C", "112 5.5 on this is a test");
77
78 cout << c.Get<int>(0) << endl; // prints '112'
79 cout << c.Get<float>(1) << endl; // prints '5.5'
80 cout << c.Get<bool>(2) << endl; // prints '1'
81 cout << c.Get<string>(3) << endl; // prints 'this'
82 cout << c.Get<string>(4) << endl; // prints 'is a test'
83\endcode
84*/
85
86// --------------------------------------------------------------------------
87//
88//! This function is supposed to remove all whitespaces from the format
89//! string to allow easier regular expressions later.
90//!
91//! @param s
92//! string to be cleaned
93//!
94//! @returns
95//! string cleaned from whitespaces
96//
97std::string Converter::Clean(std::string s)
98{
99 while (1)
100 {
101 const size_t pos = s.find_last_of(' ');
102 if (pos==string::npos)
103 break;
104 s.erase(pos, pos+1);
105 }
106
107 return s;
108}
109
110// --------------------------------------------------------------------------
111//
112//! This is just a simplification. For the time being it is used to output
113//! the interpreted contents to the logging stream. Its main purpose
114//! is to add the contents of val in a binary representation to the
115//! vector v
116//!
117//! @tparam
118//! data type of the variable which should be added
119//!
120//! @param val
121//! reference to the data
122//!
123//! @param v
124//! vector<char> to which the binary copy should be added
125//!
126template <class T>
127void Converter::GetBinImp(std::vector<char> &v, const T &val) const
128{
129 wout << " (" << val << ")";
130
131 v.insert(v.end(),
132 reinterpret_cast<const char*>(&val),
133 reinterpret_cast<const char*>(&val+1));
134}
135
136// --------------------------------------------------------------------------
137//
138//! This is just a simplification. For the time being it is used to output
139//! the interpreted contents to the logging stream. Its main purpose
140//! is to add the contents of val as a boost::any object to the
141//! vector v
142//!
143//! @tparam
144//! data type of the variable which should be added
145//!
146//! @param val
147//! reference to the data
148//!
149//! @param v
150//! vector<boost::any> to which the value should be added
151//!
152template <class T>
153void Converter::GetBinImp(std::vector<boost::any> &v, const T &val) const
154{
155 wout << " (" << val << ")";
156
157 v.push_back(val);
158}
159
160// --------------------------------------------------------------------------
161//
162//! This is just a simplification. For the time being it is used to output
163//! the interpreted contents to the logging stream. Its main purpose
164//! is to add the contents of the provided string at the end of the vector v.
165//! vector v
166//!
167//! @param val
168//! reference to the string
169//!
170//! @param v
171//! vector<char> to which the value should be added
172//!
173void Converter::GetBinString(std::vector<char> &v, const string &val) const
174{
175 wout << " (" << val << ")";
176
177 v.insert(v.end(), val.begin(), val.end());
178}
179
180// --------------------------------------------------------------------------
181//
182//! This is just a simplification. For the time being it is used to output
183//! the interpreted contents to the logging stream. Its main purpose
184//! is to add the contents of the provided string at the end of the vector v.
185//! vector v
186//!
187//! @param val
188//! reference to the string
189//!
190//! @param v
191//! vector<boost::any> to which the value should be added
192//!
193void Converter::GetBinString(std::vector<boost::any> &v, const string &val) const
194{
195 wout << " (" << val << ")";
196
197 v.push_back(val);
198}
199
200// --------------------------------------------------------------------------
201//
202//! Converts from the stringstream into the provided type.
203//!
204//! @param line
205//! reference to the stringstream from which the data should be
206//! interpreted
207//!
208//! @tparam
209//! Type of the data to be returned
210//!
211//! @returns
212//! The interpreted data
213//!
214template <class T>
215T Converter::Get(std::stringstream &line) const
216{
217 T val;
218 line >> val;
219 return val;
220}
221
222// --------------------------------------------------------------------------
223//
224//! Converts from the stringstream into bool. It allows to use lexical
225//! boolean representations like yes/no, on/off, true/false and of
226//! course 0/1. If the conversion fails the failbit is set.
227//!
228//! @param line
229//! reference to the stringstream from which the data should be
230//! interpreted
231//!
232//! @returns
233//! The boolean. 0 in case of failure
234//!
235bool Converter::GetBool(std::stringstream &line) const
236{
237 string buf;
238 line >> buf;
239 transform(buf.begin(), buf.end(), buf.begin(), (int(*)(int)) std::tolower);
240
241 if (buf=="yes" || buf=="true" || buf=="on" || buf=="1")
242 return true;
243
244 if (buf=="no" || buf=="false" || buf=="off" || buf=="0")
245 return false;
246
247 line.clear(ios::failbit);
248
249 return false;
250}
251
252// --------------------------------------------------------------------------
253//
254//! Converts from the stringstream into a string. Leading whitespaces are
255//! skipped. Everything up to the next whitespace is returned.
256//! strings can be encapsulated into escape characters ("). Note, that
257//! they cannot be nested.
258//!
259//! @param line
260//! reference to the stringstream from which the data should be
261//! interpreted
262//!
263//! @returns
264//! The string
265//!
266string Converter::GetString(std::stringstream &line) const
267{
268 while (line.peek()==' ')
269 line.get();
270
271 string buf;
272 if (line.peek()=='\"')
273 {
274 line.get();
275 getline(line, buf, '\"');
276 }
277 else
278 line >> buf;
279
280 return buf;
281}
282
283// --------------------------------------------------------------------------
284//
285//! Converts from the stringstream into a string. Leading whitespaces are
286//! skipped. Everything until the end-of-line is returned. A trailing
287//! \0 is added.
288//!
289//! @param line
290//! reference to the stringstream from which the data should be
291//! interpreted
292//!
293//! @returns
294//! The string
295//!
296string Converter::GetStringEol(stringstream &line) const
297{
298 // Remove leading whitespaces
299 while (line.peek()==' ')
300 line.get();
301
302 line >> noskipws;
303
304 const istream_iterator<char> eol; // end-of-line iterator
305 const string s(istream_iterator<char>(line), eol);
306 return s + '\0';
307}
308
309// --------------------------------------------------------------------------
310//
311//! Converts from a binary block into a string. The type of the expected
312//! value is defined by the template parameter.
313//!
314//! @param ptr
315//! A refrenece to the pointer of the binary representation to be
316//! interpreted. The pointer is incremented by the sizeof the type.
317//!
318//! @tparam T
319//! Expected type
320//!
321//! @returns
322//! The string
323//!
324template<class T>
325string Converter::GetString(const char* &ptr) const
326{
327 const T &t = *reinterpret_cast<const T*>(ptr);
328
329 ostringstream stream;
330 stream << t;
331 ptr += sizeof(T);
332
333 return stream.str();
334}
335
336// --------------------------------------------------------------------------
337//
338//! Converts from a binary block into a hex representation.
339//!
340//! @param ptr
341//! Pointer to the data block
342//!
343//! @param size
344//! Size of the data block
345//!
346//! @param chunk
347//! Size of the size of hex chunks seperted by a ':' in bytes
348//!
349//! @returns
350//! The string
351//!
352string Converter::GetHex(const void *dat, size_t size, size_t chunk)
353{
354 const unsigned char *ptr = reinterpret_cast<const unsigned char *>(dat);
355
356 ostringstream text;
357
358 text << hex;
359
360 for (size_t i=0; i<size; i++)
361 {
362 text << setw(2) << setfill('0') << (unsigned int)ptr[i];
363 if ((i%chunk)==chunk-1)
364 text << ":";
365 }
366
367 return text.str();
368}
369
370
371// --------------------------------------------------------------------------
372//
373//! Convert the pointer using GetString into a string and add it (prefixed
374//! by a whaitespace) to the given string.
375//!
376//! @param str
377//! Reference to the string to which the ptr should be added
378//!
379//! @param ptr
380//! Pointer to the binary representation. It will be incremented
381//! according to the sze of the template argument
382//!
383//! @tparam T
384//! Type as which the binary data should be interpreted
385//!
386template<class T>
387void Converter::Add(string &str, const char* &ptr) const
388{
389 str += ' ' + GetString<T>(ptr);
390}
391
392// --------------------------------------------------------------------------
393//
394//! Convert the pointer into a boost::any object and add it to the
395//! provided vector
396//!
397//! @param vec
398//! Vector to which the boost::any object should be added
399//!
400//! @param ptr
401//! Pointer to the binary representation. It will be incremented
402//! according to the sze of the template argument
403//!
404//! @tparam T
405//! Type as which the binary data should be interpreted
406//!
407template<class T>
408void Converter::Add(vector<boost::any> &vec, const char* &ptr) const
409{
410 vec.push_back(*reinterpret_cast<const T*>(ptr));
411 ptr += sizeof(T);
412}
413
414// --------------------------------------------------------------------------
415//
416//! Add the string pointed to by ptr to the given string.
417//!
418//! @param str
419//! Reference to the string to which the ptr should be added
420//!
421//! @param ptr
422//! Pointer to the binary representation.
423//!
424void Converter::AddString(string &str, const string &ptr) const
425{
426 str += ' ' + ptr;
427}
428
429// --------------------------------------------------------------------------
430//
431//! Add the string pointed to by ptr as boost::any to the provided vector
432//!
433//! @param vec
434//! Vector to which the boost::any object should be added
435//!
436//! @param ptr
437//! Pointer to the binary representation of the string.
438//!
439void Converter::AddString(vector<boost::any> &vec, const string &ptr) const
440{
441 vec.push_back(ptr);
442}
443
444// --------------------------------------------------------------------------
445//
446//! Compiles the format string into fList. See Compile() for more details.
447//!
448//! @param out
449//! Output stream to which possible logging is redirected
450//!
451//! @param fmt
452//! Format to be compiled. For details see class reference
453//!
454//! @param strict
455//! Setting this to true allows non DIM options, whiel false
456//! will restrict the possible format strings to the ones also
457//! understood by DIM.
458//!
459Converter::Converter(std::ostream &out, const std::string &fmt, bool strict)
460: wout(out), fFormat(Clean(fmt)), fList(Compile(out, fmt, strict))
461{
462}
463
464// --------------------------------------------------------------------------
465//
466//! Compiles the format string into fList.
467//!
468//! Output by default is redirected to cout.
469//!
470//! @param fmt
471//! Format to be compiled. For details see class reference
472//!
473//! @param strict
474//! Setting this to true allows non DIM options, whiel false
475//! will restrict the possible format strings to the ones also
476//! understood by DIM.
477//!
478Converter::Converter(const std::string &fmt, bool strict)
479: wout(cout), fFormat(Clean(fmt)), fList(Compile(fmt, strict))
480{
481}
482
483// --------------------------------------------------------------------------
484//
485//! Converts the provided format string into a vector.
486//!
487//! @tparam T
488//! Kind of data to be returned. This can either be boost::any objects
489//! or a bnary data-block (char).
490//!
491//! @param fmt
492//! Format to be compiled. For details see class reference
493//!
494//! @returns
495//! A vector of the given template type containing the arguments. In
496//! case of failure an empty vector is returned.
497//!
498//! @throws
499//! std::runtime_error if the conversion was not successfull
500//!
501template <class T>
502vector<T> Converter::Get(const std::string &str) const
503{
504 if (!valid())
505 throw runtime_error("Compiled format invalid!");
506
507 // If the format is empty we are already done
508 if (empty() && str.empty())
509 {
510 wout << endl;
511 return vector<T>();
512 }
513
514 int arg = 0;
515 stringstream line(str);
516
517 vector<T> data;
518
519 for (Converter::FormatList::const_iterator i=fList.begin(); i<fList.end()-1; i++)
520 {
521 if (*i->first.first == typeid(string))
522 {
523 GetBinString(data, GetStringEol(line));
524 line.clear(ios::eofbit);
525 continue;
526 }
527
528 // Get as many items from the input line as requested
529 for (int j=0; j<i->second.first; j++)
530 {
531 switch (i->first.first->name()[0])
532 {
533 case 'b': GetBinImp(data, GetBool(line)); break;
534 case 's': GetBinImp(data, Get<short> (line)); break;
535 case 'i': GetBinImp(data, Get<int> (line)); break;
536 case 'l': GetBinImp(data, Get<long> (line)); break;
537 case 'f': GetBinImp(data, Get<float> (line)); break;
538 case 'd': GetBinImp(data, Get<double> (line)); break;
539 case 'x': GetBinImp(data, Get<long long>(line)); break;
540 case 'c':
541 if (line.peek()==-1)
542 {
543 line.clear(ios::failbit|ios::eofbit);
544 break;
545 }
546 GetBinImp(data, Get<unsigned char>(line));
547 if (line.peek()==-1)
548 line.clear(ios::eofbit);
549 break;
550 case 'N':
551 GetBinString(data, GetString(line));
552 if (*i->first.first == typeid(O))
553 line.clear(ios::goodbit);
554 break;
555 default:
556 // This should never happen!
557 throw runtime_error("Format '"+string(i->first.first->name())+" not supported!");
558 }
559
560 arg++;
561 }
562
563 if (!line)
564 break;
565 }
566
567 wout << endl;
568
569 // Something wrong with the conversion (e.g. 5.5 for an int)
570 if (line.fail() && !line.eof())
571 {
572 line.clear(); // This is necesasary to get a proper response from tellg()
573
574 ostringstream err;
575 err << "Error converting argument at " << arg << " [fmt=" << fFormat << "]!\n";
576 err << line.str() << "\n";
577 err << setw(int(line.tellg())) << " " << "^\n";
578 throw runtime_error(err.str());
579 }
580
581 // Not enough arguments, we have not reached the end
582 if (line.fail() && line.eof())
583 {
584 line.clear();
585
586 ostringstream err;
587 err << "Not enough arguments [fmt=" << fFormat << "]!\n";
588 err << line.str() << "\n";
589 err << setw(int(line.tellg())+1) << " " << "^\n";
590 throw runtime_error(err.str());
591 }
592
593 // Too many arguments, we have not reached the end
594 // Unfortunately, this can also mean that there is something
595 // wrong with the last argument
596 if (line.good() && !line.eof())
597 {
598 ostringstream err;
599 err << "More arguments available than expected [fmt=" << fFormat << "]!\n";
600 err << line.str() << "\n";
601 err << setw(int(line.tellg())+1) << " " << "^\n";
602 throw runtime_error(err.str());
603 }
604
605 return data;
606
607}
608
609std::vector<boost::any> Converter::GetAny(const std::string &str) const
610{
611 return Get<boost::any>(str);
612}
613
614std::vector<char> Converter::GetVector(const std::string &str) const
615{
616 return Get<char>(str);
617}
618
619// --------------------------------------------------------------------------
620//
621//! Converts the provided data block into a vector of boost::any or
622//! a string.
623//!
624//! @tparam T
625//! Kind of data to be returned. This can either be boost::any objects
626//! or a string
627//!
628//! @returns
629//! A vector of the given template type containing the arguments. In
630//! case of failure an empty vector is returned.
631//!
632//! @throws
633//! std::runtime_error if the conversion was not successfull
634//!
635template<class T>
636T Converter::Get(const void *dat, size_t size) const
637{
638 if (!valid())
639 throw runtime_error("Compiled format invalid!");
640
641 const char *ptr = reinterpret_cast<const char *>(dat);
642
643 T text;
644 for (Converter::FormatList::const_iterator i=fList.begin(); i<fList.end()-1; i++)
645 {
646 if (ptr-size>=dat)
647 {
648 ostringstream err;
649 err << "Format description [fmt=" << fFormat << "] exceeds available data size (" << size << ")";
650 throw runtime_error(err.str());
651 }
652
653 if (*i->first.first == typeid(string))
654 {
655 const string str(ptr);
656 AddString(text, str);
657 ptr += str.length()+1;
658 break;
659 }
660
661 // Get as many items from the input line as requested
662 for (int j=0; j<i->second.first; j++)
663 {
664 switch (i->first.first->name()[0])
665 {
666 case 'b': Add<bool> (text, ptr); break;
667 case 'c': Add<char> (text, ptr); break;
668 case 's': Add<short> (text, ptr); break;
669 case 'i': Add<int> (text, ptr); break;
670 case 'l': Add<long> (text, ptr); break;
671 case 'f': Add<float> (text, ptr); break;
672 case 'd': Add<double> (text, ptr); break;
673 case 'x': Add<long long>(text, ptr); break;
674 case 'v':
675 // This should never happen!
676 throw runtime_error("Type 'void' not supported!");
677 default:
678 throw runtime_error("TypeId '"+string(i->first.first->name())+"' not known!");
679 }
680 }
681 }
682
683 if (ptr-size!=dat)
684 {
685 ostringstream err;
686 err << "Data block size (" << size << ") doesn't fit format description [fmt=" << fFormat << "]";
687 throw runtime_error(err.str());
688 }
689
690 return text;
691}
692
693std::vector<boost::any> Converter::GetAny(const void *dat, size_t size) const
694{
695 return Get<vector<boost::any>>(dat, size);
696}
697
698std::vector<char> Converter::GetVector(const void *dat, size_t size) const
699{
700 const string ref = GetString(dat, size);
701
702 vector<char> data;
703 data.insert(data.begin(), ref.begin()+1, ref.end());
704 data.push_back(0);
705
706 return data;
707}
708
709string Converter::GetString(const void *dat, size_t size) const
710{
711 const string s = Get<string>(dat, size);
712 return s.empty() ? s : s.substr(1);
713}
714
715template<class T>
716Converter::Type Converter::GetType()
717{
718 Type t;
719 t.first = &typeid(T);
720 t.second = sizeof(T);
721 return t;
722}
723
724template<class T>
725Converter::Type Converter::GetVoid()
726{
727 Type t;
728 t.first = &typeid(T);
729 t.second = 0;
730 return t;
731}
732
733// --------------------------------------------------------------------------
734//
735//! static function to compile a format string.
736//!
737//! @param out
738//! Output stream to which possible logging is redirected
739//!
740//! @param fmt
741//! Format to be compiled. For details see class reference
742//!
743//! @param strict
744//! Setting this to true allows non DIM options, whiel false
745//! will restrict the possible format strings to the ones also
746//! understood by DIM.
747//!
748Converter::FormatList Converter::Compile(std::ostream &out, const std::string &fmt, bool strict)
749{
750 ostringstream text;
751
752 // Access both, the data and the format through a stringstream
753 stringstream stream(fmt);
754
755 // For better performance we could use sregex
756 static const boost::regex expr1("^([CSILFDXBOW])(:([1-9]+[0-9]*))?$");
757 static const boost::regex expr2("^([CSILFDX])(:([1-9]+[0-9]*))?$");
758
759 FormatList list;
760 Format format;
761
762 // Tokenize the format
763 string buffer;
764 while (getline(stream, buffer, ';'))
765 {
766 boost::smatch what;
767 if (!boost::regex_match(buffer, what, strict?expr2:expr1))
768 {
769 out << kRed << "Wrong format string '" << buffer << "'!" << endl;
770 return FormatList();
771 }
772
773 const string t = what[1]; // type id
774 const string n = what[3]; // counter
775
776 const int cnt = atoi(n.c_str());
777
778 // if the :N part was not given assume 1
779 format.second.first = cnt == 0 ? 1: cnt;
780
781 // Check if the format is just C (without a number)
782 // That would mean that it is a \0 terminated string
783 if (t[0]=='C' && cnt==0)
784 {
785 format.first = GetType<string>();
786 list.push_back(format);
787 format.second.second = 0; // end position not known
788 break;
789 }
790
791 // Get as many items from the input line as requested
792 switch (t[0])
793 {
794 case 'B': format.first = GetType<bool>(); break;
795 case 'C': format.first = GetType<char>(); break;
796 case 'S': format.first = GetType<short>(); break;
797 case 'I': format.first = GetType<int>(); break;
798 case 'L': format.first = GetType<long>(); break;
799 case 'F': format.first = GetType<float>(); break;
800 case 'D': format.first = GetType<double>(); break;
801 case 'X': format.first = GetType<long long>(); break;
802 case 'O': format.first = GetVoid<O>(); break;
803 case 'W': format.first = GetVoid<W>(); break;
804 default:
805 // This should never happen!
806 out << kRed << "Format '" << t[0] << " not known!" << endl;
807 return list;
808 }
809
810 list.push_back(format);
811 format.second.second += format.first.second * format.second.first;
812 }
813
814 format.first = GetVoid<void>();
815 format.second.first = 0;
816
817 list.push_back(format);
818
819 return list;
820}
821
822// --------------------------------------------------------------------------
823//
824//! Same as Compile(ostream&,string&,bool) but cout is used as the default
825//! output stream.
826//!
827//!
828Converter::FormatList Converter::Compile(const std::string &fmt, bool strict)
829{
830 return Compile(cout, fmt, strict);
831}
832
833vector<string> Converter::Regex(const string &expr, const string &line)
834{
835 const boost::regex reg(expr);
836
837 boost::smatch what;
838 if (!boost::regex_match(line, what, reg, boost::match_extra))
839 return vector<string>();
840
841 vector<string> ret;
842 for (unsigned int i=0; i<what.size(); i++)
843 ret.push_back(what[i]);
844
845 return ret;
846}
Note: See TracBrowser for help on using the repository browser.