#ifndef INCAHTABLEXX #define INCAHTABLEXX #include #include #include #include #include #include #include "stringutil.hh" using namespace stringutil; #include "foreach.hh" namespace valimpl // holds implementation of Value, not for public consumption. { struct Value_imp_base { virtual Value_imp_base* clone() const = 0; virtual const std::type_info& type_info() const = 0; virtual istream& read(istream&) = 0; virtual ostream& write(ostream&) const = 0; virtual ~Value_imp_base() {} }; template struct Value_imp: public Value_imp_base { T val; Value_imp* clone() const { return new Value_imp( val ); } Value_imp(T v): val(v) {} Value_imp() {} virtual const std::type_info& type_info() const { return typeid( T ); } // the val below can be implicitly converted to Dummy_value virtual istream& read(istream& i) { i >> val; return i; } virtual ostream& write(ostream& o) const { o << val ; return o; } virtual ~Value_imp() {} }; /* dummy class so that << and >> don't fail at compile time */ // (needed to be able to make Value for types that don't have << >>) struct Dummy_type { string type; template Dummy_type(const T& v) { imp = new Value_imp(v); type = typeid(T).name(); } Dummy_type() {}; Value_imp_base* imp; }; inline ostream& operator<<(ostream& o, const Dummy_type& v) { string s = "Runtime error in Value: No streaming operator << for type " + v.type ; o << s << endl; //handle_error( s ); return o; } inline istream& operator>>(istream& i, const Dummy_type& v) { string s = "Runtime error in Value: No streaming operator >> for type " + v.type ; cout << s << endl; return i; } }// namespace valimpl // a type that can hold any value. based on Ideas from boost:any struct Value { Value() { imp = new valimpl::Value_imp(0); } // default: make an int // copy contructors for types convertable double and string Value(const double& v) { imp = new valimpl::Value_imp(v); } Value(const string& v) { imp = new valimpl::Value_imp(v); } Value(const char* v) { imp = new valimpl::Value_imp(string(v)); } Value(const Value& v) : imp( v.imp->clone() ) {} // Copy constructors for all other types. template explicit Value(const T& v) { imp = new valimpl::Value_imp(v); } ~Value() { delete imp; } const std::type_info& type_info() const { return imp->type_info(); } template T *to_ptr() const { if ( type_info() == typeid(T) ) { return &static_cast< valimpl::Value_imp *>(imp)->val ; } else { return 0; } } template T as() { return *to_ptr(); } template bool copy_to(T &value) const { const T* copyable = to_ptr(); if (copyable) value = *copyable; else cout << "can't copy" << endl; return copyable; } Value& operator=(const Value& v) { if (imp) delete imp; imp = v.imp->clone(); return *this; } template< typename T> Value& operator=(const T& v) { return operator=( Value(v) ); } // the following works fine, but I turn it off // template Value& operator=(const T& v) //{ // return operator=(Value(v)); // } bool is_number() { ostringstream ss; imp->write(ss); return stringutil::is_num( ss.str() ); // from stringutil.hh } // Checks if we are of type A. If yes, convert to // type B using regular a (B) cast. template bool try_convert_from (B& r) const { if (type_info() != typeid(A)) return false; A v; copy_to(v); r = (B) v; return true; } // Conversion operators for string and double operator double () const { double r; if (try_convert_from< double >( r ) || try_convert_from< int >( r ) || try_convert_from< float >( r ) || try_convert_from< unsigned int >( r ) || try_convert_from< long >( r ) || try_convert_from< long double >( r ) ) return r; // try to use streaming. r = 0; stringstream ss; imp->write(ss); ss >> r; if (!ss) { cout << " failed to convert value to double" << endl; cout << " value as string is : " << string(*this) << endl; } return r; } // Convert to a string. operator std::string() const { ostringstream s; imp->write(s); return s.str(); } string type() { return imp->type_info().name(); } // member data valimpl::Value_imp_base* imp; }; /*! Value_adaptor turns a value into an object that can be assigned to anything (if you make a type-error, you'll get a runtime error */ struct Value_adaptor: public Value { Value_adaptor( const Value& v ) : Value(v) {} template operator T() const { T r; try_convert_from< T >( r ); return r; } }; // streaming operators for Value inline ostream& operator<<(ostream& o, const Value& v) { v.imp->write(o); return o;} inline istream& operator>>(istream& i, Value& v) { v.imp->read(i); return i;} struct Cell { Value content; unsigned width; //unsigned precission; template explicit Cell (const T& c): content(c), width(0) {} Cell () : content(" . "), width(0) { }; template Cell& operator= (const T& s) { content = Value(s); return *this; } string str(unsigned w = 0) const { unsigned ww = width; // if w=0, use the cell's width if (w > 0) ww = w; stringstream s; s.width(ww); ostringstream ss; ss << content; s.setf( ios::left ); s << ss.str(); return s.str(); } string strraw() { ostringstream ss; ss << content; return ss.str(); } operator double () const { return (double) content; } unsigned actual_width() const { return str().length(); } }; struct Table_row: public vector { void add_cell(string n = " . ") { if (n == "") { Cell c; push_back( c ); } else { Cell c(n); push_back( c ); } } void remove_cell(unsigned column_number) { erase( begin() + column_number ); } }; struct Table: public vector< Table_row > { public: unsigned c_col; //current/cursor unsigned c_row; string title, caption; explicit Table( const vector& column_names ) { add_row(); foreach ( s, column_names ) { if (s != "") add_column( s ); } c_col = ncols(); // after 1st row: check_cursor will create c_row = 0; // new row on next input. } explicit Table( string c0 = "", string c1 = "", string c2 = "", string c3 = "", string c4 = "", string c5 = "", string c6 = "", string c7 = "", string c8 = "", string c9 = "", string c10 = "", string c11 = "", string c12 = "", string c13 = "", string c14 = "", string c15 = "", string c16 = "", string c17 = "", string c18 = "", string c19 = "", string c20 = "", string c21 = "", string c22 = "", string c23 = "", string c24 = "", string c25 = "", string c26 = "", string c27 = "", string c28 = "", string c29 = "", string c30 = "", string c31 = "", string c32 = "", string c33 = "", string c34 = "", string c35 = "") { add_row(); // start with one row, zero columns string c[36] = {c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, c22, c23, c24, c25, c26, c27, c28, c29, c30, c31, c32, c33, c34, c35 }; for (unsigned i = 0; i < 36; i++) { if (c[i] != "") add_column( c[i] ); } c_col = ncols(); // after 1st row: check_cursor will create c_row = 0; // new row on next input. } template explicit Table( const std::map& m, string c0 = "key", string c1 = "value") { add_row(); add_column( c0 ); add_column( c1 ); for ( typename std::map::const_iterator i = m.begin(); i != m.end(); i++) { add_at_cursor( (*i).first ); add_at_cursor( (*i).second ); } } template explicit Table( const std::vector& keys, const std::vector& values, string c0 = "key", string c1 = "value" ) { add_row(); add_column( c0 ); add_column( c1 ); unsigned n = keys.size() < values.size() ? keys.size() : values.size(); for (unsigned i = 0 ; i < n ; i++) { add_at_cursor( keys[i] ); add_at_cursor( values[i] ); } } //! fill the table with a vector of vectors template explicit Table ( const vector& colnames, const vector< vector< T > >& V ) { add_row(); foreach( s, colnames ) add_column( s ); // check if ( V.size() != colnames.size() ) { cout << " error in Table ctor V.size() != colnames.size() " << endl; return; } for (unsigned j = 0; j < V[0].size(); j++ ) // assume the're all the same size { for ( unsigned i = 0; i < V.size(); i++ ) { add_at_cursor( V[i][j] ); } } } void delete_row(int row_number = -1 ) // -1 means: last one { if (row_number == -1 ) row_number = size() - 1; erase( begin() + row_number ); } void delete_column(int col_number = -1) // -1 means: last one { for (unsigned i = 0; i < unsigned(size()); i++) { Table_row& r = operator[](i); if (col_number == -1 ) col_number = r.size() - 1; r.remove_cell( col_number ); } } unsigned nrows() const { return size(); } unsigned ncols() const { unsigned os = 0; unsigned s = 0; for (unsigned i = 0; i < size(); i++) // check all rows are the same size { s = operator[](i).size(); if (i > 0 && os != s) { cout << form( "Table error: inconsistent row size %d", i) << endl; } os = s; } return s; } void resize_colrows( unsigned n_cols, unsigned n_rows ) { unsigned nr = n_rows - nrows(); unsigned nc = n_cols - ncols(); for (unsigned i = 0; i < nc; i++) add_column(); for (unsigned i = nc; i > nc; i--) delete_column(); for (unsigned i = 0; i < nr; i++) add_row(); for (unsigned i = nr; i > nc; i--) delete_row(); } template void add_at_cursor(const T& v) { check_cursor(); at(c_col, c_row) = Cell(v); c_col++; } // the following are intented for use from python Table& add( double x ) { add_at_cursor(x); return *this;} Table& add( int x ) { add_at_cursor(x); return *this;} Table& add( string x ) { add_at_cursor(x); return *this;} string __str__() const { return str(); } string str() const { ostringstream s; print_ascii( s ); return s.str(); } void print(std::ostream& out = std::cout) const { out << str() << endl; } string html() { ostringstream s; print_html( s ); return s.str(); } string latex() { ostringstream s; print_latex( s ); return s.str(); } void next_row() { do {add_at_cursor(".");} while (c_col != ncols() ); } void check_cursor() // see if we're good to do input, if not: add row. { if ( c_col >= ncols() ) { c_col = 0; c_row++; if ( c_row >= size() ) { add_row(); c_row = nrows() - 1; // prevent adding lots of rows after resize } } } void add_row() { Table_row r; r.resize( ncols() ); push_back( r ); } void add_column( string n = "") { for (unsigned i = 0; i < size() ; i++) { Table_row& r = operator[](i); r.add_cell( n ); } c_col = ncols(); } void set_column_width(unsigned icol, unsigned width ) { for (unsigned i = 0; i < size() ; i++) { at(icol, i).width = width; } } const Cell& at(unsigned x, unsigned y) const { if ( y >= size() ) { string err = form (" Error in Table::at, request for cell <%d,%d> while table" " has only %d rows", x, y, size() ); cout << err << endl; } const Table_row& r = operator[](y); if ( x >= r.size() ) { string err = form (" Error in Table::at, request for cell <%d,%d> while row" " has only %d columns", x, y, r.size() ); cout << err << endl; } return r.operator[](x); } Cell& at(unsigned x, unsigned y) { if ( y >= size() ) { string err = form (" Error in Table::at, request for cell <%d,%d> while table" " has only %d rows", x, y, size() ); cout << err << endl; } Table_row& r = operator[](y); if ( x >= r.size() ) { string err = form (" Error in Table::at, request for cell <%d,%d> while row" " has only %d columns", x, y, r.size() ); cout << err << endl; } return r.operator[](x); } /*! if columns have string-titles in row 0, you can use this to look them up */ int find_column( const string& s ) { for ( unsigned i = 0; i< ncols() ; i++ ) { if ( at(i,0).strraw() == s ) return i; } return -1; } /*! if rows start with a string, you can use this to look up a row */ unsigned find_row( const string& s ) { for ( unsigned i = 0; i< nrows() ; i++ ) { if ( at(0,i).strraw() == s ) return i; } return 0;// todo: something better here } Cell& at( const string& x, const string& y ) { return at( find_column(x), find_row( y ) ); } void print_latex(ostream& out = cout) { out << "\\verb|" << title << "|" << endl; out << "\\newline" << endl << "\\begin{tabular}{"; for (unsigned i = 0; i < ncols(); i++) out << "l"; out << "}" << endl << "\\hline" << endl; for (unsigned i = 0; i < nrows(); i++) { if (i == 1) out << "\\hline" << endl; for (unsigned j = 0; j < ncols(); j++) { out << at(j, i).str(); if (j != ncols() - 1) out << " & "; } out << "\\\\" << endl; } out << "\\hline" << endl; out << "\\end{tabular}" << endl; } void make_eps(string filename = "table.eps", bool debug = false) { ofstream f("tmptexfile.tex"); f << "\\documentclass{letter}" << endl; f << "\\begin{document}" << endl; f << "\\pagestyle{empty}" << endl; print_latex(f); f << "\\end{document}" << endl; f.close(); string cmd = "latex tmptexfile.tex; dvips -E -o " + filename + " tmptexfile.dvi"; system(cmd.c_str()); if (!debug) system("rm -f tmptexfile.*"); } void make_any(string filename) { string ext = filename.substr( filename.length() - 4, filename.length()); if (ext == "eps") // just save { make_eps(filename); } else // first eps, then convert { make_eps("tmpepsfile.eps"); string s = "convert -density 200 tmpepsfile.eps " + filename; system(s.c_str()); system("\\rm -rf tmpepsfile.eps"); } } void print_html(ostream& out = cout ) { out << "" << endl; out << "" << endl; out << " " << endl; out << "" << endl; for (unsigned j = 0; j < nrows(); j++) { out << "" << endl; for (unsigned i = 0; i < ncols(); i++) { out << "" << endl; } out << "" << endl; } out << "
" << title << "
" << at(i, j).str() << "
\n" << endl; } void print_raw(ostream& out = cout , string delim = " | ") { for (unsigned j = 0; j < nrows(); j++) { for (unsigned i = 0; i < ncols(); i++) { out << at(i, j).strraw() << delim ; } out << endl; } } string str() { ostringstream s; print_ascii( s ); return s.str(); } void print_ascii(ostream& out = cout, string lines = " ---", bool do_upper_line = true, bool do_line_after_top_row = true, bool do_lines_between_rows = false, bool do_lower_line = true ) const { const char c_h = lines[0]; const char c_v = lines[1]; const char c_c = lines[2]; string lup, lmid, ldown; lup += c_c; lmid += c_c; ldown += c_c; vector col_width( ncols(), 0 ); for (unsigned i = 0; i < ncols(); i++) { unsigned w = 0; for (unsigned j = 0; j < nrows(); j++) { unsigned l = at(i, j).actual_width(); if (l > w) w = l; } col_width[i] = w; for (unsigned j = 0 ; j < w ; j++) { lup += c_v; lmid += c_v; ldown += c_v; } if (i != ncols() - 1) { lup += c_c; lmid += c_c; ldown += c_c; } } lup += c_c; lmid += c_c; ldown += c_c; if (do_upper_line) out << lup << endl; if (title != "" ) { out << title << endl; if (do_upper_line) out << lup << endl; } for (unsigned i = 0; i < size() ; i++) { const Table_row& r = operator[](i); string line; line += c_h; for (unsigned j = 0; j < r.size(); j++) { string f = at(j, i).str( col_width[j] ); line += f; line += c_h; } out << line << endl; if (i != size() - 1 ) { if ( do_lines_between_rows || (do_line_after_top_row && i == 0)) out << lmid << endl; } else if (do_lower_line) out << ldown << endl; } } }; template inline Table& operator<<(Table& t, const T& v) { t.add_at_cursor(v); return t;}; inline std::ostream& operator<<(std::ostream& out, const Table& t ) { (const_cast< Table& >(t)).print_ascii(out); return out; } // as of ROOT 6.16?, pyroot seems to use printValue to print namespace cling { inline std::string printValue(const Table* t) { return t->__str__(); } } #endif