// Boost.TypeErasure library // // Copyright 2011 Steven Watanabe // Copyright 2012 Paul A. Bristow Adding comments and examples of new types. // // Distributed under the Boost Software License Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // from \boost-sandbox\type_erasure\libs\type_erasure\example\print_sequence.cpp // $Id: print_sequence.cpp 78453 2012-05-13 15:24:00Z steven_watanabe $ //[print_sequence /*` (For the source of this example see [@boost:/libs/type_erasure/example/print_sequence.cpp print_sequence.cpp]) This example demonstrates using Boost.TypeErasure to implement a virtual "template" function. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // An uncertain type called measure. #include #include #include #include #include #include using namespace boost::type_erasure; // Placeholders used by the abstract printer during specification of requirements. struct _t : placeholder {}; struct _iter : placeholder {}; struct _os : placeholder {}; // This is more readable than using the 'out of the box' _a, ... _g placeholders. // See placeholder.hpp. template struct base_and_derived { // What does this do??? // Template parameter for concept_interface. // and in list of requirements in abstract printer. // base_and_derived, // _os must derive from std::ios_base. static T& apply(U& arg) { return arg; } }; namespace boost { namespace type_erasure { template struct concept_interface, Base, U> : Base { operator typename rebind_any::type() const { return call(base_and_derived(), const_cast(*this)); } operator typename rebind_any::type() { return call(base_and_derived(), *this); } }; } // namespace type_erasure } // namespace boost // Define an abstract sequence printer = a 'template' that is inherited // to implement the examples of actual printers defined below. class abstract_printer { public: // Range must be a Forward Range whose elements can be printed to os. template void print(std::basic_ostream& os, const Range& r) const { // Capture the print arguments. // Range iterators. typename boost::range_iterator::type first(boost::begin(r)), last(boost::end(r)); // Assemble requirements into tuple: ostream, and range pair of iterators. tuple args(os, first, last); // and forward to the real implementation. do_print(get<0>(args), get<1>(args), get<2>(args)); // get<0>() is ostream, get<1>() is first, get<2>() is last. } virtual ~abstract_printer() {} protected: // Define the requirements in an mpl::vector for containers to be printed. typedef boost::mpl::vector< base_and_derived, // _os must derive from std::ios_base. ostreamable<_os, _t>, // type _t must be ostreamable. ostreamable<_os, const char*>, // C string must be ostreamable. forward_iterator<_iter, const _t&>, // t range must have forward iterator. same_type<_t, forward_iterator<_iter, const _t&>::value_type> // _t's type value and iterator must match. > requirements; // Use type_erasure::any to enforce the requirements onto ostream and iterator. typedef boost::type_erasure::any ostream_type; typedef boost::type_erasure::any iterator_type; // Declare the pure virtual function `do_print` that is defined in all real printers. virtual void do_print( ostream_type os, iterator_type first, iterator_type last) const = 0; }; class separator_printer : public abstract_printer { // Output items on same line with a separator like space and/or comma. public: explicit separator_printer(const std::string& sep) : separator(sep) { // Constructor, for example: `separator_printer(", ");` // A separator string must be provided. } protected: // Print all items on one line, separated by the separator argument. virtual void do_print( ostream_type os, iterator_type first, iterator_type last) const { if(first != last) { os << *first; ++first; for(; first != last; ++first) { os << separator.c_str() << *first; } } } private: std::string separator; // Set by constructor, for example, ", ". }; class separator_terminator_printer : public abstract_printer { // Output items on same line with a separator like space and/or comma. public: explicit separator_terminator_printer(const std::string& sep, const std::string& term) : separator(sep), terminator(term) { // Constructor, for example: `separator_terminator_printer(", ", "\n");` // A separator string and a terminator string (possibly null "") must be provided. } protected: // Print all items on one line, separated by the separator argument. virtual void do_print( ostream_type os, iterator_type first, iterator_type last) const { if(first != last) { os << *first; ++first; for(; first != last; ++first) { os << separator.c_str() << *first; } } os << terminator.c_str(); // Perhaps useful to have a terminating newline? } private: std::string separator; // Set by constructor, for example, ", ". std::string terminator; // Set by constructor, for example, "\n". }; // class separator_terminator_printer class decor_printer : public abstract_printer { // Output items on same line with a separator like space and/or comma. public: explicit decor_printer(const std::string& pre, const std::string& sep, const std::string& term) : prefix(pre), separator(sep), terminator(term) { // Constructor, for example: `decor_printer(", ", "\n");` // A separator string and a terminator string (possibly null "") must be provided. } protected: // Print all items on one line, separated by the separator argument. virtual void do_print( ostream_type os, iterator_type first, iterator_type last) const { os << prefix.c_str(); if(first != last) { os << *first; ++first; for(; first != last; ++first) { os << separator.c_str() << *first; } } os << terminator.c_str(); // Perhaps useful to have a terminating newline? } private: std::string prefix; // Set by constructor, for example, "{ ". std::string separator; // Set by constructor, for example, ", ". std::string terminator; // Set by constructor, for example, "}\n". }; // class decor_printer class column_separator_printer : public abstract_printer { // Output items on same line with a separator like space and or comma, // but add a newline after every `num_columns`. public: column_separator_printer(const std::string& sep, std::size_t num_columns) : separator(sep), cols(num_columns) {} protected: virtual void do_print( ostream_type os, iterator_type first, iterator_type last) const { std::size_t count = 0; for(; first != last; ++first) { os << *first; boost::type_erasure::any temp = first; ++temp; if(temp != last) { os << separator.c_str(); } if(++count % cols == 0) { // Last column. os << "\n"; // Newline at end of a row. } } } private: std::string separator; std::size_t cols; }; class aligned_column_printer : public abstract_printer { // Output items in fixed column_width down fixed number of columns. public: aligned_column_printer(std::size_t column_width, std::size_t num_columns) : width(column_width), cols(num_columns) {} protected: virtual void do_print( ostream_type os, iterator_type first, iterator_type last) const { if(first == last) return; std::vector column_iterators; // Find the tops of the columns. // Count the number of items. std::size_t count = 0; for(iterator_type iter = first; iter != last; ++iter) { ++count; } // Calculate how many rows needed. std::size_t rows = (count + cols - 1) / cols; // Store the iterators to tops of the columns. count = 0; for(iterator_type iter = first; iter != last; ++iter) { if(count % rows == 0) { column_iterators.push_back(iter); } ++count; } iterator_type last_col = column_iterators.back(); // Print all the full rows. while(column_iterators.back() != last) { for(std::vector::iterator iter = column_iterators.begin(), end = column_iterators.end(); iter != end; ++iter) { static_cast(os).width(width); os << **iter; ++*iter; } os << "\n"; } // Print the rows that are missing the last column. column_iterators.pop_back(); if(!column_iterators.empty()) { while(column_iterators.back() != last_col) { for(std::vector::iterator iter = column_iterators.begin(), end = column_iterators.end(); iter != end; ++iter) { static_cast(os).width(width); os << **iter; ++*iter; } os << "\n"; } } } private: std::size_t width; std::size_t cols; }; namespace boost { namespace type_erasure { // Fully specialized for const std::pair& //std::ostream& operator<< (std::ostream& os, const std::pair& p) //{ /*! Output a pair of values. // \details For example: "1.23 , 3.45". // */ // os << p.first << ", " << p.second; // return os; //} // std::ostream& operator<< // But more useful is just providing the two template version: template std::ostream& operator<< (std::ostream& os, const std::pair& p) { /*! Output a pair of values with space as delimiter. \details For example: "1.23 3.45". */ os << p.first << " " << p.second; return os; } // std::ostream& operator<< (std::ostream& os, const std::pair& p) // Or can use a version defined elsewhere, for example, in namespace my_detail. // using my_detail::operator<<; // Partial specialization of struct ostreamable for pair: //template //struct ostreamable > //{ // static void apply(Os& out, const std::pair& arg) // { // out << arg; // } //}; // Partial specialization of struct ostreamable for pair: template struct ostreamable > { static void apply(Os& out, const std::pair& arg) { out << arg; } }; } // namespace type_erasure } // namespace boost int main() { using namespace boost::units; using namespace boost::units::si; int test[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; double testd[] = { 1., 2., 3., 4., 5., 6., 7., 8., 9., 10. }; separator_printer p1(", "); // Construct a sequence printer with comma separator. p1.print(std::cout, test); // 1,2,3,4,5,6,7,8,9,10 std::cout << std::endl; separator_terminator_printer p1nl(", ", "|\n"); // Construct a sequence printer with comma separator and newline terminator. p1nl.print(std::cout, test); // 1,2,3,4,5,6,7,8,9,10 // std::cout << std::endl; // now not needed to ensure next item starts on a newline. decor_printer p1decor("int[10] = {", ", ", "};\n"); // Construct a sequence printer with prefix int[10] = {, comma separator and } newline terminator. // std::string s("int[10] = {"); // Or can use a std::string. // decor_printer p1decor(s, ", ", "};\n"); // Construct a sequence printer with prefix {, comma separator and } newline terminator. p1decor.print(std::cout, test); // int[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; separator_printer p1a("___"); // Construct a printer with an underscore as separator. p1a.print(std::cout, test); // 1___2___3___4___5___6___7___8___9___10 std::cout << std::endl; p1.print(std::cout, testd); // Type now double. std::cout << std::endl; // 1,2,3,4,5,6,7,8,9,10 std::cout.precision(5); // Add some fancy layout. std::cout << std::showpos << std::showpoint << std::endl; p1.print(std::cout, testd); // Type still double. std::cout << std::endl; // +1.0000,+2.0000,+3.0000,+4.0000,+5.0000,+6.0000,+7.0000,+8.0000,+9.0000,+10.000 std::cout << std::noshowpos << std::endl; // Switch off fancy +. // Try an array of Boost.Units quantity. quantity testq[] = {2345.6 * meters, 123.4 * meters, 0.0123 * meters}; p1.print(std::cout, testq); // Type now quantity of length. std::cout << std::endl; // 2.3456 km, 123.40 m, 12.300 mm // Note the cool change of unit prefix from km to m to mm! // Try an array of Boost.Units quantity with an uncertain type. quantity > testqu[] = {measurement(45210.0, 1234.0) * meters, measurement(789, 2.5) * meters, measurement(0.000567,2.34e-5) * meters}; p1.print(std::cout, testqu); // Type now quantity of length with uncertainty estimate. std::cout << std::endl; // 45.210(+/-1.2340) km, 789.00(+/-2.5000) m, 567.00(+/-23.400) um // Note the cool change of unit prefix *and* showing uncertainty! const char* testCstring[] = {"one", "two", "three", "four","five", "six","seven","eight", "nine", "ten"}; p1.print(std::cout, testCstring); // Type is now a C string. std::cout << std::endl; std::string teststring[] = {"ONE", "TWO", "THREE", "FOUR","FIVE", "SIX","SEVEN","EIGHT", "NINE", "TEN"}; p1.print(std::cout, teststring); // Type is now a std::string. std::cout << std::endl; // Now some other sequence printers. column_separator_printer p2(",", 4); // Construct another 4 column separator printer. p2.print(std::cout, test); // 1,2,3,4, and then 5,6,7,8, and then 9,10 std::cout << std::endl; aligned_column_printer p3(16, 4); // Print down columns 16 spaces across. p3.print(std::cout, test); // 1 4 7 10 then 2 5 8, then 3 6 9 std::cout << std::endl; // Check it works with a STL container like vector (copying values from array testd). std::vector testv (testd, testd + sizeof(testd)/ sizeof(testd[0])); p1.print(std::cout, testv); // 1.0000, 2.0000, 3.0000, 4.0000, 5.0000, 6.0000, 7.0000, 8.0000, 9.0000, 10.000 std::cout << std::endl; // std::list testl (testd, testd + sizeof(testd)/ sizeof(testd[0])); p2.print(std::cout, testl); // 1.0000,2.0000,3.0000,4.0000, // 5.0000,6.0000,7.0000,8.0000, // 7.0000 std::cout << std::endl; // std::set tests; tests.insert(3.); tests.insert(4.); tests.insert(5.); tests.insert(6.); tests.insert(7.); p2.print(std::cout, tests); // 3.0000,4.0000,5.0000,6.0000, std::cout << std::noshowpoint << std::endl; // 7.0000 // For map example see type_erasure_print_map.cpp. // A definition of operator<< for std::pair and a partial specialization for ostreamable are required. std::map my_map; my_map.insert(std::make_pair(1, 9.9)); my_map.insert(std::make_pair(2, 8.8)); my_map.insert(std::make_pair(3, 7.7)); my_map.insert(std::make_pair(4, 6.6)); p2.print(std::cout, my_map); // 1, 9.9,2, 8.8,3, 7.7,4, 6.6 std::cout << std::endl; // using boost::tuples::tuple; using boost::tuples::make_tuple; using boost::tuples::null_type; tuple ids1(23, 99.9, "my_string"); // Construct a tuple. // But convenient to use a typedef for the tuple type. typedef tuple my_tuple_t; my_tuple_t ids2(42, 3.1459, "other_string"); // Construct another using typedef. std::vector testtup; testtup.push_back(ids1); testtup.push_back(ids2); my_tuple_t ids3(43, 2.8, "third_string"); // Construct yet another also using typedef. testtup.push_back(ids3); p2.print(std::cout, testtup); // (23 99.900 my_string),(42 3.1459 other_string) std::cout << std::endl; // By default, tuple is output enclosed in () curved parentheses and separated by space. // Instead, create a new printer with prefix "{", decor_printer p2decor("= {", ", ", "};\n"); // Construct a sequence printer with prefix "= {", comma separator and "}; newline" terminator. // and for good measure, change the tuple delimiter from space to underscore: using boost::tuples::set_open; using boost::tuples::set_delimiter; using boost::tuples::set_close; std::cout << set_open('[') << set_delimiter('_') << set_close(']') << std::endl; // Sadly these do not permit strings (only char)? p2decor.print(std::cout, testtup); // = {[23_99.900_my_string], [42_3.1459_other_string], [43_2.8000_third_string]}; // But this might be a very long line, so to place each tuple on a new line, // construct another decor_printer. decor_printer p3decor("= {", ",\n ", "};\n"); // Construct a sequence printer with prefix "= {", comma separator and "}; newline" terminator. p3decor.print(std::cout, testtup); /* = {[23_99.900_my_string], [42_3.1459_other_string], [43_2.8000_third_string]}; */ // Note how the comma delimiter is correctly only placed between the items, and not at the end. } //] /* Output: Description: Autorun "J:\Cpp\Misc\Debug\type_erasure_print_sequence.exe" 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 1, 2, 3, 4, 5, 6, 7, 8, 9, 10| int[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 1___2___3___4___5___6___7___8___9___10 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +1.0000, +2.0000, +3.0000, +4.0000, +5.0000, +6.0000, +7.0000, +8.0000, +9.0000, +10.000 2345.6 m, 123.40 m, 0.012300 m 45210.(+/-1234.0) m, 789.00(+/-2.5000) m, 0.00056700(+/-2.3400e-005) m one, two, three, four, five, six, seven, eight, nine, ten ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN 1,2,3,4, 5,6,7,8, 9,10 1 4 7 10 2 5 8 3 6 9 1.0000, 2.0000, 3.0000, 4.0000, 5.0000, 6.0000, 7.0000, 8.0000, 9.0000, 10.000 1.0000,2.0000,3.0000,4.0000, 5.0000,6.0000,7.0000,8.0000, 9.0000,10.000 3.0000,4.0000,5.0000,6.0000, 7.0000 1 9.9,2 8.8,3 7.7,4 6.6 (23 99.9 my_string),(42 3.1459 other_string),(43 2.8 third_string) = {[23_99.9_my_string], [42_3.1459_other_string], [43_2.8_third_string]}; = {[23_99.9_my_string], [42_3.1459_other_string], */