How to write an ostream statement with custom flags

I often want to write a stl container for ostream. The following code works fine (at least for vector and list):

template< typename T ,template<typename ELEM, typename ALLOC=std::allocator<ELEM> > class Container > std::ostream& operator<< (std::ostream& o, Container<T>const & container){ typename Container<T>::const_iterator beg = container.begin(); while(beg != container.end()){ o << *beg++; if (beg!=container.end()) o << "\t"; } return o; } 

Now I want to extend this code to support custom separators. The following approach obviously does not work , since the operator must accept only two parameters.

 template< typename T ,template<typename ELEM, typename ALLOC=std::allocator<ELEM> > class Container > std::ostream& operator<< (std::ostream& o, Container<T>const & container,char* separator){ typename Container<T>::const_iterator beg = container.begin(); while(beg != container.end()){ o << *beg++; if (beg!=container.end()) o << separator; } return o; } 

Is it possible to achieve something similar without resorting to singles or global variables?

It would be ideal to introduce a custom flag or stream manipulator, such as std::fixed . Then one could write

 std::cout << streamflags::tabbed << myContainer; 
+4
source share
2 answers

You can write your own manipulator. basic_ostream provides operator<< overloads that take a pointer to a function (see (9) by reference) and call this function, passing the stream itself as a parameter. So a manipulator is just a function name.

Alternatively, the manipulator may be an object with a suitable operator<< . This will allow you to write, say, cout << setSeparator(',') << myContainer; . Here setSeparator is a class with the constructor setSeparator(char) .

Finally, ios_base provides the xalloc , iword and pword , which essentially allow you to associate arbitrary information with a specific thread instance. So your manipulator can contact your operator<<(Container) . You need one global variable to keep the index allocated for you, xalloc .

+4
source

To end @ Igor-Tandetnik's answer, a simple (insecure) more explicit example:

 #include <iostream> #include <vector> namespace MyNamespace { namespace IO { enum class OrientationEnum { Row, Column }; struct State { OrientationEnum orientation = OrientationEnum::Row; }; static State currentState; template <typename CharT, typename Traits> inline std::basic_ostream<CharT, Traits>& rowOriented( std::basic_ostream<CharT, Traits>& os) { currentState.orientation = OrientationEnum::Row; return os; } template <typename CharT, typename Traits> inline std::basic_ostream<CharT, Traits>& columnOriented( std::basic_ostream<CharT, Traits>& os) { currentState.orientation = OrientationEnum::Column; return os; } } template <typename T> std::ostream& operator<<(std::ostream& out, const std::vector<T>& toPrint) { switch (IO::currentState.orientation) { case IO::OrientationEnum::Column: for (const auto& e : toPrint) { out << e << "\n"; } break; case IO::OrientationEnum::Row: for (const auto& e : toPrint) { out << e << " "; } break; default: break; } return out; } } ////////////////////////////////////////////////////////////////// using namespace MyNamespace; int main() { std::vector<int> v(5,0); // A 5-vector of 0 // If you want to save your state // auto savedState = IO::currentState; std::cout << "\nBy row\n" << IO::rowOriented << v // << "\nBy column\n" << IO::columnOriented << v; // IO::currentState = savedState; } 

You can compile and run it.

 g++ streamExample.cpp -o streamExample ./streamExample 

Output:

 By row 0 0 0 0 0 By column 0 0 0 0 0 
0
source

Source: https://habr.com/ru/post/1496018/


All Articles