/*========================================================================= medInria Copyright (c) INRIA 2013 - 2018. All rights reserved. See LICENSE.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =========================================================================*/ #pragma once #include #include #include #include // Various stream manipulators to handle various kinds of token // in the input stream (comments, strings, white spaces, // optional elements...). namespace io_utils { // Copy the imanip object from the old C++ library since it is apparently // not in the standard. Remove it for a more standard solution ? (TODO). template class imanip; template inline std::istream& operator>>(std::istream& i,const imanip& m) { return (*m.func)(i,m.obj); } template class imanip { std::istream& (*func)(std::istream&, TP); TP obj; public: imanip(std::istream& (*f)(std::istream&, TP), TP a): func(f), obj(a) {} friend std::istream& operator>> <>(std::istream&,const imanip&); }; template class imanip2; template inline std::istream& operator>>(std::istream& i,const imanip2& m) { return (*m.func)(i,m.obj1,m.obj2); } template class imanip2 { std::istream& (*func)(std::istream&,T1,T2); T1 obj1; T2 obj2; public: imanip2(std::istream& (*f)(std::istream&,T1,T2),T1 a,T2 b): func(f), obj1(a),obj2(b) {} friend std::istream& operator>> <>(std::istream&,const imanip2&); }; template class imanip3; template inline std::istream& operator>>(std::istream& i,const imanip3& m) { return (*m.func)(i,m.obj1,m.obj2,m.obj3); } template class imanip3 { std::istream& (*func)(std::istream&,T1,T2,T3); T1 obj1; T2 obj2; T3 obj3; public: imanip3(std::istream& (*f)(std::istream&,T1,T2,T3),T1 a,T2 b,T3 c): func(f), obj1(a),obj2(b),obj3(c) {} friend std::istream& operator>> <>(std::istream&,const imanip3&); }; template class omanip; template inline std::ostream& operator<<(std::ostream& i,const omanip& m) { return (*m.func)(i,m.obj); } template class omanip { std::ostream& (*func)(std::ostream&,TP); TP obj; public: omanip(std::ostream& (*f)(std::ostream&,TP),TP a): func(f), obj(a) {} friend std::ostream& operator<< <>(std::ostream&,const omanip&); }; template class omanip2; template inline std::ostream& operator<<(std::ostream& i,const omanip2& m) { return (*m.func)(i,m.obj1,m.obj2); } template class omanip2 { std::ostream& (*func)(std::ostream&,T1,T2); T1 obj1; T2 obj2; public: omanip2(std::ostream& (*f)(std::ostream&,T1,T2),T1 a,T2 b): func(f), obj1(a),obj2(b) {} friend std::ostream& operator<< <>(std::ostream& i,const omanip2& m); }; // Ignoring the istream till the end of the line. inline std::istream& skip_line(std::istream& is) { return is.ignore(std::numeric_limits::max(),'\n'); } // Eat pure white spaces (' ' and '\t') inline std::istream& skip_spaces(std::istream& is) { char separator; while (is.get(separator) && ((separator==' ') || (separator=='\t'))); return is.putback(separator); } // Eat up comments starting with the given character. // Example: To remove the lines begining with a '#' then read data. // cin >> skip_comments('#') >> data; inline std::istream& skip_lines_internal(std::istream& is,const unsigned char c) { is >> std::ws; while (is.peek()==c) { skip_line(is); is >> std::ws; } return is; } inline imanip skip_comments(const unsigned char c) { return imanip(skip_lines_internal,c); } // Test if the input character stream is equal to s. inline bool match_string(std::istream& is,const char*& s) { is >> std::ws; while (*s && is.peek()==static_cast(*s)) { is.get(); s++; } return *s=='\0'; } // Eat up comments beginning with the given string. // Example: To remove the lines begining with a "//" then read data. // cin >> skip_comments("//") >> data; inline std::istream& skip_lines_internal(std::istream& is,const char* s) { const char* s1 = s; while (match_string(is,s1)) { skip_line(is); s1 = s; } return is; } inline imanip skip_comments(const char* s) { return imanip(skip_lines_internal,s); } // Match the given string with an input stream. // Example: To parse lines such as "x=156 y=34". // cin >> match("x=") >> x >> match("y=") >> y; inline std::istream& match_internal(std::istream& is,const char* s) { is >> std::ws; if (!match_string(is,s)) is.setstate(std::ios::failbit); return is; } inline std::istream& match_internal(std::istream& is,const char c) { char cc; is >> cc; if (cc!=c) is.setstate(std::ios::failbit); return is; } inline std::istream& match_internal(std::istream& is,const int i) { int num; is >> num; if (i!=num) is.setstate(std::ios::failbit); return is; } inline std::istream& match_internal(std::istream& is,const unsigned i) { unsigned num; is >> num; if (i!=num) is.setstate(std::ios::failbit); return is; } inline imanip match(const char* str) { return imanip(match_internal,str); } inline imanip match(const std::string& str) { return imanip(match_internal,str.c_str()); } inline imanip match(const char c) { return imanip(match_internal,c); } inline imanip match(const int i) { return imanip(match_internal,i); } inline imanip match(const unsigned i) { return imanip(match_internal,i); } // Restore the string [s,s1[ to the input stream. inline void restore_string(std::istream& is,const char* s,const char *s1) { while (s!=s1--) is.putback(*s1); } // The string equivalent. inline void restore_string(std::istream& is,const std::string& s) { restore_string(is,&*s.begin(),&*s.end()); } // Like match but the matching is optional and the // presence of the string is given in result. inline std::istream& match_string_internal(std::istream& is,const char* s,bool& res) { std::ios::iostate state = is.rdstate(); is >> std::ws; const char* s1 = s; res = match_string(is,s1); if (!res) { is.setstate(state); restore_string(is,s,s1); } return is; } inline imanip2 match_optional(const char* str,bool& res) { return imanip2(match_string_internal,str,res); } inline imanip2 match_optional(const std::string& str,bool& res) { return imanip2(match_string_internal,str.c_str(),res); } // Eat the given string if present on an input stream. // Example: To parse lines such as "x=156 y=34". // cin >> eat("x=") >> x >> eat("y=") >> y; // The difference with match is that it will silently // ignore the strings if they are not on the stream // and the stream will remain in the good state. // So 156 34 will also be accepted as an input line. // Note that x156 34 or any other variation will not // be accepted. inline std::istream& eat_string_internal(std::istream& is,const char* s) { is >> std::ws; const char* s1 = s; if (!match_string(is,s1)) restore_string(is,s,s1); return is; } inline imanip eat(const char* str) { return imanip(eat_string_internal,str); } inline imanip eat(const std::string& str) { return imanip(eat_string_internal,str.c_str()); } // Skip everything until string s is found on the stream. inline std::istream& skip_to_internal(std::istream& is,const char* s) { const char* s1 = s; while(is) { is.ignore(std::numeric_limits::max(),*s); if (match_string(is,++s1)) break; restore_string(is,s+1,s1); s1 = s; } return is; } inline imanip skip_to(const char* str) { return imanip(skip_to_internal,str); } inline imanip skip_to(const std::string& str) { return imanip(skip_to_internal,str.c_str()); } // An optional input is a field eventually put before the // end of the line. template inline std::istream& optional_internal(std::istream& is,T& t) { skip_spaces(is); if (is.peek()!='\n') is >> t; return is; } template inline imanip optional(T& t) { return imanip(optional_internal,t); } // Force numeric reading. template inline std::istream& numeric_internal(std::istream& is,T& t) { return is >> t; } inline std::istream& numeric_internal(std::istream& is,char& t) { int i; is >> i; t = static_cast(i); return is; } inline std::istream& numeric_internal(std::istream& is,unsigned char& t) { unsigned i; is >> i; t = static_cast(i); return is; } template inline imanip numeric(T& t) { return imanip(numeric_internal,t); } // File reading. inline std::istream& file_internal(std::istream& is,std::string& filename,const char delimiter,const bool force_delimiter) { skip_spaces(is); // Read the initial delimiter if necessary. char c; bool delimiter_seen = false; if (is.peek()!=delimiter) { if (force_delimiter) { is.setstate(std::ios::failbit); return is; } } else { is.get(c); delimiter_seen = true; } // Read the string content. while (is.get(c) && c!=delimiter) { if (delimiter_seen && c==delimiter) break; if (!delimiter_seen && std::isspace(c,is.getloc())) break; filename += c; } return is; } inline imanip3 filename(std::string& filename,const char delimiter='"',const bool force_delimiter=true) { return imanip3(file_internal,filename,delimiter,force_delimiter); } }