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

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